1
0
mirror of https://github.com/adambard/learnxinyminutes-docs.git synced 2025-01-17 05:28:37 +01:00

Merge pull request #3701 from ysads/clojure/pt-br/rewrite-text

[clojure/pt-br] Rewrite text to be more natural in portuguese
This commit is contained in:
Divay Prakash 2020-01-24 19:46:39 +05:30 committed by GitHub
commit 5c16cd103d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -5,12 +5,13 @@ contributors:
- ["Adam Bard", "http://adambard.com/"] - ["Adam Bard", "http://adambard.com/"]
translators: translators:
- ["Mariane Siqueira Machado", "https://twitter.com/mariane_sm"] - ["Mariane Siqueira Machado", "https://twitter.com/mariane_sm"]
- ["Ygor Sad", "https://github.com/ysads"]
lang: pt-br 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) (Sua versão de clojure precisa ser pelo menos 1.2)
@ -18,369 +19,551 @@ Essa combinação permite gerenciar processamento concorrente de maneira muito s
```clojure ```clojure
; Comentários começam por ponto e vírgula ; Comentários começam por ponto e vírgula
; Clojure é escrito em "forms", os quais são simplesmente ; Código Clojure é escrito em formas - 'forms', em inglês. Tais estruturas são
; listas de coisas dentro de parênteses, separados por espaços em branco. ; 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 ; Ao interpretar um código em Clojure, o interpretador ou leitor - do inglês 'reader' - assume
; uma par de parênteses é uma função ou macro, e que os resto são argumentos. ; 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) (ns learnclojure)
; Alguns exemplos básicos: ; Alguns exemplos básicos:
; str cria uma string concatenando seus argumentos ; Aqui, str é uma função e "Olá" " " e "Mundo" são seus argumentos. O que ela faz é criar
(str "Hello" " " "World") ; => "Hello World" ; 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 (+ 1 1) ; => 2
(- 2 1) ; => 1 (- 2 1) ; => 1
(* 1 2) ; => 2 (* 1 2) ; => 2
(/ 2 1) ; => 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 (= 1 1) ; => true
(= 2 1) ; => false (= 2 1) ; => false
; Negação para operações lógicas ; Para saber se dois valores são diferentes
(not true) ; => false (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 (+ 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 ; Tipos
;;;;;;;;;;;;; ;;;;;;;;;;;;;
; Clojure usa os tipos de objetos de Java para booleanos, strings e números. ; Por ter interoperabilidade com Java, Clojure usa os tipos de objetos de Java para booleanos,
; Use `class` para inspecioná-los ; strings e números. Para descobrir qual o tipo de um valor, você pode usar a função `class`:
(class 1) ; Literais Integer são java.lang.Long por padrão (class 1234) ; Literais Integer são java.lang.Long por padrão
(class 1.); Literais Float são java.lang.Double (class 1.50) ; Literais Float são java.lang.Double
(class ""); Strings são sempre com aspas duplas, e são java.lang.String (class "oi") ; Strings sempre usam aspas duplas e são java.lang.String
(class false) ; Booleanos são java.lang.Boolean (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 ; Tenha cuidado, ao dividir valores inteiros:
; ela não ser avaliada (= (/ 1 2)
'(+ 1 2) ; => (+ 1 2) (/ 1.0 2.0)) ; => false
; (que é uma abreviação de (quote (+ 1 2)))
(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 ; Coleções e sequências
;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;
; Listas são estruturas encadeadas, enquanto vetores são implementados como arrays. ; Os dois tipos básicos de coleção são listas - "list" em inglês - e vetores - "vectors"
; Listas e Vetores são classes Java também! ; no original. A principal diferença entre eles se
(class [1 2 3]); => clojure.lang.PersistentVector ; dá pela implementação:
(class '(1 2 3)); => clojure.lang.PersistentList ; - 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 ; Outra forma de declarar listas é usando a função list
; simples para impedir o leitor (reader) de pensar que é uma função. (list 1 2 3) ; => '(1 2 3)
; Também, (list 1 2 3) é o mesmo que '(1 2 3)
; "Coleções" são apenas grupos de dados ; Clojure classifica conjuntos de dados de duas maneiras
; Listas e vetores são ambos coleções:
; "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
(coll? [1 2 3]) ; => true (coll? [1 2 3]) ; => true
; "Sequências" (seqs) são descrições abstratas de listas de dados. ; "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)) ; => true
(seq? [1 2 3]) ; => false (seq? [1 2 3]) ; => false
; Um seq precisa apenas prover uma entrada quando é acessada. ; Ter avaliação lazy significa que uma seq somente precisa prover uma informação quando
; Portanto, já que seqs podem ser avaliadas sob demanda (lazy) -- elas podem definir séries infinitas: ; ela for requisitada. Isso permite às seqs representar listas infinitas.
(range 4) ; => (0 1 2 3) (range) ; => (0 1 2 3 4 ...)
(range) ; => (0 1 2 3 4 ...) (uma série infinita) (cycle [1 2]) ; => (1 2 1 2 1 2 ...)
(take 4 (range)) ; (0 1 2 3) (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)
(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. ; Já 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. ; 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) ; => [1 2 3 4]
(conj '(1 2 3) 4) ; => (4 1 2 3) (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) (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) (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 ; Use reduce reduzir coleções a um único valor. Também é possível passar um argumento
(reduce + [1 2 3 4]) ; para o valor inicial das operações
; = (+ (+ (+ 1 2) 3) 4) (reduce + [1 2 3]) ; = (+ (+ (+ 1 2) 3) 4) => 10
; => 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 ; Funções
;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;
; Use fn para criar novas funções. Uma função sempre retorna ; Use fn para criar novas funções. Uma função sempre retorna sua última expressão.
; sua última expressão. (fn [] "Olá Mundo") ; => fn
(fn [] "Hello World") ; => fn
; (É necessário colocar parênteses para chamá-los) ; Para executar suas funções, é preciso chamá-las, envolvendo-as em parênteses.
((fn [] "Hello World")) ; => "Hello World" ((fn [] "Olá Mundo")) ; => "Olá Mundo"
; Você pode atribuir valores a variáveis utilizando def ; Como isso não é muito prático, você pode nomear funções atribuindo elas a literais.
(def x 1) ; Isso torna muito mais fácil chamá-las:
x ; => 1 (def ola-mundo (fn [] "Olá Mundo")) ; => fn
(ola-mundo) ; => "Olá Mundo"
; Atribua uma função para uma var ; Você pode abreviar esse processo usando defn:
(def hello-world (fn [] "Hello World")) (defn ola-mundo [] "Olá Mundo")
(hello-world) ; => "Hello World"
; Você pode abreviar esse processo usando defn ; Uma função pode receber uma lista de argumentos:
(defn hello-world [] "Hello World") (defn ola
[nome]
(str "Olá " nome))
(ola "Jonas") ; => "Olá Jonas"
; O [] é uma lista de argumentos para um função. ; É possível criar funções que recebam multivariadas, isto é, que aceitam números
(defn hello [name] ; diferentes de argumentos:
(str "Hello " name)) (defn soma
(hello "Steve") ; => "Hello Steve" ([] 0)
([a] a)
([a b] (+ a b)))
; Você pode ainda usar essa abreviação para criar funcões: (soma) ; => 0
(def hello2 #(str "Hello " %1)) (soma 1) ; => 1
(hello2 "Fanny") ; => "Hello Fanny" (soma 1 2) ; => 3
; Vocé pode ter funções multi-variadic, isto é, com um número variável de argumentos ; Funções podem agrupar argumentos extras em uma seq:
(defn hello3 (defn conta-args
([] "Hello World") [& args]
([name] (str "Hello " name))) (str "Você passou " (count args) " argumentos: " args))
(hello3 "Jake") ; => "Hello Jake" (conta-args 1 2 3 4) ; => "Você passou 4 argumentos: (1 2 3 4)"
(hello3) ; => "Hello World"
; Funções podem agrupar argumentos extras em uma seq ; Você pode misturar argumentos regulares e argumentos em seq:
(defn count-args [& args] (defn ola-e-conta
(str "You passed " (count args) " args: " args)) [nome & args]
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)" (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] ; Nos exemplos acima usamos def para associar nomes a funções, mas poderíamos usá-lo
(str "Hello " name ", you passed " (count args) " extra args")) ; para associar nomes a quaisquer valores:
(hello-count "Finn" 1 2 3) (def xis :x)
; => "Hello Finn, you passed 3 extra args" 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 ; Mapas
;;;;;;;;;; ;;;;;;;;;;
; Hash maps e array maps compartilham uma mesma interface. Hash maps são mais ; Existem dois tipos de mapas: hash maps e array maps. Ambos compartilham uma mesma
; rápidos para pesquisa mas não mantém a ordem da chave. ; 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 {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap
(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap (class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap
; Arraymaps pode automaticamente se tornar hashmaps através da maioria das ; Clojure converte automaticamente array maps em hash maps, por meio da maioria das
; operações se eles ficarem grandes o suficiente, portanto não há necessida de ; funções de manipulação de mapas, caso eles fiquem grandes o suficiente. Não é
; se preocupar com isso. ; preciso se preocupar com isso.
;Mapas podem usar qualquer valor que se pode derivar um hash como chave ; Chaves podem ser qualquer valor do qual possa ser obtido um hash, mas normalmente
; usam-se keywords como chave, por possuírem algumas vantagens.
; 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.
(class :a) ; => clojure.lang.Keyword (class :a) ; => clojure.lang.Keyword
(def stringmap {"a" 1, "b" 2, "c" 3}) ; Keywords são como strings, porém, duas keywords de mesmo valor são sempre armazenadas
stringmap ; => {"a" 1, "b" 2, "c" 3} ; 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}) (def mapa-strings {"a" 1 "b" 2 "c" 3})
keymap ; => {:a 1, :c 3, :b 2} 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 ; Você pode usar um mapa como função para recuperar um valor dele:
(stringmap "a") ; => 1 (mapa-strings "a") ; => 1
(keymap :a) ; => 1 (mapa-keywords :a) ; => 1
; Uma palavra-chave pode ser usada pra recuperar os valores de um mapa ; Se a chave buscada for uma keyword, ela também pode ser usada como função para recuperar
(:b keymap) ; => 2 ; 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 ; Se você buscar uma chave que não existe, Clojure retorna nil:
;("a" stringmap) (mapa-strings "d") ; => nil
; => Exception: java.lang.String cannot be cast to clojure.lang.IFn
; Buscar uma chave não presente retorna nil ; Use assoc para adicionar novas chaves em um mapa.
(stringmap "d") ; => nil (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 ; Mas lembre-se que tipos em Clojure são sempre imutáveis! Isso significa que o mapa
(def newkeymap (assoc keymap :d 4)) ; inicial continua com as mesmas informações e um novo mapa, com mais dados, é criado
newkeymap ; => {:a 1, :b 2, :c 3, :d 4} ; a partir dele
mapa-keywords ; => {:a 1, :b 2, :c 3}
; Mas lembre-se, tipos em Clojure são sempre imutáveis! ; assoc também pode ser usado para atualizar chaves:
keymap ; => {:a 1, :b 2, :c 3} (def outro-mapa-keywords (assoc mapa-keywords :a 0))
outro-mapa-keywords ; => {:a 0, :b 2, :c 3}
; Use dissoc para remover chaves ; 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 ; 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} (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 ; Note que nem sempre um set vai armazenar seus elementos na ordem esperada.
(conj #{1 2 3} 4) ; => #{1 2 3 4} (def meu-conjunto #{1 2 3})
meu-conjunto ; => #{1 3 2}
; Remova um membro com disj ; Adição funciona normalmente com conj.
(disj #{1 2 3} 1) ; => #{2 3} (conj meu-conjunto 4) ; => #{1 4 3 2}
; Test por existência usando set como função: ; Remoção, no entanto, precisa ser feita com disj:
(#{1 2 3} 1) ; => 1 (disj meu-conjunto 1) ; => #{3 2}
(#{1 2 3} 4) ; => nil
; 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 ; Você pode usar um bloco let para criar um escopo local, no qual estarão disponíveis
; se parecem com as demais ; 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" (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 (if false "a") ; => nil
; Use let para criar um novo escopo associando sîmbolos a valores (bindings) ; A forma if somente aceita um comando para ser executado em cada caso. Se você
(let [a 1 b 2] ; precisar executar mais comandos, você pode usar a função do:
(> a b)) ; => false (if true
(do
(print "Olá ")
(print "Mundo"))) ; => escreve "Olá Mundo" na saída
; Agrupe comandos juntos com "do" ; Se você só deseja tratar o caso de sua condição ser verdadeira, o comando when é
(do ; uma alternativa melhor. Seu comportamento é idêntico a um if sem condição negativa.
(print "Hello") ; Uma de suas vantagens é permitir a execução de vários comandos sem exigir do:
"World") ; => "World" (prints "Hello") (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 ; Isso ocorre porque when possui um bloco do implícito. O mesmo se aplica a funções e
(defn print-and-say-hello [name] ; comandos let:
(print "Saying hello to " name) (defn escreve-e-diz-xis
(str "Hello " name)) [nome]
(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff") (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 ; 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) (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} (intersection #{1 2 3} #{2 3 4}) ; => #{2 3}
(difference #{1 2 3} #{2 3 4}) ; => #{1} (difference #{1 2 3} #{2 3 4}) ; => #{1}
; Você pode escolher um subconjunto de funções para importar ; Isso porém não é uma boa prática pois dificulta saber de qual módulo cada função
(use '[clojure.set :only [intersection]]) ; 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
; Use require para importar um módulo ; de require:
(require 'clojure.string) (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? ; Aqui, o módulo é clojure.string e a função é blank?
(clojure.string/blank? "") ; => true (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]) (require '[clojure.string :as str])
(str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst." (str/replace "alguém quer teste?" #"[aeiou]" str/upper-case) ; => "AlgUém qUEr tEstE?"
; (#"" denota uma expressão regular literal)
; Você pode usar require (e até "use", mas escolha require) de um namespace utilizando :require. ; Nesse exemplo usamos também a construção #"", que delimita uma expressão regular.
; Não é necessário usar aspa simples nos seus módulos se você usar desse jeito.
; É 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 (ns test
(:require (:require
[clojure.string :as str] [clojure.string :as str]
[clojure.set :as set])) [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
;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;
; Java tem uma biblioteca padrão enorme e muito útil, ; A biblioteca padrão de Java é enorme e possui inúmeros algoritmos e estruturas de
; portanto é importante aprender como utiliza-la. ; 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) (import java.util.Date)
; Você pode importar usando ns também. ; Você pode importar classes Java dentro de ns também:
(ns test (ns test
(:import java.util.Date (: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 ; 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" ; Para chamar um método, use o operador . com o nome do método. Outra forma é
(. (Date.) getTime) ; <a timestamp> ; usar simplesmente .<nome do método>
(.getTime (Date.)) ; exatamente a mesma coisa. (. instante getTime) ; => retorna um inteiro representando o instante
(.getTime instante) ; => exatamente o mesmo que acima
; Use / para chamar métodos estáticos ; Para chamar métodos estáticos dentro de classes Java, use /
(System/currentTimeMillis) ; <a timestamp> (o módulo System está sempre presente) (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 ; STM
;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;
; Software Transactional Memory é o mecanismo que Clojure usa para gerenciar ; Até aqui usamos def para associar nomes a valores. Isso, no entanto, possui algumas
; estado persistente. Tem algumas construções em Clojure que o utilizam. ; 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 ; Para lidar com estado persistente e mutação de valores, Clojure usa o mecanismo Software
(def my-atom (atom {})) ; 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!. ; Para acessar o valor de um atom, você pode usar a função deref ou o operador @:
; swap! pega uma função e chama ela com o valor atual do atom @atom-mapa ; => {}
; como primeiro argumento, e qualquer argumento restante como o segundo (deref atom-mapa) ; => {}
(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)
; Use '@' para desreferenciar um atom e acessar seu valor ; Para mudar o valor de um atom, você deve usar a função swap!
my-atom ;=> Atom<#...> (Retorna o objeto do Atom) ; O que ela faz é chamar a função passada usando o atom como seu primeiro argumento. Com
@my-atom ; => {:a 1 :b 2} ; 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 ; Observe que essas chamadas alteraram de fato o valor de atom-mapa. Seu novo valor é:
(def counter (atom 0)) @atom-mapa ; => {:a 1 :b 2}
(defn inc-counter []
(swap! counter inc))
(inc-counter) ; Isso é diferente de fazer:
(inc-counter) (def atom-mapa-2 (atom {}))
(inc-counter) (def atom-mapa-3 (assoc @atom-mapa-2 :a 1))
(inc-counter)
(inc-counter)
@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 ; Refs: http://clojure.org/refs
; Agents: http://clojure.org/agents ; Agents: http://clojure.org/agents
``` ```
### Leitura adicional ### 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/) [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) [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/) [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/) [http://clojure-doc.org/](http://clojure-doc.org/)
Clojure for the Brave and True é um livro de introdução ao Clojure e possui uma versão gratuita online: Clojure for the Brave and True é um livro de introdução ao Clojure e possui uma versão gratuita online: