Neste curso, começaremos um projeto novo de Clojure do Leiningen, cujo título será "loja". Esse processo demora alguns minutos.
Termos o diretório "src", então "loja" que por sua vez abrigará o documento core.clj:
(ns loja.core)
(defn foo
"I don't do a whole lot."
[x]
(println x "Hello, World!"))
Primeiramente, executaremos o nosso Leiningen. Com o botão direito clicaremos sobre o arquivo project.clj e selecionaremos a opção "Run 'REPL for loja'. Agora podemos trabalha em loja.core.
Já conhecemos alguns tipos de coleções, por exemplo um vetor que pode ter vários elementos. Por exemplo um vetor que contém diversos nomes como :
;["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"]
Também conhecemos o modelo associativo, ou dicionário. Nesta coleção poderíamos dizer que guilherme tem 37 anos e o paulo tem 39. O uso da vírgula para separar elementos no Clousure é opcional.
;["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"]
;{ "guilherme" 37, "paulo" 39}
Poderíamos ter ainda outro tipo de coleção que é meramente uma sequencia de elementos. Ainda poderia ver uma coleção que, ao invés da restrição ser por acesso randômico via array, seria uma lista ligada, em que se utiliza ;'().
;["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"]
;{ "guilherme" 37, "paulo" 39}
;
Outro tipo de coleção possível é aquela que contém 0 ou 1 elemento, totalmente válida. Um tipo de coleção também válida é aquela composta por um conjunto.
No cotidiano da programação, utilizamos coleções o tempo todo, por isso destacamos a importância de map, reduce e filter. Quando queremos passar por vários elementos de uma coleção executando algo em cada um delas e ainda retornar o resultado da execução para cada um deles fazemos o map. Quando queremos filtrar alguns dos elementos, utilizamos o filter e quando queremos reduzir os elementosacionamos o reduce.
E se quisermos usar um loop como em outras linguagens? Ou mesmo for? Existem recursos do gênero, mas antes aprenderemos de que maneira funcional podemos fazer algo para todos os elementos de um vetor. Por exemplo, num conjunto de nomes, gostaríamos de imprimir todos eles.
Sabemos que essa é uma ação possível:
(map println ["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"])
O map realiza esse trabalho para nós. O map é um código funcional que realiza algo, não se trata de uma key word da linguagem, trata-se de uma função que foi implementada por alguém, ainda que carregado de otimizações.
O map passa pelo primeiro elemento e executa a função, ao final só resta println ["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"], e então depois println ["guilherme" "carlos" "paulo" "lucia" "ana"]), até que todos os elementos tenham sido lidos, e resta apenas printl[]
Conseguimos também coletar o primeiro elemento de um vetor, e não existe apenas uma maneira de fazer isso. Uma maneira bem simples é usar o first.
(println (first ["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"]))
Será coletado apenas daniela. Existem maneiras de coletar todos os elementos, exceto o primeiro, como o rest e o next. Há pequenas diferenças entre os dois, principalmente quando estamos de uma sequência vazia de elementos, neste caso o rest devolve vazio, já o next nos devolve nulo. Então, o next poderia ser utilizado para descobrir quando uma sequencia termina.
Já utilizamos várias vezes o termo "sequência" ao invés de "vetor". O uso de { explicita um vetor, mas a verdade é que várias funções nos devolvem uma sequência de elementos, como esse sequência foi implementada são informações que não sabemos.
Podemos transformar um vetor vazio em uma sequência (seq), e o seq de um vetor vazio é nulo, mas o seq de um vetor com elementos resulta em uma sequencia com esses elementos.
Queremos executar, usando o first, rest ou next ser capaz de chamar o println para todos os elementos, quer dizer queremos implementar uma versão simples do nosso mapa.
Começaremos com uma função e uma sequência. Feito isso coletaremos o primeiro elemento dessa sequência (first)
(println "\n\n\n\nMEU MAPA")
(defn meu-mapa
[funcao sequencia]
(let [primeiro (first sequencia)]
(funcao primeiro)))
Vamos testar.
(meu-mapa println ["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"])
Funcionou perfeitamente, a foi impresso daniela. Não queremos imprimir o primeiro elemento, mas todo o resto da sequência. Chamaremos meu-mapa e então usaremos a função rest.
(println "\n\n\n\nMEU MAPA")
(defn meu-mapa
[funcao sequencia]
(let [primeiro (first sequencia)]
(funcao primeiro)
(meu-mapa funcao(rest sequencia))
))
Ao testaremos nosso código, veremos que ele entrou em loop. Por mais que o loop não seja explicito, no código chamamos nós mesmos até que imprimimos anae e ficamos com uma sequencia vazia. O first de uma sequência vazia é nulo, e então ficamos nesse ciclo de loop. Portanto precisamos em algum momento parar o laço.
Estamos nos invocando recursivamente, sem parar. Precisamos interromper esse processo quando não houver mais próximo elemento a ser buscado. Realizaremos então um if
(defn meu-mapa
[funcao sequencia]
(let [primeiro (first sequencia)]
(if primeiro
(funcao primeiro)
(meu-mapa funcao(rest sequencia)))
))
(meu-mapa println ["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"])
Ao testarmos nosso código, veremos impresso daniela, apenas. Por que isso aconteceu?
O if possui duas funções if e else. Queremos escrever o if com mais um bloco de código, e então inseriremos o do
(defn meu-mapa
[funcao sequencia]
(let [primeiro (first sequencia)]
(if primeiro
(do
(funcao primeiro)
(meu-mapa funcao(rest sequencia))))
))
(meu-mapa println ["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"])
Feito isso,teremos todos os outros nomes impressos. Isso se deu porque o if verdadeiro executa a ação. Se o primeiro for nulo ou falso, a impressão é interrompida.
Imprimiremos o mapa com um vetor que contém false.
(defn meu-mapa
[funcao sequencia]
(let [primeiro (first sequencia)]
(if primeiro
(do
(funcao primeiro)
(meu-mapa funcao(rest sequencia))))
))
(meu-mapa println ["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"])
(meu-mapa println ["daniela" false "carlos" "paulo" "lucia" "ana"])
Novamente é impressão é interrompinda após daniela, isso ocorreu porque o primeiro elemento possui um valor falso. Logo, o if não é o melhor recurso para usarmos nessa situação, pois não conseguimos verificar o valor.
Outra maneira de usar o if é realizar a verificação de nulo.
(defn meu-mapa
[funcao sequencia]
(let [primeiro (first sequencia)]
(if (not(nil? primeiro))
(do
(funcao primeiro)
(meu-mapa funcao(rest sequencia))))))
(meu-mapa println ["daniela" "guilherme" "carlos" "paulo" "lucia" "ana"])
(meu-mapa println ["daniela" false "carlos" "paulo" "lucia" "ana"])
Teremos impresso na tela
daniela
guilherme
carlos
paulo
lucia
ana
daniela
false
carlos
paulo
lucia
ana
Nesse caso o mapa deu certo, inclusive se tentarmos imprimir um vetor vazio, nada será impresso, o mesmo o ocorrerá para vetores nulos. Essa é uma maneira de trabalharmos, mas não a única.
Por meio da recursão, conseguimos implementar tarefas que deverão ser executadas mais de uma vez.