Trilhas
/
Elixir
Elixir
/
Exercícios
/
Retire uma senha
Retire uma senha

Retire uma senha

Exercício de aprendizagem

Introdução

Processos

No Elixir, todo código roda dentro de processos.

Por padrão, uma função roda no mesmo processo de onde foi chamada. Quando você precisa rodar explicitamente uma determinada função em um novo processo, use spawn/1:

spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>

spawn/1 cria um novo processo que executa a função de aridade 0 fornecida e retorna um identificador de processo (PID). O novo processo permanece vivo enquanto a função estiver em execução e depois termina silenciosamente.

Os processos do Elixir não devem ser confundidos com processos do sistema operacional. Os processos do Elixir usam muito menos memória e CPU. Não há problema nenhum em ter aplicações Elixir que rodam centenas de processos Elixir.

Mensagens

Os processos não compartilham informações diretamente uns com os outros. Os processos enviam e recebem mensagens para compartilhar dados.

Você pode enviar uma mensagem para qualquer processo com send/2. O primeiro argumento de send/2 é o PID do destinatário, o segundo argumento é a mensagem.

Uma mensagem pode ser de qualquer tipo. Muitas vezes ela é composta de átomos e tuplas. Se você quiser receber uma resposta, deve incluir o PID do remetente em algum lugar da mensagem. Você pode obter o PID do processo atual com self().

send/2 não verifica se a mensagem foi recebida pelo destinatário, nem se o destinatário ainda está vivo. A mensagem vai parar na caixa de mensagens do destinatário e só será lida se e quando o destinatário pedir explicitamente para receber mensagens.

Uma mensagem pode ser lida de uma caixa de mensagens usando a macro receive/1. Ela aceita um bloco do que pode fazer correspondência de padrões nas mensagens.

receive do
  {:ping, sender_pid} -> send(sender_pid, :pong)
  :do_nothing -> nil
end

receive/1 vai pegar uma mensagem da caixa de mensagens que corresponda a qualquer um dos padrões dados e executar a expressão dada para aquele padrão. Se não houver mensagens na caixa de mensagens, ou se nenhuma das mensagens corresponder a nenhum dos padrões, receive/1 vai esperar por uma.

Laço de recebimento

Se você quiser receber mais de uma mensagem, precisa chamar receive/1 recursivamente. É um padrão comum implementar uma função recursiva, por exemplo chamada loop, que chama receive/1, faz algo com a mensagem e depois chama a si mesma para esperar por mais mensagens. Se você precisar carregar algum estado de uma chamada de receive/1 para outra, pode fazer isso passando um argumento para essa função loop.

PIDs

Os identificadores de processo são um tipo de dados próprio. Eles funcionam como endereços de caixa de mensagens: se você tem o PID de um processo, pode enviar uma mensagem para esse processo. Os PIDs geralmente são criados indiretamente, como valor de retorno de funções que criam novos processos, como spawn.

Instruções

Você está escrevendo um sistema embarcado para uma máquina de tirar senha. É um modelo bem simples. Ela consegue entregar números consecutivos e informar qual foi o último número entregue.

1. Inicie a máquina

Implemente a função start/0. Ela deve criar um novo processo e retornar o PID do processo. O novo processo ainda não precisa fazer nada.

TakeANumber.start()
# => #PID<0.138.0>

Repare que o PID pode ser diferente a cada vez que você rodar este código.

2. Reporte o estado da máquina

Modifique a máquina para que o processo recém-criado esteja pronto para receber mensagens (inicie um laço de receive) com um estado inicial de 0. Ela deve ser capaz de receber mensagens {:report_state, sender_pid}. Como resposta a essas mensagens, ela deve enviar seu estado atual (o último número de senha entregue) para sender_pid e depois aguardar mais mensagens.

machine_pid = TakeANumber.start()

# a client sending a message to the machine
send(machine_pid, {:report_state, self()})

# a client receiving a message from the machine
receive do
  msg -> msg
end

# => 0

3. Entregue números

Modifique a máquina para que ela possa receber mensagens {:take_a_number, sender_pid}. Ela deve aumentar seu estado em 1, enviar o novo estado para sender_pid e depois aguardar mais mensagens.

machine_pid = TakeANumber.start()

# a client sending a message to the machine
send(machine_pid, {:take_a_number, self()})

# a client receiving a message from the machine
receive do
  msg -> msg
end

# => 1

4. Pare a máquina

Modifique a máquina para que ela possa receber uma mensagem :stop. Ela deve parar de aguardar mais mensagens.

5. Ignore mensagens inesperadas

Modifique a máquina para que, ao receber uma mensagem inesperada, ela a ignore e continue aguardando mais mensagens.

Editar via GitHub O link abre em uma nova janela ou aba
Elixir Exercism

Tudo pronto para começar Retire uma senha?

Crie sua conta no Exercism para aprender e dominar Elixir com 58 conceitos168 exercícios e mentoria humana de verdade, tudo de graça.