Ao fazer recursão sobre enumeráveis (listas, bitstrings, strings), muitas vezes há duas preocupações:
Para lidar com essas preocupações, pode-se usar um acumulador.
Um acumulador é uma variável que é passada junto, além dos dados. Ele serve para passar o estado atual da execução da função, de chamada em chamada, até chegar ao caso base. No caso base, o acumulador é usado para retornar o valor final da chamada recursiva.
Os acumuladores devem ser inicializados por quem escreve a função, não por quem a usa. Para isso, declare duas funções: uma função pública que recebe apenas os dados necessários como argumentos e inicializa o acumulador, e uma função privada que também recebe um acumulador. No Elixir, é um padrão comum prefixar o nome da função privada com do_.
# Count the length of a list without an accumulator
def count([]), do: 0
def count([_head | tail]), do: 1 + count(tail)
# Count the length of a list with an accumulator
def count(list), do: do_count(list, 0)
defp do_count([], count), do: count
defp do_count([_head | tail], count), do: do_count(tail, count + 1)
O uso de um acumulador permite transformar funções recursivas em funções recursivas de cauda. Uma função é recursiva de cauda se a última coisa executada por ela é uma chamada a si mesma.
No seu laboratório de pesquisa de DNA, você vem trabalhando em várias formas de comprimir seus dados de pesquisa para economizar espaço de armazenamento. Uma pessoa da equipe sugere converter os dados de DNA para uma representação binária:
| Ácido nucleico | Código |
|---|---|
| um espaço | 0000 |
| A | 0001 |
| C | 0010 |
| G | 0100 |
| T | 1000 |
Você reflete sobre isso, já que pode reduzir pela metade o custo de armazenamento necessário, mas às custas da legibilidade para humanos. Você decide escrever um módulo para codificar e decodificar seus dados e medir a economia.
Implemente encode_nucleotide/1 para receber o code point do ácido nucleico e retornar o valor inteiro do código codificado.
DNA.encode_nucleotide(?A)
# => 1
# (which is equal to 0b0001)
Implemente decode_nucleotide/1 para receber o valor inteiro do código codificado e retornar o code point do ácido nucleico.
DNA.decode_nucleotide(0b0001)
# => 65
# (which is equal to ?A)
Implemente encode/1 para receber uma charlist que representa ácidos nucleicos e lacunas e retornar uma bitstring com os dados codificados.
DNA.encode(~c"AC GT")
# => <<18, 4, 8::size(4)>>
Implemente decode/1 para receber uma bitstring que representa ácidos nucleicos e lacunas e retornar os dados decodificados como uma charlist.
DNA.decode(<<132, 2, 1::size(4)>>)
# => ~c"TG CA"
Crie sua conta no Exercism para aprender e dominar Elixir com 58 conceitos168 exercícios e mentoria humana de verdade, tudo de graça.