I've been working on the exercises from Dave Thomas's Programming in Elixir. I came across one that says to write a function (called caesar
for some reason) that takes a charlist and an integer to add to each element in the charlist, cycling back around to 'a' if it goes past 'z', so you should be able to call it like
MyList.caesar('ryvke', 13)
and it should return a string.
I have a function that maps over the list and performs the addition, but it returns a charlist, and I can't figure out how to convert it to a string:
defmodule MyList do
def caesar(list, n) do
Enum.map list, &(perform_addition(&1, n))
|> to_charlist
|> to_string
end
defp perform_addition(char_val, n) when char_val < 122 do
char_val + n
end
defp perform_addition(_, n) do
97 + n
end
end
I've tried:
The attempt based on the last bullet led to:
MyList.caesar('ryvke', 13)
# => <<127, 194, 134, 194, 131, 120, 114>>
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.
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With