Estruturas de Dados

  • Vetores
  • Mapas

Grupo de dados - Coleções

Até agora lidamos com pedaços discretos de dados: um número, uma string, um valor. Em programação, no entanto, é mais comum o caso em que temos que lidar com grupos de dados.

Clojure tem ótimas facilidades para trabalhar com esses grupos, ou coleções de dados. Não só nos dá quatro tipos diferentes de coleções mas também nos permite acessar de uma maneira uniforme todos esses tipos de coleções junto.

Vetores

Coleção sequencial

Um vetor é uma coleção sequencial de valores. Um vetor pode estar vazio. Um vetor pode ter valores de diferentes tipos. Cada valor em um vetor é numerado começando em 0, e esse número é chamado de índice. O índice é usado para consultar cada valor de dentro do vetor quando estivermos procurando algo.

Estrutura em divisórias

Para imaginar um vetor, pense em uma caixa separada em uma quantidade de divisórias de mesmo tamanho. Cada uma dessas divisórias tem um número. Você pode colocar algum dado dentro de cada divisória e sempre saber onde procurar esse dado no número da divisória.

Note que os números começam em zero. Isso pode parecer estranho, mas no geral em programação começamos a contar do zero.

Vetor

Sintaxe

Vetores são geralmente escritos entre colchetes [] com qualquer quantidade de valores separados por espaços. Estes são alguns exemplos de vetores:

[1 2 3 4 5]
[56.9 60.2 61.8 63.1 54.3 66.4 66.5 68.1 70.2 69.2 63.1 57.1]
[]

Criação

As duas próximas funções são usadas pra criar novos vetores. A função vector recebe um número qualquer de itens e os coloca em um vetor novo. conj é uma função interessante que você verá sendo usada com todas as estruturas de dados. Com vetores, ela recebe um vetor e um item e devolve um novo vetor que tem esse item adicionado ao final dos itens do vetor recebido. Por que o nome conj? conj é abreviação de conjoin, que significa juntar ou combinar. E é isso que estamos fazendo: Estamos juntando o novo item ao vetor.

(vector 5 10 15)
;=> [5 10 15]

(conj [5 10] 15)
;=> [5 10 15]

Extração

Agora veja estas quatro funções. count nos dá a quantidade de itens que o vetor tem. nth nos dá o enézimo item do vetor. Note que começamos a contar do 0, então no exemplo chamar nth com o número 1 nos dá o que chamaríamos de segundo elemento se não estivéssemos programando. first devolve o primeiro item da coleção. rest retorna todos os itens exceto o primeiro. Tente não pensar sobre isso e nth ao mesmo tempo, pois pode ser confuso.

(count [5 10 15])
;=> 3
(nth [5 10 15] 1)
;=> 10
(first [5 10 15])
;=> 5
(rest [5 10 15])
;=> (10 15)

Mapas

pares de chave-valor

Mapas são conjuntos de chave e valores associados a elas. Você pode pensar nelas como se fossem um dicionário: você procura por coisas usando uma palavra (uma palavra-chave/keyword) e consegue ver a sua definição (seu valor). Se você já programou em outra linguagem, você pode já ter visto algo parecido com mapas – talvez chamados de dicionários, hashes ou arrays associativos.

Mapa

Sintaxe

Para escrever mapas alternamos chaves e valores, colocando chaves {} em volta de todas elas.

Mapas são útes pois conseguem guardar dados da maneira que normalmente já pensamos sobre eles. Pense no nosso exemplo hipotético, Sally Brown. Um mapa pode guardar o seu nome e sobrenome, seu endereço, sua comida favorita ou qualquer outra coisa. É uma forma simples de coletar estes dados e ser fácil de procurar depois. O último exemplo é um mapa vazio. É um mapa que está pronto pra guardar alguma coisa, mas ainda não tem nada.

{:first "Sally" :last "Brown"}
{:a 1 :b "two"}
{}

Criação

assoc e dissoc são funções complementares: elas associam e desassociam itens em um mapa. Veja como colocamos o sobrenome “Brown” no mapa com assoc, e depois o removemos com dissoc. merge mistura dois mapas para formar um novo mapa com todos os seus elementos juntos.

(assoc {:first "Sally"} :last "Brown")
;=> {:first "Sally", :last "Brown"}

(dissoc {:first "Sally" :last "Brown"} :last)
;=> {:first "Sally"}

(merge {:first "Sally"} {:last "Brown"})
;=> {:first "Sally", :last "Brown"}

Extração 1

count, toda coleção tem essa função. Por que você acha que a resposta é dois? count nos devolve o número de associações.

Como mapas são pares de chave-valor, a chave é usada para pegar o valor de um mapa. Um dos jeitos mais usados em Clojure são os exemplos abaixo. Podemos usar uma keyword como se fosse uma função para buscar os valores dentro do mapa. No último exemplo, passamos a chave :INEXISTENTE como último argumento. Isso funciona para quando a chave que procuramos não existe no mapa.

(count {:first "Sally" :last "Brown"})
;=> 2

(get {:first "Sally" :last "Brown"} :first)
;=> "Sally"
(get {:first "Sally"} :last)
;=> nil


(get {:first "Sally"} :last :INEXISTENTE)
;=> :INEXISTENTE

Extração 2

Também temos keys e vals, que são bem simples: retornam as chaves e os valores do mapa. A ordem não é garantida, então poderiamos ter como resultado (:first :last) ou (:last :first)

(keys {:first "Sally" :last "Brown"})
;=> (:first :last)

(vals {:first "Sally" :last "Brown"})
;=> ("Sally" "Brown")

Atualização

Após a criação, queremos salvar um novo valor associado à chave. A função assoc pode ser usada para associar um novo valor a uma chave já existente. Também existe uma função útil chamada update. Esta função recebe o mapa e a chave com uma função. O valor da chave especificada vai ser o primeiro argumento da função passada. A função update-in funciona como a update, mas recebe um vetor de chaves para atualizar um caminho dentro de um mapa aninhado.

(def hello {:count 1 :words "hello"})

(update hello :count inc)
;=> {:count 2, :words "hello"}
(update hello :words str ", world")
;=> {:count 1, :words "hello, world"}


(def mine {:pet {:age 5 :name "able"}})

(update-in mine [:pet :age] - 3)
;=> {:pet {:age 2, :name "able"}}

Voltar ao primeiro slide, ou ir para o índice do currículo.