Para imprimir isso, podemos usar o IO.inspect função para impressão bonita:


IO.inspect(book_lengths)
(17, 12, 9, 9, 21)

Tipos de coleção de Elixir

Já vimos o List digite ação. Elixir inclui estes principais tipos de coleção:

  • Lista: Uma coleção imutável, mas projetada para modificação por duplicação, homogênea de tipos arbitrários.
    • Sintaxe: suportes quadrados com itens: (x,y,z)
  • Tupla: Projetado para manter os valores principalmente, não a manipulação, as tuplas são como listas, mas voltadas para o desempenho da leitura. Pense neles um tipo de coleção de acesso a dados.
    • Sintaxe: aparelho encaracolado com itens: {x,y,z}
  • Lista de palavras -chave: Pares de valor-chave ordenados, teclas somente de string, usadas principalmente para argumentos nomeados para funções
    • Sintaxe: suportes quadrados com pares: {x: x1,y: y1,z: z1}
  • Mapas: Os pares familiares de valor-chave, onde as chaves podem ser qualquer coisa e a coleção não é ordenada.
    • Sintaxe: Porcentagem de aparelho encaracolado com pares:
      • %{x => x1, y => y1, z => z1}
      • %{x: x1, y: y1, z: z1}

Mapas e átomos

Os mapas têm dois estilos de declaração, e o que usa depende se as teclas são átomos. Um átomo é uma variável cujo valor é o mesmo que seu nome, uma espécie de super constante. Um átomo é declarado com um cólon seguido por um literal.

Poderíamos criar um mapa de teclas de string para valores inteiros como assim:


books_and_lengths = %{ "The Bhagavad Gita" => 17, "Tao Te Ching" => 12 }

A seguir, é diferente e cria um mapa de átomos para inteiros, provavelmente não é o que queremos neste caso:


books_and_lengths = %{ "The Bhagavad Gita": 17, "Tao Te Ching": 12 }

Observe a colocação do cólon. Em um Mapo cólon está diretamente ao lado do Kay indica que é um átomo, e os átomos podem ser fechados (para apoiar os caracteres ilegais).

A linha inferior é usar a sintaxe de seta (=>) quando você deseja uma variável normal e a chave e o cólon (:) quando você quer átomos.

Normalmente, os átomos são declarados assim:


:my_atom

Aqui está outra maneira de declarar um mapa com as teclas Atom:


my_map = %{:atom1 => “foo”, :atom2 => “bar”}

Módulos

O ELIXIR suporta módulos, que são espaços de nome que reúnem funções relacionadas. Isso faz não Mantenha o estado ou variáveis ​​como uma classe ou bloco de código. Como seria de esperar, você pode chamar outras funções de dentro do mesmo módulo, mas aqueles que chamam de fora precisam preceder as chamadas ou importar o módulo.

Aqui está um módulo simples:


defmodule BookFunctions do
  def myFunc
  end
end

BookFunctions.myFunc()

Correspondência de padrões

Os sabores sintáticos e as características da biblioteca padrão abrangem um longo caminho para compensar a sensação geral de usar um idioma. Eles são os recursos comuns com os quais você interage o tempo todo. Mas todo idioma tem alguns recursos que se destacam.

A correspondência de padrões funcionais é um recurso sofisticado e encantador que o Elixir traz para a tabela, permitindo que você execute a execução da função condicional em uma sintaxe do tipo Switch. Digamos que queremos gerar pequenos, médios ou longos com base nos comprimentos do título do livro:


defmodule BookFunctions do
  def categorize_length(length) do
    case length do
      length when length  "Short"
      length when length &lt= 20 -> "Medium"
      _ -> "Long"
    end
  end

  def print_categories(lengths) do
    Enum.each(lengths, fn length ->
      category = categorize_length(length)
      IO.puts("#{length} characters: #{category}")
    end)
  end
end

Algumas anotações:

  • BookFunctions é um módulo que você viu.
  • Em Elixir, as declarações de retorno estão implícitas, de modo que o categorize_length() A função retorna automaticamente qualquer que seja o resultado da última expressão.

O case palavra-chave é o que cria o bloco de correspondência de padrões, no categorize_length função. O length when length Sintaxe (tecnicamente, uma cláusula de guarda) permite fazer uma verificação de alcance na variável de comprimento e, se atender aos critérios, o -> O operador nos diz o que retornar do caso. (Como esta é a declaração final da função, também será o valor de retorno funcional.)

Poderíamos usar essa nova função em nosso book_lengths Assim:


BookBookFunctions.print_categories(book_lengths)
17 characters: Medium
12 characters: Medium
9 characters: Short
9 characters: Short
21 characters: Long

Enum.each é análogo a forEach Em outros idiomas, como o JavaScript, nos deixando executar uma operação em cada elemento de uma coleção.

Looping

Elixir não tem para e enquanto loops. Isso pode ser um pouco chocante no começo, mas está alinhado com a imutabilidade favorecida pela filosofia funcional. Em essência, o Elixir não quer que você faça mutação durante os loops e, em vez disso, deseja que você use a recursão. A recursão o mantém no campo das funções e, idealmente, você deseja usar funções puras (significado, funções sem efeitos colaterais).

Grande parte do loop que você precisa fazer pode ser tratada com operações funcionais como Enum.each e Enum.map. O fórum Elixir tem uma boa e extensa discussão sobre looping e alternativas.

Compreensões

Uma das maneiras mais diretas de simular um loop é com compreensões, que você seria perdoado por confundir um loop real:


for x 

Veja o Elixir Docs para saber mais sobre compreensões e como eles simplificam as operações de coleta.

Operador de tubo

O operador do tubo fornece uma sintaxe limpa para os resultados da função de encadeamento juntos. Pense nisso como uma forma mais elegante de funções de nidificação. Aqui está um exemplo simples do operador de tubo em nosso books_and_lengths coleção:


books_and_lengths
  |> Map.keys() 
  |> Enum.map(&String.upcase/1) 
  |> Enum.join(", ") 
  |> IO.puts() 

A saída é:


The Bhagavad Gita, Tao Te Ching

Simultaneidade

Embora a simultaneidade seja um tópico complexo, é uma das áreas de força de Elixir, então vamos dar uma olhada rápida. Elixir usa atores, algo como threads virtuais, pois não são processos completos do sistema operacional. Os atores suportam a transferência de mensagens para uma comunicação simultânea simplificada.

O exemplo a seguir, demonstrando o manuseio de mensagens, é do Elixir Docs:


defmodule Example do
  def listen do
    receive do
      {:ok, "hello"} -> IO.puts("World")
    end

    listen()
  end
end

Observe que o listen A função é recursiva (se chama no final), o que permite lidar com várias mensagens. Sem a recursão, o processo sairia.

Para lançar este ator, usamos spawn:


pid = spawn(Example, :listen, ())

Então podemos enviar uma mensagem do processo principal, usando o pid Salvamos:


send pid, {:ok, "hello"}

Isso produz “mundo” para o console.

Conclusão

Os idiomas são definidos em grande parte pelo que facilitam e o que dificulta. O Elixir é claramente dedicado a facilitar a permanecer um programador na mentalidade de programação funcional e mais difícil de se desviar em mutações e efeitos colaterais.

O efeito geral é que você tende a escrever um bom código de programação funcional, desde que trabalhe com o idioma e não o combate. Não é difícil ver por que Elixir capturou tanto interesse, trazendo o legado de Erlang para o mundo moderno. É uma linguagem de programação com fortes idéias sobre como fazer as coisas e uma comunidade ativa e entusiasta.