Este é o capítulo mais importante para iniciantes! Vamos aprender:
- Sintaxe: Como escrever código Clojure
- Estruturas de dados: Numbers, strings, maps, vectors, lists, sets
- Funções: Criar, chamar e usar funções
- Projeto prático: Modelar um hobbit e criar função para "atacá-lo"
Clojure tem estrutura muito simples e uniforme. Tudo segue o mesmo padrão:
(operador operando1 operando2 ... operandoN)Clojure reconhece dois tipos de estruturas:
- Literais: Representações diretas de dados
- Operações: Fazem coisas com os dados
;; Literais
1
"uma string"
["um" "vetor" "de" "strings"]
;; Operações
(+ 1 2 3) ; => 6
(str "Olá" " " "mundo") ; => "Olá mundo"(if condicao-booleana
forma-then
forma-else-opcional)
;; Exemplos
(if true
"Verdadeiro!"
"Falso!")
; => "Verdadeiro!"
(if false
"Nunca executado")
; => nil(if true
(do (println "Sucesso!")
"Resultado final")
(do (println "Falha!")
"Outro resultado"))
; => Sucesso!
; => "Resultado final"(when true
(println "Executando...")
"valor de retorno")
; => Executando...
; => "valor de retorno"- Falsy:
nilefalse - Truthy: Todo o resto (incluindo 0, "", [])
(if "qualquer string"
"strings são truthy")
; => "strings são truthy"
(if nil
"nunca executado"
"nil é falsy")
; => "nil é falsy";; or - retorna primeiro valor truthy ou último valor
(or false nil :primeiro-truthy :nunca-alcançado)
; => :primeiro-truthy
(or false nil)
; => nil
;; and - retorna primeiro falsy ou último truthy
(and :primeiro :segundo :terceiro)
; => :terceiro
(and :primeiro nil :nunca-alcançado)
; => nil(def lista-herois
["Superman" "Batman" "Wonder Woman"])
lista-herois
; => ["Superman" "Batman" "Wonder Woman"]def como constantes, não como variáveis mutáveis!
Todas as estruturas em Clojure são imutáveis - não podem ser modificadas no local.
93 ; inteiro
1.2 ; float
1/5 ; ratio (fração)"Uma string"
"String com \\"aspas\\" escapadas"
;; Concatenação
(def nome "João")
(str "Olá, " nome "!")
; => "Olá, João!"Associam chaves a valores (como dicionários/objetos):
;; Map vazio
{}
;; Map com keywords
{:nome "Ana"
:idade 25
:cidade "São Paulo"}
;; Map com strings
{"chave-string" "valor"}
;; Maps aninhados
{:pessoa {:nome "Carlos" :sobrenome "Silva"}}Acessando valores:
(def pessoa {:nome "Ana" :idade 25})
(get pessoa :nome) ; => "Ana"
(get pessoa :altura "N/A") ; => "N/A" (valor padrão)
;; Map como função
(pessoa :nome) ; => "Ana"
;; Keyword como função
(:nome pessoa) ; => "Ana"Maps aninhados:
(def dados {:pessoa {:nome "João" :idade 30}})
(get-in dados [:pessoa :nome]) ; => "João"Identificadores especiais que começam com ::
:nome
:idade
:categoria
:_qualquer-coisa
;; Como função para lookup
(:nome {:nome "Ana" :idade 25}) ; => "Ana"
(:altura {:nome "Ana"} "N/A") ; => "N/A"Coleções ordenadas e indexadas (como arrays):
[1 2 3 4]
["misturado" 42 :keyword {:mapa "valor"}]
;; Acessando por índice
(get ["a" "b" "c"] 0) ; => "a"
(get ["a" "b" "c"] 1) ; => "b"
;; Criando vetores
(vector "um" "dois" "três") ; => ["um" "dois" "três"]
;; Adicionando elementos (ao final)
(conj [1 2 3] 4) ; => [1 2 3 4]Coleções ordenadas otimizadas para acesso sequencial:
'(1 2 3 4) ; Literal com quote
;; Acessando por posição
(nth '(:a :b :c) 0) ; => :a
(nth '(:a :b :c) 2) ; => :c
;; Criando listas
(list 1 "dois" {:tres 4}) ; => (1 "dois" {:tres 4})
;; Adicionando elementos (ao início)
(conj '(1 2 3) 4) ; => (4 1 2 3)Vector vs List:
- Vector: Use quando precisa de acesso rápido por índice
- List: Use quando vai adicionar no início ou escrevendo macros
Coleções de valores únicos:
#{"valor1" "valor2" :keyword}
;; Criando sets
(hash-set 1 1 2 2 3) ; => #{1 2 3}
(set [1 1 2 2 3]) ; => #{1 2 3}
;; Adicionando (duplicatas ignoradas)
(conj #{:a :b} :b) ; => #{:a :b}
;; Verificando pertencimento
(contains? #{:a :b} :a) ; => true
(:a #{:a :b}) ; => :a
(get #{:a :b} :a) ; => :a"É melhor ter 100 funções operando em uma estrutura de dados do que 10 funções em 10 estruturas." — Alan Perlis
(+ 1 2 3) ; => 6
(* 2 3 4) ; => 24
(str "a" "b" "c") ; => "abc"Expressões como operadores:
((or + -) 1 2 3) ; => 6
((first [+ *]) 10 5) ; => 15Funções de alta ordem:
(map inc [0 1 2 3]) ; => (1 2 3 4)(defn nome-da-funcao
"Docstring opcional"
[parametros]
corpo-da-funcao)
;; Exemplo
(defn saudacao
"Cria uma saudação personalizada"
[nome]
(str "Olá, " nome "!"))
(saudacao "Maria") ; => "Olá, Maria!"(defn multi-arity
([x] (multi-arity x 0)) ; 1 parâmetro chama versão de 2
([x y] (+ x y))) ; 2 parâmetros
(multi-arity 5) ; => 5
(multi-arity 5 3) ; => 8(defn var-args
[primeiro & resto]
(str "Primeiro: " primeiro
", Resto: " resto))
(var-args "a" "b" "c" "d")
; => "Primeiro: a, Resto: (\\"b\\" \\"c\\" \\"d\\")"Vetores/Listas:
(defn meu-primeiro
[[primeiro-item]] ; Destructuring do primeiro elemento
primeiro-item)
(meu-primeiro ["a" "b" "c"]) ; => "a"
;; Com rest parameters
(defn escolhedor
[[primeira segunda & outras]]
(println "Primeira:" primeira)
(println "Segunda:" segunda)
(println "Outras:" outras))Maps:
(defn localizar-tesouro
[{:keys [lat lng]}] ; Extrai :lat e :lng
(println "Latitude:" lat)
(println "Longitude:" lng))
(localizar-tesouro {:lat 28.22 :lng 81.33})
;; Com :as para manter referência original
(defn processar-coordenadas
[{:keys [lat lng] :as coords}]
(println "Processando:" coords)
(+ lat lng))Forma fn:
(fn [x] (* x 2)) ; Função anônima
(map (fn [nome] (str "Oi, " nome))
["Ana" "João"])
; => ("Oi, Ana" "Oi, João")
;; Com nome local
(def dobrar (fn [x] (* x 2)))Forma #():
#(* % 2) ; Forma compacta
(map #(str "Oi, " %) ["Ana" "João"])
; => ("Oi, Ana" "Oi, João")
;; Múltiplos parâmetros
#(str %1 " e " %2) ; %1, %2, %3...
;; Rest parameters
#(println %&) ; %& = rest args(defn criar-incrementador
[n]
#(+ % n)) ; Closure - acessa 'n'
(def inc3 (criar-incrementador 3))
(inc3 7) ; => 10(let [x 1
y 2]
(+ x y)) ; => 3
;; Com destructuring
(let [[primeiro & resto] [1 2 3 4]]
(str "Primeiro: " primeiro
", Resto: " resto))(loop [contador 0]
(println "Contador:" contador)
(if (< contador 3)
(recur (inc contador)) ; Chama loop novamente
"Fim!"))#"padrão" ; Literal regex
(re-find #"^left-" "left-eye") ; => "left-"
(re-find #"^left-" "right-eye") ; => nil
;; Substituição
(clojure.string/replace "left-hand" #"^left-" "right-")
; => "right-hand"(reduce + [1 2 3 4]) ; => 10
; Equivale a: (+ (+ (+ 1 2) 3) 4)
(reduce + 10 [1 2 3 4]) ; => 20 (com valor inicial)
;; Construindo coleções
(reduce conj [] [:a :b :c]) ; => [:a :b :c](def partes-corpo-assimetricas
[{:nome "cabeça" :tamanho 3}
{:nome "olho-esquerdo" :tamanho 1}
{:nome "orelha-esquerda" :tamanho 1}
{:nome "ombro-esquerdo" :tamanho 3}
;; ... mais partes esquerdas
])(defn parte-correspondente
[parte]
{:nome (clojure.string/replace (:nome parte) #"^esquerdo-" "direito-")
:tamanho (:tamanho parte)})(defn simetrizar-partes-corpo
[partes-assimetricas]
(reduce (fn [partes-finais parte]
(into partes-finais
(set [parte (parte-correspondente parte)])))
[]
partes-assimetricas))(defn atingir
[partes-assimetricas]
(let [partes-simetricas (simetrizar-partes-corpo partes-assimetricas)
soma-tamanhos (reduce + (map :tamanho partes-simetricas))
alvo (rand soma-tamanhos)]
(loop [[parte & restantes] partes-simetricas
tamanho-acumulado (:tamanho parte)]
(if (> tamanho-acumulado alvo)
parte
(recur restantes
(+ tamanho-acumulado (:tamanho (first restantes))))))))
;; Testando
(atingir partes-corpo-assimetricas)
; => {:nome "ombro-direito", :tamanho 3}- Estrutura uniforme:
(operador operandos...) - Notação prefixa em tudo
- Formas = código válido
- Imutáveis por padrão
- Numbers, Strings: Tipos básicos
- Maps:
{:chave valor}- associações - Keywords:
:palavra- identificadores - Vectors:
[1 2 3]- sequências indexadas - Lists:
'(1 2 3)- sequências linkadas - Sets:
#{1 2 3}- valores únicos
- Cidadãos de primeira classe - podem ser valores
- Higher-order: recebem/retornam outras funções
- Aridade múltipla: diferentes números de parâmetros
- Destructuring: decomposição automática de estruturas
- Anônimas:
fne#() - Closures: capturam ambiente
- let: binding local de nomes
- loop/recur: recursão otimizada
- reduce: padrão processo-e-acumule
- Regex:
#"padrão"matching
- Simplicidade: poucas estruturas, muitas funções
- Composição: funções pequenas que se combinam
- Imutabilidade: dados não mudam
- Expressividade: código como dados
- Pratique no REPL - digite todos os exemplos
- Experimente variações - mude os exemplos
- Faça os exercícios do final do capítulo
- Use o Cheat Sheet: clojure.org/api/cheatsheet
- Desafios: 4Clojure.com e Project Euler
- Use
str,vector,list,hash-map,hash-set - Escreva função que adiciona 100 a um número
- Implemente
dec-maker(comoinc-makermas subtraindo) - Escreva
mapset(comomapmas retorna set) - Crie simetrizador para alienígenas com 5 membros
- Generalize o simetrizador para N membros
Próximo: Capítulo 4 - Core Functions in Depth
Lembre-se: Este capítulo é a base de tudo - pratique muito!