Programando em Clojure/Conceitos-chave
Programação Funcional
[editar | editar código-fonte]A programação funcional é um paradigma de programação que se centra ao redor do conceito de funções de primeira classe sem efeitos colaterais (as suas variaveis não podem ser alteradas ao decorrer do programa).
Na ciência da computação, entidades de primeira classe são entidades que podem ser usados sem restrições em linguagens de programação (em comparação com outras entidades da mesma linguagem). Isso quer dizer que funções podem receber funções como parâmetro e retornar funções como resultados.
Um conceito chave de funções no paradigma funcional é que funções não possuem efeito colateral. Uma função é dita ter efeito colateral se, além de processar valores de entrada e retornar valores de saída, ela altera o estado de um programa (por exemplo, alterando alguma variável global ou escrevendo dados em um disco rígido).
A técnica mais amplamente usada na programação funcional como método de solução de problemas é a recursão.
O que é um paradigma?
[editar | editar código-fonte]Quando escrevemos programas de computador, um problema que sempre temos em mente é como traduzir para a linguagem de programação o que queremos dizer. Como, por exemplo, escreveríamos um programa para calcular a soma dos primeiros 10 números naturais?
Se você tem algum conhecimento em linguagens como C ou Pascal, provavelmente pensaria de imediato em declarar uma variável que guardasse o valor da soma. Uma possibilidade de se chegar a esse valor seria incrementando essa variável, provavelmente usando um loop com for ou while. Por fim, imprimiríamos para a tela o valor dessa variável.
Após modelar o problema usando conceitos que ele "entende", escreveríamos um código parecido com o seguinte:
int main()
{
int soma = 0; // variavel para acumular a soma
int i; // variavel para ser usada na iteracao
for (i = 0; i < 10; i++) { // para i variando de 0 ate 9
soma = soma + i; // faça: soma = antiga soma + i
}
printf("%d", soma); // imprima o valor da variavel soma
return 0; // retorne zero
}
Note que para escrever efetivamente o programa, tivemos que antes pensar em termos de conceitos e abstrações que temos em comum com a linguagem de programação: loops, declarações de variáveis, funções, etc. Modelamos o código de acordo com um estilo que a linguagem compreende. A esse estilo damos o nome de paradigma de programação.
Exemplo de uso
[editar | editar código-fonte]O paradigma funcional determina um estilo específico que usamos para escrever nossos programas de computador. Uma de suas características determinantes é a aplicação de funções à dados imutáveis e sem estados. Pense em um estado como as valorações que um dado assume em determinado momento da execução do programa (por exemplo, um objeto do tipo carro pode estar com velocidade de 15km/h em um determinado estado, e em 30km/h em outro estado). Dados imutáveis são dados que não podem mudar de valorações com o percorrer do tempo. A este conceito específico de funções damos o nome de funções livres de efeito colateral.
É comum, em linguagens de programação funcional (inclusive em Clojure), funções serem também de primeira ordem, ou seja, funções são tratadas como qualquer outro tipo de dado, podendo inclusive servir como parâmetro de entrada para outras funções e como valor de retorno de outras funções.
A programação funcional contrasta diretamente com a programação imperativa (C, C++, Java, Python...). A técnica da recursão, por exemplo, é primária para a modelagem de problemas na programação funcional. O paradigma imperativo, por outro lado, enfatiza o uso de variáveis mutáveis e construções de loops.
A tabela abaixo compara aspectos da programação imperativa e da programação funcional.
Característica | Programação imperativa | Programação funcional |
---|---|---|
Foco do programador | Como implementar algoritmos e manter uma ordem de estados. | Que informações são desejadas e que transformações são necessárias |
Mudança de estados | Importante | Não-existente |
Ordem de execução | Importante | Baixa importância |
Controle de fluxo primário | Loops, condicionais e chamadas de funções (ou métodos) | Chamadas de funções e recursão |
Unidades de manipulação | Instância de estruturas (programação procedural) ou classes (programação orientada à objetos) | Funções como dados, e coleções de dados |
O problema citado no início desse Wiki pode ser muito bem modelado de acordo com o paradigma funcional, isto é, usando as abstrações e conceitos próprios do mesmo. Vamos tentar desenvolver o exemplo na linguagem Scheme.
Primeiro, podemos pensar na sequência de 0 à 9 (ou seja, os 10 primeiros números naturais) como uma lista:
(list 0 1 2 3 4 5 6 7 8 9)
Agora, como transformar esta lista na soma de todos os elementos da mesma? Em programação funcional, o mais natural é pensarmos em uma função recursiva que aceita a lista como parâmetro e calcula recursivamente a soma de seus elementos. A declaração dessa função, em Clojure, seria:
(defn soma-elementos
[lst]
...)
No lugar das reticências, colocamos o corpo da definição da função. Mas como seria, matematicamente, essa função?
Vamos pensar em um caso base. A soma dos elementos de uma lista vazia é 0:
soma-elementos(lista) = 0
, se lista é vazia
Se a lista não é vazia, a soma dos seus elementos pode ser representada como a soma do primeiro elemento com a soma do resto da lista:
soma-elementos(lista) = primeiro(lista) + soma-elementos(resto(lista))
, se lista não é vazia
Em Clojure, isso se traduz em:
(defn soma-elementos ; definicao da funcao soma-elementos
[lst] ; argumento da funcao: lst
(if (empty? lst) ; se a lista eh vazia
0 ; retorna 0
(+ (first lst) ; senão, soma o primeiro elemento da lista
(soma-elementos (rest lst))))) ; com a soma do resto da lista
Este código é, de fato, válido em Clojure, e uma chamada na forma:
> (soma-elementos (list 0 1 2 3 4 5 6 7 8 9))
Retornaria 45, que é o valor esperado.