[clojure/pt-br] Rewrite text to be more natural

This commit is contained in:
Ygor Sad 2019-10-13 19:36:14 -03:00
parent ef1ccd2b0f
commit 17360e644f

View File

@ -5,12 +5,13 @@ contributors:
- ["Adam Bard", "http://adambard.com/"]
translators:
- ["Mariane Siqueira Machado", "https://twitter.com/mariane_sm"]
- ["Ygor Sad", "https://github.com/ysads"]
lang: pt-br
---
Clojure é uma linguagem da família do Lisp desenvolvida para a JVM (máquina virtual Java). Possui uma ênfase muito mais forte em [programação funcional] (https://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_funcional) pura do que Common Lisp, mas inclui diversas utilidades [STM](https://en.wikipedia.org/wiki/Software_transactional_memory) para lidar com estado a medida que isso se torna necessário.
Clojure é uma linguagem da família do Lisp desenvolvida para a JVM (máquina virtual Java). Possui uma ênfase muito mais forte em [programação funcional] (https://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_funcional) pura do que Common Lisp, mas inclui diversos recursos [STM](https://en.wikipedia.org/wiki/Software_transactional_memory) para lidar com estado e mutabilidade, caso isso seja necessário.
Essa combinação permite gerenciar processamento concorrente de maneira muito simples, e frequentemente de maneira automática.
Essa combinação permite gerenciar processamento concorrente de maneira muito simples - frequentemente, de modo automático.
(Sua versão de clojure precisa ser pelo menos 1.2)
@ -18,367 +19,549 @@ Essa combinação permite gerenciar processamento concorrente de maneira muito s
```clojure
; Comentários começam por ponto e vírgula
; Clojure é escrito em "forms", os quais são simplesmente
; listas de coisas dentro de parênteses, separados por espaços em branco.
; Código Clojure é escrito em formas - 'forms', em inglês. Tais estruturas são
; simplesmente listas de valores encapsuladas dentro de parênteses, separados por
; espaços em branco.
; O "reader" (leitor) de Clojure presume que o primeiro elemento de
; uma par de parênteses é uma função ou macro, e que os resto são argumentos.
; Ao interpretar um código em Clojure, o interpretador ou leitor - do inglês 'reader' - assume
; que o primeiro valor dentro de uma forma é uma função ou macro, de modo que os demais valores
; são seus argumentos. Isso se deve ao fato de que Clojure, por ser uma derivação de Lisp,
; usa notação prefixa (ou polonesa).
: A primeira chamada de um arquivo deve ser ns, para configurar o namespace (espaço de nomes)
; Num arquivo, a primeira chamada deve ser sempre para a função ns,
; que é responsável por definir em qual namespace o código em questão
; deve ser alocado
(ns learnclojure)
; Alguns exemplos básicos:
; str cria uma string concatenando seus argumentos
(str "Hello" " " "World") ; => "Hello World"
; Aqui, str é uma função e "Olá" " " e "Mundo" são seus argumentos. O que ela faz é criar
; uma string concatenando seus argumentos.
(str "Olá" " " "Mundo") ; => "Olá Mundo"
; Cálculos são feitos de forma direta e intuitiva
; Note que espaços em branco separam os argumentos de uma função. Opcionalmente vírgulas
; podem ser usadas, se você quiser.
(str, "Olá", " ", "Mundo") ; => "Olá Mundo"
; As operações matemáticas básicas usam os operadores de sempre
(+ 1 1) ; => 2
(- 2 1) ; => 1
(* 1 2) ; => 2
(/ 2 1) ; => 2
; Você pode comparar igualdade utilizando =
; Esses operadores aceitam um número arbitrário de argumentos
(+ 2 2 2) ; = 2 + 2 + 2 => 6
(- 5 1 1) ; = 5 - 1 - 1 => 3
(* 3 3 3 3) ; = 3 * 3 * 3 * 3 => 81
; Para verificar se dois valores são iguais, o operador = pode ser usado
(= 1 1) ; => true
(= 2 1) ; => false
; Negação para operações lógicas
(not true) ; => false
; Para saber se dois valores são diferentes
(not= 1 2) ; => true
(not (= 1 2)) ; => true
; Aninhar "forms" funciona como esperado
; Conforme vimos acima, é possível aninhar duas formas
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2
(* (- 3 2) (+ 1 2)) ; = (3 - 2) * (1 + 2) => 3
; Se a leitura ficar comprometida, as fórmulas também podem ser escritas em múltiplas linhas
(* (- 3 2)
(+ 1 2)) ; => 3
(*
(- 3 2)
(+ 1 2)) ; => 3
; Tipos
;;;;;;;;;;;;;
; Clojure usa os tipos de objetos de Java para booleanos, strings e números.
; Use `class` para inspecioná-los
(class 1) ; Literais Integer são java.lang.Long por padrão
(class 1.); Literais Float são java.lang.Double
(class ""); Strings são sempre com aspas duplas, e são java.lang.String
; Por ter interoperabilidade com Java, Clojure usa os tipos de objetos de Java para booleanos,
; strings e números. Para descobrir qual o tipo de um valor, você pode usar a função `class`:
(class 1234) ; Literais Integer são java.lang.Long por padrão
(class 1.50) ; Literais Float são java.lang.Double
(class "oi") ; Strings sempre usam aspas duplas e são java.lang.String
(class false) ; Booleanos são java.lang.Boolean
(class nil); O valor "null" é chamado nil
; Se você quiser criar um lista de literais, use aspa simples para
; ela não ser avaliada
'(+ 1 2) ; => (+ 1 2)
; (que é uma abreviação de (quote (+ 1 2)))
; Tenha cuidado, ao dividir valores inteiros:
(= (/ 1 2)
(/ 1.0 2.0)) ; => false
(class (/ 1 2)) ; => clojure.lang.Ratio
(class (/ 1.0 2.0)) ; => java.lang.Double
; Aqui temos uma diferença em relação a Java, pois valores nulos são representados por `nil`
(class nil) ; nil
; É possível avaliar uma lista com aspa simples
(eval '(+ 1 2)) ; => 3
; Coleções e sequências
;;;;;;;;;;;;;;;;;;;
; Listas são estruturas encadeadas, enquanto vetores são implementados como arrays.
; Listas e Vetores são classes Java também!
(class [1 2 3]); => clojure.lang.PersistentVector
(class '(1 2 3)); => clojure.lang.PersistentList
; Os dois tipos básicos de coleção são listas - "list" em inglês - e vetores - "vectors"
; no original. A principal diferença entre eles se
; dá pela implementação:
; - Vetores são implementados como arrays
; - Listas são listas ligadas
(class [1 2 3]) ; => clojure.lang.PersistentVector
(class '(1 2 3)) ; => clojure.lang.PersistentList
; Uma lista é escrita como (1 2 3), mas temos que colocar a aspa
; simples para impedir o leitor (reader) de pensar que é uma função.
; Também, (list 1 2 3) é o mesmo que '(1 2 3)
; Outra forma de declarar listas é usando a função list
(list 1 2 3) ; => '(1 2 3)
; "Coleções" são apenas grupos de dados
; Listas e vetores são ambos coleções:
; Clojure classifica conjuntos de dados de duas maneiras
; "Coleções" são grupos simples de dados
; Tanto listas quanto vetores são coleções:
(coll? '(1 2 3)) ; => true
(coll? [1 2 3]) ; => true
; "Sequências" (seqs) são descrições abstratas de listas de dados.
; Apenas listas são seqs.
; Sequências - ou seqs - são conjuntos de dados com avaliação "lazy"
; Apenas listas são seqs:
(seq? '(1 2 3)) ; => true
(seq? [1 2 3]) ; => false
; Um seq precisa apenas prover uma entrada quando é acessada.
; Portanto, já que seqs podem ser avaliadas sob demanda (lazy) -- elas podem definir séries infinitas:
(range 4) ; => (0 1 2 3)
(range) ; => (0 1 2 3 4 ...) (uma série infinita)
(take 4 (range)) ; (0 1 2 3)
; Ter avaliação lazy significa que uma seq somente precisa prover uma informação quando
; ela for requisitada. Isso permite às seqs representar listas infinitas.
(range) ; => (0 1 2 3 4 ...)
(cycle [1 2]) ; => (1 2 1 2 1 2 ...)
(take 4 (range)) ; => (0 1 2 3)
; Use cons para adicionar um item no início de uma lista ou vetor
; A função cons é usada para adicionar um item ao início de uma lista ou vetor:
(cons 4 [1 2 3]) ; => (4 1 2 3)
(cons 4 '(1 2 3)) ; => (4 1 2 3)
; Conj adiciona um item em uma coleção sempre do jeito mais eficiente.
; Para listas, elas inserem no início. Para vetores, é inserido no final.
; Já conj adiciona um item em uma coleção sempre do jeito mais eficiente.
; Em listas, isso significa inserir no início. Já em vetores, ao final.
(conj [1 2 3] 4) ; => [1 2 3 4]
(conj '(1 2 3) 4) ; => (4 1 2 3)
; Use concat para concatenar listas e vetores
; Concatenação de coleções pode ser feita usando concat. Note que ela sempre gera uma
; seq como resultado e está sujeita a problemas de perfomance em coleções grandes, por
; conta da natureza lazy das seqs.
(concat '(1 2) [3 4]) ; => (1 2 3 4)
(concat [1 2] '(3 4)) ; => (1 2 3 4)
; Use filter, map para interagir com coleções
; Outra forma de concatenar coleções é usando into. Ela não está sujeita a problemas
; com a avaliação lazy, mas o resultado final da ordem e do tipo dos argumentos passados
(into [1 2] '(3 4)) ; => [1 2 3 4]
(into '(1 2) [3 4]) ; => (4 3 1 2)
; Note que em into a ordem dos parâmetros influencia a coleção final.
(into [1 2] '(3 4)) ; => (1 2 3 4)
(into '(1 2) [3 4]) ; => (4 3 1 2)
; As funções filter e map podem ser usadas para interagir com as coleções. Repare que
; elas sempre retornam seqs, independentemente do tipo do seu argumento.
(map inc [1 2 3]) ; => (2 3 4)
(filter even? [1 2 3]) ; => (2)
(filter even? [1 2 3 4]) ; => (2 4)
; Use reduce para reduzi-los
(reduce + [1 2 3 4])
; = (+ (+ (+ 1 2) 3) 4)
; => 10
; Use reduce reduzir coleções a um único valor. Também é possível passar um argumento
; para o valor inicial das operações
(reduce + [1 2 3]) ; = (+ (+ (+ 1 2) 3) 4) => 10
(reduce + 10 [1 2 3 4]) ; = (+ (+ (+ (+ 10 1) 2) 3) 4) => 20
(reduce conj [] '(3 2 1)) ; = (conj (conj (conj [] 3) 2) 1) => [3 2 1]
; Reparou na semelhança entre listas e as chamadas de código Clojure? Isso se deve ao
; fato de que todo código clojure é escrito usando listas. É por isso que elas sempre
; são declaradas com o caracter ' na frente. Dessa forma o interpretador não tenta
; avaliá-las.
'(+ 2 3) ; cria uma lista com os elementos +, 2 e 3
(+ 2 3) ; o interpretador chama a função + passando como argumentos 2 e 3
; Note que ' é apenas uma abreviação para a função quote.
(quote (1 2 3)) ; => '(1 2 3)
; É possível passar uma lista para que o interpretador a avalie. Note que isso está
; sujeito ao primeiro elemento da lista ser um literal com um nome de uma função válida.
(eval '(+ 2 3)) ; => 5
(eval '(1 2 3)) ; dá erro pois o interpretador tenta chamar a função 1, que não existe
; Reduce pode receber um argumento para o valor inicial
(reduce conj [] '(3 2 1))
; = (conj (conj (conj [] 3) 2) 1)
; => [3 2 1]
; Funções
;;;;;;;;;;;;;;;;;;;;;
; Use fn para criar novas funções. Uma função sempre retorna
; sua última expressão.
(fn [] "Hello World") ; => fn
; Use fn para criar novas funções. Uma função sempre retorna sua última expressão.
(fn [] "Olá Mundo") ; => fn
; (É necessário colocar parênteses para chamá-los)
((fn [] "Hello World")) ; => "Hello World"
; Para executar suas funções, é preciso chamá-las, envolvendo-as em parênteses.
((fn [] "Olá Mundo")) ; => "Olá Mundo"
; Você pode atribuir valores a variáveis utilizando def
(def x 1)
x ; => 1
; Como isso não é muito prático, você pode nomear funções atribuindo elas a literais.
; Isso torna muito mais fácil chamá-las:
(def ola-mundo (fn [] "Olá Mundo")) ; => fn
(ola-mundo) ; => "Olá Mundo"
; Atribua uma função para uma var
(def hello-world (fn [] "Hello World"))
(hello-world) ; => "Hello World"
; Você pode abreviar esse processo usando defn:
(defn ola-mundo [] "Olá Mundo")
; Você pode abreviar esse processo usando defn
(defn hello-world [] "Hello World")
; Uma função pode receber uma lista de argumentos:
(defn ola
[nome]
(str "Olá " nome))
(ola "Jonas") ; => "Olá Jonas"
; O [] é uma lista de argumentos para um função.
(defn hello [name]
(str "Hello " name))
(hello "Steve") ; => "Hello Steve"
; É possível criar funções que recebam multivariadas, isto é, que aceitam números
; diferentes de argumentos:
(defn soma
([] 0)
([a] a)
([a b] (+ a b)))
; Você pode ainda usar essa abreviação para criar funcões:
(def hello2 #(str "Hello " %1))
(hello2 "Fanny") ; => "Hello Fanny"
(soma) ; => 0
(soma 1) ; => 1
(soma 1 2) ; => 3
; Vocé pode ter funções multi-variadic, isto é, com um número variável de argumentos
(defn hello3
([] "Hello World")
([name] (str "Hello " name)))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"
; Funções podem agrupar argumentos extras em uma seq:
(defn conta-args
[& args]
(str "Você passou " (count args) " argumentos: " args))
(conta-args 1 2 3 4) ; => "Você passou 4 argumentos: (1 2 3 4)"
; Funções podem agrupar argumentos extras em uma seq
(defn count-args [& args]
(str "You passed " (count args) " args: " args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
; Você pode misturar argumentos regulares e argumentos em seq:
(defn ola-e-conta
[nome & args]
(str "Olá " nome ", você passou " (count args) " argumentos extras"))
(ola-e-conta "Maria" 1 2 3 4) ; => "Olá Maria, você passou 4 argumentos extras"
; Você pode misturar argumentos regulares e argumentos em seq
(defn hello-count [name & args]
(str "Hello " name ", you passed " (count args) " extra args"))
(hello-count "Finn" 1 2 3)
; => "Hello Finn, you passed 3 extra args"
; Nos exemplos acima usamos def para associar nomes a funções, mas poderíamos usá-lo
; para associar nomes a quaisquer valores:
(def xis :x)
xis ; => :x
; Inclusive, tais literais podem possuir alguns caracteres não usuais em outras linguagens:
(def *num-resposta* 42)
(def conexao-ativa? true)
(def grito-de-medo! "AAAAAAA")
(def ->vector-vazio [])
; É possível, inclusive, criar apelidos a nomes que já existem:
(def somar! soma)
(somar! 41 1) ; => 42
; Uma forma rápida de criar funções é por meio de funções anônimas. Elas são ótimas
; para manipulação de coleções e seqs, já que podem ser passadas para map, filter
; e reduce. Nessas funções, % é substituído por cada um dos items na seq ou na coleção:
(filter #(not= % nil) ["Joaquim" nil "Maria" nil "Antônio"]) ; => ("Joaquim" "Maria" "Antônio")
(map #(* % (+ % 2)) [1 2]) ; => (3 8)
; Mapas
;;;;;;;;;;
; Hash maps e array maps compartilham uma mesma interface. Hash maps são mais
; rápidos para pesquisa mas não mantém a ordem da chave.
; Existem dois tipos de mapas: hash maps e array maps. Ambos compartilham uma mesma
; interface e funções. Hash maps são mais rápidos para retornar dados, mas não mantém
; as chaves ordenadas.
(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap
(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap
; Arraymaps pode automaticamente se tornar hashmaps através da maioria das
; operações se eles ficarem grandes o suficiente, portanto não há necessida de
; se preocupar com isso.
; Clojure converte automaticamente array maps em hash maps, por meio da maioria das
; funções de manipulação de mapas, caso eles fiquem grandes o suficiente. Não é
; preciso se preocupar com isso.
;Mapas podem usar qualquer valor que se pode derivar um hash como chave
; Mapas podem usar qualquer valor em que se pode derivar um hash como chave,
; mas normalmente palavras-chave (keywords) são melhores.
; Keywords são como strings mas com algumas vantagens.
; Chaves podem ser qualquer valor do qual possa ser obtido um hash, mas normalmente
; usam-se keywords como chave, por possuírem algumas vantagens.
(class :a) ; => clojure.lang.Keyword
(def stringmap {"a" 1, "b" 2, "c" 3})
stringmap ; => {"a" 1, "b" 2, "c" 3}
; Keywords são como strings, porém, duas keywords de mesmo valor são sempre armazenadas
; na mesma posição de memória, o que as torna mais eficientes.
(identical? :a :a) ; => true
(identical? (String. "a") (String. "a")) ; => false
(def keymap {:a 1, :b 2, :c 3})
keymap ; => {:a 1, :c 3, :b 2}
(def mapa-strings {"a" 1 "b" 2 "c" 3})
mapa-strings ; => {"a" 1, "b" 2, "c" 3}
; A propósito, vírgulas são sempre tratadas como espaçoes em branco e não fazem nada.
(def mapa-keywords {:a 1 :b 2 :c 3})
mapa-keywords ; => {:a 1, :c 3, :b 2}
; Recupere o valor de um mapa chamando ele como uma função
(stringmap "a") ; => 1
(keymap :a) ; => 1
; Você pode usar um mapa como função para recuperar um valor dele:
(mapa-strings "a") ; => 1
(mapa-keywords :a) ; => 1
; Uma palavra-chave pode ser usada pra recuperar os valores de um mapa
(:b keymap) ; => 2
; Se a chave buscada for uma keyword, ela também pode ser usada como função para recuperar
; valores. Note que isso não funciona com strings.
(:b mapa-keywords) ; => 2
("b" mapa-strings) ; => java.lang.String cannot be cast to clojure.lang.IFn
; Não tente isso com strings
;("a" stringmap)
; => Exception: java.lang.String cannot be cast to clojure.lang.IFn
; Se você buscar uma chave que não existe, Clojure retorna nil:
(mapa-strings "d") ; => nil
; Buscar uma chave não presente retorna nil
(stringmap "d") ; => nil
; Use assoc para adicionar novas chaves em um mapa.
(def mapa-keywords-estendido (assoc mapa-keywords :d 4))
mapa-keywords-estendido ; => {:a 1, :b 2, :c 3, :d 4}
; Use assoc para adicionar novas chaves para hash-maps
(def newkeymap (assoc keymap :d 4))
newkeymap ; => {:a 1, :b 2, :c 3, :d 4}
; Mas lembre-se que tipos em Clojure são sempre imutáveis! Isso significa que o mapa
; inicial continua com as mesmas informações e um novo mapa, com mais dados, é criado
; a partir dele
mapa-keywords ; => {:a 1, :b 2, :c 3}
; Mas lembre-se, tipos em Clojure são sempre imutáveis!
keymap ; => {:a 1, :b 2, :c 3}
; assoc também pode ser usado para atualizar chaves:
(def outro-mapa-keywords (assoc mapa-keywords :a 0))
outro-mapa-keywords ; => {:a 0, :b 2, :c 3}
; Use dissoc para remover chaves
(dissoc keymap :a :b) ; => {:c 3}
(dissoc mapa-keywords :a :b) ; => {:c 3}
; Mapas também são coleções - mas não seqs!
(coll? mapa-keywords) ; => true
(seq? mapa-keywords) ; => false
; É possível usar filter, map e qualquer outra função de coleções em mapas.
; Porém a cada iteração um vetor no formato [chave valor] vai ser passado como
; argumento. Por isso é conveniente usar funções anônimas.
(filter #(odd? (second %)) mapa-keywords) ; => ([:a 1] [:c 3])
(map #(inc (second %)) mapa-keywords) ; => (2 3 4)
; Conjuntos
;;;;;;
(class #{1 2 3}) ; => clojure.lang.PersistentHashSet
; Conjuntos são um tipo especial de coleções que não permitem elementos repetidos.
; Eles podem ser criados com #{} ou com a função set.
(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}
(class #{1 2 3}) ; => clojure.lang.PersistentHashSet
; Adicione um membro com conj
(conj #{1 2 3} 4) ; => #{1 2 3 4}
; Note que nem sempre um set vai armazenar seus elementos na ordem esperada.
(def meu-conjunto #{1 2 3})
meu-conjunto ; => #{1 3 2}
; Remova um membro com disj
(disj #{1 2 3} 1) ; => #{2 3}
; Adição funciona normalmente com conj.
(conj meu-conjunto 4) ; => #{1 4 3 2}
; Test por existência usando set como função:
(#{1 2 3} 1) ; => 1
(#{1 2 3} 4) ; => nil
; Remoção, no entanto, precisa ser feita com disj:
(disj meu-conjunto 1) ; => #{3 2}
; Existem muitas outras funções no namespace clojure.sets
; Para saber se um elemento está em um conjunto, use-o como função. Nesse aspecto
; conjuntos funcionam de maneira semelhante a mapas.
(meu-conjunto 1) ; => 1
(meu-conjunto 4) ; => nil
; Forms úteis
; Condicionais e blocos
;;;;;;;;;;;;;;;;;
; Construções lógicas em Clojure são como macros, e
; se parecem com as demais
; Você pode usar um bloco let para criar um escopo local, no qual estarão disponíveis
; os nomes que você definir:
(let [a 1 b 2]
(+ a b)) ; => 3
(let [cores {:yellow "Amarelo" :blue "Azul"}
nova-cor :red
nome-cor "Vermelho"]
(assoc cores nova-cor nome-cor)) ; => {:yellow "Amarelo", :blue "Azul", :red "Vermelho"}
; Formas do tipo if aceitam três argumentos: a condição de teste, o comando a ser
; executado caso a condição seja positiva; e o comando para o caso de ela ser falsa.
(if true "a" "b") ; => "a"
(if false "a" "b") ; => "b"
; Opcionalmente você pode não passar o último argumento, mas se a condição for falsa
; o if vai retornar nil.
(if false "a") ; => nil
; Use let para criar um novo escopo associando sîmbolos a valores (bindings)
(let [a 1 b 2]
(> a b)) ; => false
; A forma if somente aceita um comando para ser executado em cada caso. Se você
; precisar executar mais comandos, você pode usar a função do:
(if true
(do
(print "Olá ")
(print "Mundo"))) ; => escreve "Olá Mundo" na saída
; Agrupe comandos juntos com "do"
(do
(print "Hello")
"World") ; => "World" (prints "Hello")
; Se você só deseja tratar o caso de sua condição ser verdadeira, o comando when é
; uma alternativa melhor. Seu comportamento é idêntico a um if sem condição negativa.
; Uma de suas vantagens é permitir a execução de vários comandos sem exigir do:
(when true "a") ; => "a"
(when true
(print "Olá ")
(print "Mundo")) ; => também escreve "Olá Mundo" na saída
; Funções tem um do implícito
(defn print-and-say-hello [name]
(print "Saying hello to " name)
(str "Hello " name))
(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff")
; Isso ocorre porque when possui um bloco do implícito. O mesmo se aplica a funções e
; comandos let:
(defn escreve-e-diz-xis
[nome]
(print "Diga xis, " nome)
(str "Olá " nome))
(escreve-e-diz-xis "João") ;=> "Olá João", além de escrever "Diga xis, João" na saída.
(let [nome "Nara"]
(print "Diga xis, " nome)
(str "Olá " nome)) ;=> "Olá João", além de escrever "Diga xis, João" na saída.
; Assim como let
(let [name "Urkel"]
(print "Saying hello to " name)
(str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel")
; Módulos
;;;;;;;;;;;;;;;
; Use "use" para poder usar todas as funções de um modulo
; Você pode usar a função use para carregar todas as funções de um módulo.
(use 'clojure.set)
; Agora nós podemos usar operações com conjuntos
; Agora nós podemos usar operações de conjuntos definidas nesse módulo:
(intersection #{1 2 3} #{2 3 4}) ; => #{2 3}
(difference #{1 2 3} #{2 3 4}) ; => #{1}
; Você pode escolher um subconjunto de funções para importar
(use '[clojure.set :only [intersection]])
; Use require para importar um módulo
; Isso porém não é uma boa prática pois dificulta saber de qual módulo cada função
; veio, além de expor o código a conflitos de nomes, caso dois módulos diferentes
; definam funções com o mesmo nome. A melhor forma de referenciar módulos é por meio
; de require:
(require 'clojure.string)
; Use / para chamar funções de um módulo
; Com isso podemos chamar as funções de clojure.string usando o operador /
; Aqui, o módulo é clojure.string e a função é blank?
(clojure.string/blank? "") ; => true
; Você pode dar para um módulo um nome mais curto no import
; Porém isso não é muito prático, por isso é possível dar para um nome mais curto para
; o módulo ao carregá-lo:
(require '[clojure.string :as str])
(str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst."
; (#"" denota uma expressão regular literal)
(str/replace "alguém quer teste?" #"[aeiou]" str/upper-case) ; => "AlgUém qUEr tEstE?"
; Você pode usar require (e até "use", mas escolha require) de um namespace utilizando :require.
; Não é necessário usar aspa simples nos seus módulos se você usar desse jeito.
; Nesse exemplo usamos também a construção #"", que delimita uma expressão regular.
; É possível carregar outros módulos direto na definição do namespace. Note que nesse
; contexto não é preciso usar ' antes do vetor que define a importação do módulo.
(ns test
(:require
[clojure.string :as str]
[clojure.set :as set]))
; Operadores thread
;;;;;;;;;;;;;;;;;
; Uma das funções mais interessantes de clojure são os operadores -> e ->> - respectivamente
; thread-first e thread-last macros. Elas permitem o encadeamento de chamadas de funções,
; sendo perfeitas para melhorar a legibilidade em transformações de dados.
; -> usa o resultado de uma chamada como o primeiro argumento da chamada à função seguinte:
(-> " uMa StRIng com! aLG_uNs ##problemas. "
(str/replace #"[!#_]" "")
(str/replace #"\s+" " ")
str/trim ; se a função só aceitar um argumento, não é preciso usar parênteses
(str/lower-case)) ; => "uma string com alguns problemas."
; Na thread uma string com vários problemas foi passada como primeiro argumento à função
; str/replace, que criou uma nova string, a partir da original, porém somente com caracteres
; alfabéticos. Essa nova string foi passada como primeiro argumento para a chamada str/replace
; seguinte, que criou uma nova string sem espaços duplos. Essa nova string foi então passada
; como primeiro argumento para str/trim, que removeu espaços de seu início e fim, passando essa
; última string para str/lower-case, que a converteu para caracteres em caixa baixa.
; ->> é equivalente a ->, porém o retorno de cada função é passado como último argumento da
; função seguinte. Isso é particularmente útil para lidar com seqs, já que as funções que
; as manipulam sempre as tomam como último argumento.
(->> '(1 2 3 4)
(filter even?) ; => '(2 4)
(map inc) ; => '(3 5)
(reduce *)) ; => 15
; Java
;;;;;;;;;;;;;;;;;
; Java tem uma biblioteca padrão enorme e muito útil,
; portanto é importante aprender como utiliza-la.
; A biblioteca padrão de Java é enorme e possui inúmeros algoritmos e estruturas de
; dados já implementados. Por isso é bastante conveniente saber como usá-la dentro
; de Clojure.
; Use import para carregar um modulo java
; Use import para carregar um módulo Java.
(import java.util.Date)
; Você pode importar usando ns também.
; Você pode importar classes Java dentro de ns também:
(ns test
(:import java.util.Date
java.util.Calendar))
java.util.Calendar
java.util.ArrayList))
; Use o nome da clase com um "." no final para criar uma nova instância
(Date.) ; <a date object>
(def instante (Date.))
(class instante) => ; java.util.Date
; Use . para chamar métodos. Ou, use o atalho ".method"
(. (Date.) getTime) ; <a timestamp>
(.getTime (Date.)) ; exatamente a mesma coisa.
; Para chamar um método, use o operador . com o nome do método. Outra forma é
; usar simplesmente .<nome do método>
(. instante getTime) ; => retorna um inteiro representando o instante
(.getTime instante) ; => exatamente o mesmo que acima
; Use / para chamar métodos estáticos
(System/currentTimeMillis) ; <a timestamp> (o módulo System está sempre presente)
; Para chamar métodos estáticos dentro de classes Java, use /
(System/currentTimeMillis) ; => retorna um timestamp
; Note que não é preciso importar o módulo System, pois ele está sempre presente
; Caso queira submeter uma instância de uma classe mutável a uma sequência de operações,
; você pode usar a função doto. Ela é funciona de maneira semelhante à função -> - ou
; thread-first -, exceto pelo fato de que ele opera com valores mutáveis.
(doto (java.util.ArrayList.)
(.add 11)
(.add 3)
(.add 7)
(java.util.Collections/sort)) ; => #<ArrayList [3, 7, 11]>
; Use doto para pode lidar com classe (mutáveis) de forma mais tolerável
(import java.util.Calendar)
(doto (Calendar/getInstance)
(.set 2000 1 1 0 0 0)
.getTime) ; => A Date. set to 2000-01-01 00:00:00
; STM
;;;;;;;;;;;;;;;;;
; Software Transactional Memory é o mecanismo que Clojure usa para gerenciar
; estado persistente. Tem algumas construções em Clojure que o utilizam.
; Até aqui usamos def para associar nomes a valores. Isso, no entanto, possui algumas
; limitações, já que, uma vez definido essa associação, não podemos alterar o valor
; para o qual um nome aponta. Isso significa que nomes definidos com def não se
; comportam como as variáveis de outras linguagens.
; O atom é o mais simples. Passe pra ele um valor inicial
(def my-atom (atom {}))
; Para lidar com estado persistente e mutação de valores, Clojure usa o mecanismo Software
; Transactional Memory. O atom é o mais simples de todos. Passe pra ele um valor inicial e
; e ele criará um objeto que é seguro de atualizar:
(def atom-mapa (atom {}))
; Atualize o atom com um swap!.
; swap! pega uma função e chama ela com o valor atual do atom
; como primeiro argumento, e qualquer argumento restante como o segundo
(swap! my-atom assoc :a 1) ; Coloca o valor do átomo my-atom como o resultado de (assoc {} :a 1)
(swap! my-atom assoc :b 2) ; Coloca o valor do átomo my-atom como o resultado de (assoc {:a 1} :b 2)
; Para acessar o valor de um atom, você pode usar a função deref ou o operador @:
@atom-mapa ; => {}
(deref atom-mapa) ; => {}
; Use '@' para desreferenciar um atom e acessar seu valor
my-atom ;=> Atom<#...> (Retorna o objeto do Atom)
@my-atom ; => {:a 1 :b 2}
; Para mudar o valor de um atom, você deve usar a função swap!
; O que ela faz é chamar a função passada usando o atom como seu primeiro argumento. Com
; isso, ela altera o valor do atom de maneira segura.
(swap! atom-mapa assoc :a 1) ; Atribui a atom-mapa o resultado de (assoc {} :a 1)
(swap! atom-mapa assoc :b 2) ; Atribui a atom-mapa o resultado de (assoc {:a 1} :b 2)
; Abaixo um contador simples usando um atom
(def counter (atom 0))
(defn inc-counter []
(swap! counter inc))
; Observe que essas chamadas alteraram de fato o valor de atom-mapa. Seu novo valor é:
@atom-mapa ; => {:a 1 :b 2}
(inc-counter)
(inc-counter)
(inc-counter)
(inc-counter)
(inc-counter)
; Isso é diferente de fazer:
(def atom-mapa-2 (atom {}))
(def atom-mapa-3 (assoc @atom-mapa-2 :a 1))
@counter ; => 5
; Nesse exemplo, atom-mapa-2 permanece com o seu valor original e é gerado um novo mapa,
; atom-mapa-3, que contém o valor de atom-mapa-2 atualizado. Note que atom-mapa-3 é um
; simples mapa, e não uma instância de um atom
@atom-mapa-2 ; => {}
atom-mapa-3 ; => {:a 1}
; Outras construção STM são refs e agents.
(class atom-mapa-2) ; => clojure.lang.Atom
(class atom-mapa-3) ; => clojure.lang.PersistentArrayMap
; A ideia é que o valor do atom só será atualizado se, após ser executada a função passada
; para swap!, o atom ainda estiver com o mesmo valor de antes. Isto é, se durante a execução
; da função alguém alterar o valor do atom, swap! reexecutará a função recebida usando o valor
; atual do átoma como argumento.
; Isso é ótimo em situações nas quais é preciso garantir a consistência de algum valor - tais
; como sistemas bancários e sites de compra. Para mais exemplos e informações sobre outras
; construções STM:
; Exemplos e aplicações: https://www.braveclojure.com/zombie-metaphysics/
; Refs: http://clojure.org/refs
; Agents: http://clojure.org/agents
```
### Leitura adicional
Esse tutorial está longe de ser exaustivo, mas deve ser suficiente para que você possa começar.
Esse tutorial está longe de ser completo, mas deve ser suficiente para que você possa dar seus primeiros passos em Clojure.
Caso queira aprender mais:
Clojure.org tem vários artigos:
* clojure.org tem vários artigos:
[http://clojure.org/](http://clojure.org/)
Clojuredocs.org tem documentação com exemplos para quase todas as funções principais (pertecentes ao core):
* Brave Clojure possui um e-book que explora em profundidade diversos recursos de clojure, incluindo ótimos exemplos:
[https://www.braveclojure.com/](https://www.braveclojure.com/)
* clojuredocs.org tem documentação com exemplos para quase todas as funções principais (pertecentes ao core):
[http://clojuredocs.org/quickref/Clojure%20Core](http://clojuredocs.org/quickref/Clojure%20Core)
4Clojure é um grande jeito de aperfeiçoar suas habilidades em Clojure/Programação Funcional:
* 4clojure possui alguns problemas e desafios interessantes para quem quiser treinar clojure ou programação funcional:
[http://www.4clojure.com/](http://www.4clojure.com/)
Clojure-doc.org tem um bom número de artigos para iniciantes:
* clojure-doc.org tem um bom número de artigos para iniciantes:
[http://clojure-doc.org/](http://clojure-doc.org/)