Strings
Por padrão, uma string em Elixir sempre utilizará aspas duplas.
Por exemplo:
iex(1)> "mateus"
"mateus"
Criando interpolação de strings:
iex(7)> x = "martins"
"martins"
iex(8)> "mateus #{x}"
"mateus martins"
O que são esses /1, /2 que encontramos em algumas funções dentro das documentações?
São chamadas de Aridade da função.
E o que são?
Aridade nada mais é do que quantas argumentos a função recebe.
Por exemplo:
A função length/1 (do modulo String) só recebe um argumento.
E qual argumento é esse?
A string que eu quero saber o cumprimento:
iex(1)> String.length("mateus")
>6
Neste caso, a função length
está passando um único argumento, e me retornando o número de letras dentro dessa função.
Como obter as funções do módulo String:
String.
Assim, obtemos todas as funções do módulo String.
E como sabemos o que cada função faz?
Para isso, podemos usar o h(helper) antes de passar as funções:
H String.slice
iex(2)> h String.slice
## Examples
iex> String.slice("elixir", 1..3)
"lix"
iex> String.slice("elixir", 1..10)
"lixir"
iex> String.slice("elixir", -4..-1)
"ixir"
iex> String.slice("elixir", -4..6)
"ixir"
For ranges where start > stop, you need to explicit mark them as increasing:
iex> String.slice("elixir", 2..-1//1)
"ixir"
iex> String.slice("elixir", 1..-2//1)
"lixi"
If values are out of bounds, it returns an empty string:
iex> String.slice("elixir", 10..3)
""
iex> String.slice("elixir", -10..-7)
""
iex> String.slice("a", 0..1500)
"a"
iex> String.slice("a", 1..1500)
""
def slice(string, start, length)
@spec slice(t(), integer(), non_neg_integer()) :: grapheme()
## Examples
iex> String.slice("elixir", 1, 3)
"lix"
iex> String.slice("elixir", 1, 10)
"lixir"
iex> String.slice("elixir", 10, 3)
""
iex> String.slice("elixir", -4, 4)
"ixir"
Agora na prática:
iex(4)> String.slice("MATEUSMARTINS", 2, 5)
"TEUSM"
Outras formas de realiza-lar essa alteração:
iex(7)> x = "mateus"
"mateus"
iex(8)> String.downcase(x)
"mateus"
Atoms
O que são atoms em Elixir?
São constantes onde o valor da constante é o próprio nome.
Exemplos:
iex(10)> :error
:error
iex(11)> :ok
:ok
iex(12)> :mateus
:mateus
Listas
É possível passar uma lista com vários “tipos” diferentes:
[1, 2, 3, 4.5, “string”]
Exemplos de listas somadas:
iex(14)> [1, 2, 3] ++ [4, 5, 6]
[1, 2, 3, 4, 5, 6]
iex(15)> hd([1,2,3])
1
iex(3)> tl([1,2,3])
[2, 3]
Alguns outros exemplos:
iex(17)> x = [1, 2, 3] ++ [4]
[1, 2, 3, 4]
iex(18)> x
[1, 2, 3, 4]
Tuplas
Ao invés de se utilizar colchetes, utilizamos chaves:
iex(4)> {1,2,3}
{1, 2, 3}
Qual a principal diferença entre Tuplas e Listas?
A tupla é armazenada quantiguamente na memória, ou seja, em endereços de memória um após o outro, seguintes.
Por exemplo:
iex(5)> x = {1, 2, 3, 4, 5, 6, 7}
{1, 2, 3, 4, 5, 6, 7}
iex(6)> elem(x, 4)
5
Também podemos armazenar valores diferentes:
iex(7)> put_elem(x, 4, "banana")
{1, 2, 3, 4, "banana", 6, 7}
Geralmente uma tupla tem 2 elementos apenas:
iex(9)> {:ok, "meu arquivo"}
{:ok, "meu arquivo"}
Maps
São utilizados para quando precisamos armazenar chave e valor em uma estrutura de dados por acesso direto por chave.
Como criamos um mapa?
iex(1)> %{a: 1, b: 2, c: 3}
%{a: 1, b: 2, c: 3}
Usando strings:
iex(3)> %{"a" => 1, "b" => 2, "c" => 3}
%{"a" => 1, "b" => 2, "c" => 3}
Qual a diferença na hora de criarmos um map?
Atribuindo dentro de uma variável:
meu_map = %{a: 1, b: 2, c: 3}
%{a: 1, b: 2, c: 3}
iex(5)> meu_map.a
1
Porém, o mesmo não acontece com Strings.
Maps também podem ter valores mesclados.
Alguns meios de uso:
iex(18)> meu_map
%{a: 1, b: 2, c: 3}
iex(19)> Map.put(meu_map, "d", 5.5)
%{:a => 1, :b => 2, :c => 3, "d" => 5.5}
Como podemos alterar um valor dentro de um map?
Neste exemplo criamos um map:
iex(6)> x = %{c: 5, d: 6, e: "sal", f: 5.5}
%{c: 5, d: 6, e: "sal", f: 5.5}
Porém, gostaria de mudar o valor de “D”. Como podemos fazer isso?
Basta passar o map da seguinte maneira:
ex(7)> %{x | d: 10}
%{c: 5, d: 10, e: "sal", f: 5.5}
Não é possível adicionar um valor, apenas alterar um já existente.
Um caso bem comum é na criação de usuários:
iex(8)> users = [%{name: "Mateus", age: 22}, %{name: "Lucas", age: 25}]
[%{age: 22, name: "Mateus"}, %{age: 25, name: "Lucas"}]
Pattern Matching
Exemplos de Pattern Matching com mapas:
ex(1)> %{a: 1, b: 2, c: 3, d: 4}
%{a: 1, b: 2, c: 3, d: 4}
iex(2)> %{c: valor} = %{a: 1, b: 2, c: 3, d: 4}
%{a: 1, b: 2, c: 3, d: 4}
iex(3)> valor
3
Exemplos com tuplas:
iex(4)> {:ok, result} = {:ok, 14}
{:ok, 14}
iex(5)> result
14
Criando uma função anonima: (sintaxe de uma função anonima: fn)
iex(1)> multiply = fn a, b -> a * b end
#Function<43.65746770/2 in :erl_eval.expr/5>
Agora, com a função criada, executamos ela dessa maneira:
iex(2)> multiply.(2,3)
6
Outro exemplo de função anonima:
iex(3)> read_file = fn
...(3)> {:ok, result} -> "Sucess #{result}"
...(3)> {:error, reason} -> "Error #{reason}"
...(3)> end
#Function<44.65746770/1 in :erl_eval.expr/5>
Usando a função que foi criada:
iex(4)> read_file.(File.read("test.txt"))
"Error enoent"
*Agora que aprendemos algumas funções básicas, vamos ao nosso primeiro projeto Elixir! *
Para criarmos um projeto em Elixir, basta realizar-mos o seguinte comando dentro do terminal:
mateusmartins@Mateuss-MacBook-Pro ~ % mix new name_project
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/name_project.ex
* creating test
* creating test/test_helper.exs
* creating test/name_project_test.exs
Com o projeto criado, podemos abri-lo desta maneira no próprio terminal:
cd name_projct/
code .
(No meu caso, usei palace_list)
Com isso, conseguiremos ver a estrutura básica de um projeto.
Algumas dicas do pacote mix:
Mix test = roda os testes da aplicação
Mix compile = compila a aplicação
Mix format = formata o código automaticamente
Pra rodar o iex
dentro do seu projeto basta rodar o comando dentro do terminal:
iex -S mix
Quando fazemos isso ele automaticamente compila o nosso código e carrega todo o nosso código no iex.
E como chamamos uma função específica de dentro de um módulo?
Para isso, chamamos o nome do módulo (no meu caso, PalaceList) e o nome da função (por exemplo: hello)
Ficando assim:
iex(2)> PalaceList.hello
:world
Criando nossa primeira função dentro do projeto
Def (pra definir uma função), entre parênteses quais argumentos essa função recebe:
def sum([], acc) do
0
end
Depois criamos uma mesma função para definirmos o head, com o mesmo nome:
def sum([head | tail], acc) do
head
end
E quando rodamos o recompile e chamamos a função dentro do terminal, obtemos este resultado:
iex(7)> PalaceList.sum([1,2,3], 0)
1
Podemos atribuir o retorno do Match a uma variável:
def sum([head | tail] =list, acc) do
list
end
iex(10)> PalaceList.sum([1,2,3], 0)
[1, 2, 3]
E agora estamos imprimindo a lista inteira.
iex(8)> PalaceList.sum([1,2,3], 0)
6
iex(9)> PalaceList.sum([1,2,4], 0)
7
Porém, ainda temos um bug que se eu chamar a lista com o “5” no final, ele retorna 5.
Para consertamos isso, ao invés de executarmos essa função direto, tornaremos essas funções PRIVADAS, ou seja, não permitiremos que o usuário execute elas, elas apenas são funções pra gente utilizar ela dentro do nosso módulo.
Para deixar-mos uma função privada alteramos o def para defp
E agora criaremos uma função chamada “call”, que irá chamar a função do módulo que é somar.
Criamos ela dessa maneira: def call(lista) (aqui ela recebe uma lista qualquer) , do: sum(List, 0) (e ela sempre irá chamar a nossa função privada SUM com a lista que recebemos e o acumulador 0)
Def call(lista), do: sum(list, 0)
Agora, quando executamos no terminal, quando chamamos uma lista vazia obtemos o “0”
iex(12)> PalaceList.call([])
0
E quando chamamos uma lista obtemos a soma:
iex(13)> PalaceList.call([1,2,3])
6
Testando nossa aplicação
Dentro do nosso projeto, abriremos a pasta “test” e acessaremos o arquivo “palace_list_test.exs”
Dentro do nosso teste, podemos deletar o “doctest PalaceList”, pois ele executa todo o código que temos na documentação e ve se o código executa corretamente.
Como deletemos a documentação, não iremos utilizar o doctest.
Sempre teremos os testes com os nomes “exs”, enquanto os arquivos “comuns” serão “ex”.
Temos dentro do nosso arquivo de test um “use ExUnit.Case”. Mas o que é e para que serve?
Estamos trazendo todas as macros que temos pra usarmos no nosso test, trazendo todas as funcionalidades do ExUnit para dentro do módulo.
Todo test começaremos com “describe” acompanhado do nome da função que queremos testar junto da sua aridade.
Vale lembrar que sempre fazemos de funções PÚBLICAS.
No nosso caso, ficaria assim: describe “call/1” do
End
Usamos a “/“ para dizer qual é a aridade.
Poderíamos ter vários parâmetros, então dessa forma especificamos qual deles estamos testando.
Agora, iremos descrever o nosso test junto de uma descrição:
Test “return the list sum""
Agora, criaremos um setup para o nosso test:
List = [1, 2, 3]
E criaremos uma nova variável que irá armazenar a resposta
Chamaremos o nosso módulo “PalaceList” com a função “Call”
Não precisamos importar o módulo pois já temos acesso dentro do nosso test.
response = PalaceList.call(list)
Agora, criaremos uma resposta esperada:
expect_response = “mateus”
E, para executarmos o nosso test, utilizamos o assert.
O assert irá verificar se um valor é igual ao outro.
Faremos um primeiro test com valores diferentes para ele nos mostrar um erro.
No terminal, fora do iex, rodaremos o comando “mix test”, e ele irá nos retornar a seguinte mensagem de erro:
== Compilation error in file test/palace_list_test.exs ==
** (ExUnit.AssertionError)
Assertion with == failed
code: assert response == expect_response
left: 6
right: "mateus"
Tínhamos o valor 6 de um lado, e recebemos “mateus”.
Agora, vamos voltar ao módulo de test e levar o resultado corretamente substituindo o nome pelo valor “6”.
Rodamos o test novamente e obtemos o resultado:
mix test
.
Finished in 0.01 seconds (0.00s async, 0.01s sync)
1 test, 0 failures
Podemos realizar o teste de outra maneira também:
assert PalaceList.call([1, 2, 3]) == 6
Enum
Uma breve introdução ao módulo Enum
Normalmente, trabalhamos com o módulo Enum utilizando Listas, Maps ou Ranges.
Mudamos a nossa função para “def call_enum(list), do: Enum.sum(list)”, e dentro do terminal chamamos ela desta maneira:
iex(3)> PalaceList.call_enum([1, 2, 3, 4])
10
Veja que o próprio Enum já possui uma função para somar todos os elementos.
Também podemos realizar um teste com valores mínimos ou máximos de uma lista, utilizando o próprio enum.
Desta forma, a função ficaria assim:
def call_enum(list), do: Enum.min(list)
E o retorno de uma lista iex(5)> PalaceList.call_enum([1, 2, 3, 4]) seria “1”
Ou podemos transformar em valor máximo:
def call_enum(list), do: Enum.max(list)
E o maior valor da nossa lista seria o “4”.
E outra função muito utilizada é o Map
def call_enum(list), do: Enum.map(list, fn elem -> elem + 1 end)
Assim, criamos uma função dentro do map que irá cada valor dentro dessa lista:
iex(12)> PalaceList.call_enum([1, 2, 3, 4])
[2, 3, 4, 5]
Por hoje é isso pessoal!
Nos vemos no próximo capítulo "Introdução ao Elixir - módulo 2""
Top comments (0)