Convert list of codepoints (or binary) to string

The question as stated in the book is:

An Elixir single-quoted string is actually a list of individual character codes. Write a caesar(list, n) function that adds n to each list element, wrapping if the addition results in a character greater than z.

The answer above is almost surely the way you would do this in "real life", but the point of the exercise in the book is to do it with recursion, since it's in the chapter on recursion.

defmodule MyList do
  def caesar([], _n), do: []

  def caesar([head | tail], n) when head + n > ?z do
    [head + n - ?z + (?a - 1) | caesar(tail, n)]
  end

  def caesar([head | tail], n) do
    [head + n | caesar(tail, n)]
  end
end

This goes through the list and uses pattern matching to either add 13 to the characters ord value, or to wrap it back around within the lowercase ASCII area.

Admittedly, the question is not that clear about what "wrap" means (like wrap to 0 or what?) so you kinda have to intuit what the expected answer is before you know exactly what to do. (If you wrap to 0 you wind up with ^E being the first character, which is a pretty big hint.)


To answer the question in the title: you're looking for List.to_string/1:

iex(1)> List.to_string([97, 98, 99])
"abc"

The reason you're not getting a readable string back for those arguments is that your logic to rotate the value is incorrect. Here's how you can shift a lower case letter and rotate it back to a if it crosses z while not touching non lower case letters:

# ?a == 97, ?z == 122
defp add(ch, n) when ch in ?a..?z do
  rem((ch - ?a + n), 26) + ?a
end
defp add(ch, n) when ch in ?A..?Z do
  rem(ch - ?A + shift, 26) + ?A
end
defp add(ch, _), do: ch

With this, you just need to map the function over the input charlist and then call List.to_string/1:

def caesar(list, n) do
  list |> Enum.map(&add(&1, n)) |> List.to_string
end
iex(1)> MyList.caesar('ryvke', 13)
"elixr"

(called caesar for some reason)

This algorithm is known as the Caesar Cipher.

Tags:

Elixir