small medium large xlarge

Dave_gnome_head_isolated_pragsmall
16 Jul 2013, 02:25
Dave Thomas (370 posts)
  • An Elixir single quoted string is actually a list of individual character codes. Write a function caesar(list, n) that adds n to each element of the list, but wrapping if the addition results in a character greater than z.

    iex> MyList.caesar('ryvkve', 13)
    ?????? :)
    

A Possible Solution</summary>

defmodule MyList do

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

  def caesar([ head | tail ], n)
    when head+n <= ?z,
    do: [ head+n | caesar(tail, n) ]

  def caesar([ head | tail ], n), 
    do: [ head+n-26 | caesar(tail, n) ]

end

IO.puts MyList.caesar('ryvkve', 13)  #=> elixir

</details>

Generic-user-small
03 Aug 2013, 03:27
Luke Imhoff (6 posts)

Did I miss the ?character => integer somewhere earlier in the book? I ended up going with this since I knew only that single quotes were strings, and that single elements would give the integer:


defmodule MyList do
  @caesar_start hd('a')
  @caesar_end hd('z')
  @caesar_width (@caesar_end - @caesar_start + 1)

  def caesar([], _rotation) do
    []
  end
  def caesar([character | tail], rotation) do
    offset = character - @caesar_start
    rotated_offset = rem(offset + rotation, @caesar_width)
    rotated_character = @caesar_start + rotated_offset

    [rotated_character | caesar(tail, rotation)]
  end
end ---

I’m not sure pre-computing the start and end and width in attributes is faster or the Erlang VM would make the same lift out of the function. I do think this is better than hardcoding the 26 width.

Twitter-avatar-orangeblau_pragsmall
23 Aug 2013, 13:17
Rin Raeuber (1 post)

Alternative without hardcoded width:

defmodule MyList do

  def caesar([], n), do: []

  def caesar([head | tail], n // 0) do
    [_caesar(head, n) | caesar(tail, n) ]
  end

  defp _caesar(char, n) do
    width = ?z - ?a + 1
    ?a + rem(char + n - width, width)
  end

end

Feedback welcome.

Ac_simpsonized_pragsmall
24 Sep 2013, 03:54
Antonio Carlos da Graça Mota Durão de Souza (5 posts)

I used fixed character values.

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

  def caesar([head | tail], salt)
    when head + salt > 122  do
    [96 + (head + salt - 122) | caesar(tail, salt)]
  end

  def caesar([head | tail], salt)
    when head + salt <= 122  do
    [head + salt | caesar(tail, salt)]
  end
end
Milmazz3_pragsmall
29 Sep 2013, 04:52
Milton Mazzarri (1 post)

I think that we can express an alternative solution as result = rem (letter + shift, 26), but first, we need to assume that A=0..Z=25

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

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

  defp do_caesar(base, n) do
    ?a + rem(base - ?a + n, ?z - ?a + 1)
  end
end
Mac 128k logic board 96x96_pragsmall
19 Oct 2013, 15:00
Roger Turner (7 posts)

defmodule MyList do

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

  def caesar([ head | tail ], n) when head in ?a..?z do
	_caesar(head, n, ?a, tail)
  end

  def caesar([ head | tail ], n) when head in ?A..?Z do
	_caesar(head, n, ?A, tail)
  end

  defp _caesar(alpha, n, first, tail) do 
	[ rem(alpha-first + n, 26) + first  | caesar(tail, n) ]
  end

end

N609929290_306703_2458_pragsmall
26 Dec 2013, 17:02
Tanja Pislar (7 posts)
defmodule MyList do

  def caesar(list, n) do
     map list, &(if (&1 + n) > 122, do: &1 + n - 26, else: &1 + n)
  end

end
Generic-user-small
30 Dec 2013, 01:26
Brynjar Smári Bjarnason (1 post)

Quite simple map

def caesar(coll,n) do
Enum.map(coll, &(rem( &1-?a+n, (?z-?a+1) + ?a) ) end

Generic-user-small
27 Jan 2014, 00:32
Alan Gardner (6 posts)

I ended up with the following because I wasn’t aware of ?z :)

def wrap(char), do: 97 + rem(char-97, 26)
def caesar([], _), do: []
def caesar([head|tail], n), do: [wrap(head+n), caesar(tail, n)]
Nathan-6kites_pragsmall
18 Jan 2016, 00:53
Nathan Feaver (9 posts)

I could use only one map function if I worked this out a little more. I like this solution because I let Enum.map handle the head and tail recursion logic.

defmodule MyList do
  def ceasar(list, n) do
    list
      |> Enum.map(&(&1 + n))
      |> Enum.map(&_char_wrap/1)
  end
  defp _char_wrap(n)
    when n > ?z,
    do: n - 26
  defp _char_wrap(n), do: n
end

UPDATE:

I believe module attributes were introduced to Elixir or to the book since the last time I was here. This time, it was cool that I could use head/tail recursion rather than using Enum.

defmodule MyList do
  @a List.first('a')
  @z List.first('z')

  # adds n to each list element, wrapping if the addition
  # results in a character greater than z
  def caesar([], _n), do: []
  def caesar([head | tail], n) do
    [wrap(head + n) | caesar(tail, n)]
  end

  defp wrap(n) when n > @z do
    n - @z + @a - 1
  end

  defp wrap(n) do
    n
  end
end
Generic-user-small
26 Mar 2014, 10:45
Maximilian Schulz (1 post)

Well, I wasn’t sure about it upfront, but the matching magic seemed like the thing I was looking for. And Elixir seems to be able to compare single element lists pretty well:

  def caesar([], n) do
    []
  end

  def caesar([head | tail], n) when [head + n] > 'z' do
    [head + n - 26 | caesar(tail, n)]
  end

  def caesar([head | tail], n) do
    [head + n | caesar(tail, n)]
  end
Patrick_pragsmall
09 Jul 2014, 07:27
Patrick Oscity (13 posts)

Using our map function with modular arithmetic:

defmodule MyList do
  def map([], fun), do: []
  def map([head | tail], fun), do: [fun.(head) | map(tail, fun)]

  def caesar(string, rot \\ 13) do
    map string, fn char ->
      rem(char - 97 + rot, 26) + 97
    end
  end
end
Generic-user-small
25 Dec 2014, 02:36
Pierre Sugar (56 posts)
defmodule MyList do
  def caesar([], _offset), do: []
  def caesar([head|tail], offs), do: [wrap(head + offs) | caesar(tail, offs)]

  def wrap(value) when value < 32,  do: 122 - rem(32, value)
  def wrap(value) when value > 122, do:  32 + rem(value, 122)
  def wrap(value),                  do: value
end

I use all printable values, so to get Elixir with an offset of 13 the invokation looks like that

IO.puts MyList.caesar('8_\\k\\e', 13) # -> Elixir
Generic-user-small
25 Mar 2015, 04:31
Scott Smith (8 posts)

Interesting problem scope and implementation strategies.

Mine uses module constants; otherwise a mixture of what I see above:

defmodule MyList do
  @wrap ?z + 1 - ?a
  def caesar([], _n), do: []
  def caesar([head | tail], n) when (head + n) <= ?z, do: _recurse(head, tail, n, 0)
  def caesar([head | tail], n)                      , do: _recurse(head, tail, n, @wrap)
  defp _recurse(head, tail, n, offset) do
    [ head + n - offset | caesar(tail, n) ]
  end
end
Generic-user-small
25 Apr 2015, 13:19
Matt Schreck (2 posts)

This is more general, a lot of the above will fail if you give a rotator greater than 26, which could be considered a bug. Also, I think this is quite readable (could be because I wrote it though).

The money shot basically says, “subtract ‘a’s value from the letter we’re looking at, add the rotator, take the remainder of all that when divided by 26, then add ‘a’s value back on”

defmodule MyList do
  def caesar(list,rot), do: _caesar(list, rem(rot,26))
  defp _caesar([],_), do: []
  defp _caesar([head|tail],rot) do
    [ rem( head - ?a + rot, 26) + ?a | caesar(tail,rot)]
  end
end

Edit: Seems Milton Mazzarri above had the same approach. Good on you!

Generic-user-small
27 Apr 2015, 12:41
Francois Roucoux (2 posts)

Using a helper function returning a function:

defmodule MyList do
  defp add_char(n) do
    fn
      c when (c + n > 123) -> c + n - 26
      c -> c + n
    end
  end 
  def caesar(str, n), do: Enum.map(str, add_char(n))
end
20160310-gunnar_pragsmall
25 Jul 2015, 07:14
Felipe Juarez Murillo (8 posts)

I ended up with concatenation and in the comments I saw that, if you surround with [ ] you ended up with the same result

defmodule MyList do
  def caesar([], _),            do: []
  def caesar([head | tail], n) when head+n > 122, do: [head + n - 26] ++ caesar(tail, n)
  def caesar([head | tail], n), do: [head + n] ++ caesar(tail, n)
end
Generic-user-small
26 Jul 2015, 14:47
Yevhenii Kurtov (1 post)

What’s the point of substracting 26?

4c5c2c297ed9f4664cfbe7733a011fb2_pragsmall
22 Aug 2015, 12:33
Artem Medeusheyev (8 posts)
defmodule ListFunctions do
  def caesar([], _), do: []
  def caesar([h | t], n), do: [?a + rem(h - ?a + n, ?z - ?a + 1) | caesar(t, n)]
end
Generic-user-small
02 Sep 2015, 08:33
Andy Koch (1 post)

Add n to all letters, then remap if necessary. Handles rotators above 26

defmodule MyList do
  def caesar(s, n) do
    Enum.map(s, &(&1 + n)) |> Enum.map(&_check_range(&1))
  end

  defp _check_range(c) when c > 122, do: _check_range(c - 26)
  defp _check_range(c), do: c
end
Generic-user-small
13 Nov 2015, 18:31
asymmetric . (4 posts)

Here’s my solution:

defmodule Caesar do
  def caesar([], _count), do: ''
  def caesar([ head | tail ], count), do: [ 97 + rem(head + count - 97, 26) | caesar(tail, count) ]
end
Generic-user-small
18 Nov 2015, 07:33
Ricky Marcelin (2 posts)
defmodule MyList do
  def caesar(list, n) do
    _caesar(list, n, [])
  end

  defp _caesar([], _, list) do
    list
  end

  defp _caesar([ head | tail], n, list) when (head + n) > ?z do
    wrapping = head + (n-26)
    _caesar(tail, n, list ++ [wrapping])
  end

  defp _caesar([ head | tail], n, list) do
    letter = head + n
    _caesar(tail, n, list ++ [letter])
  end
end
iex> MyList.caesar('ryvkve', 13)
'elixir'
Avatar_pragsmall
02 Dec 2015, 02:23
Jaroslaw Zabiello (6 posts)
defmodule MyList do
  def caesar([], _n), do: []

  def caesar([head|tail], n) 
    when head+n <= ?z, 
    do: [ head+n | caesar(tail, n) ]

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

IO.puts MyList.caesar('ryvkve', 13)  #=> elixir
Generic-user-small
15 Jan 2016, 13:35
Rick Deckard (2 posts)

These two versions of MyList.caesar work with both down and up case chars, and with n in (-26..26).

Normal version :

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

	def caesar([head|tail], n) when n in -26..26 do
		new_char =  head + n
		if (head in ?a..?z and new_char < ?a) or (head in ?A..?Z and new_char < ?A),
			do: new_char = new_char + 26
		if (head in ?a..?z and new_char > ?z) or (head in ?A..?Z and new_char > ?Z),
			do: new_char = new_char - 26
		[new_char|caesar(tail, n)]
	end
end

Second version (with tail call optimization) :

defmodule MyList do
	def caesar2(text, n), do: _caesar2(text, n, '')

	defp _caesar2([], _n, new_text), do: new_text
	defp _caesar2([head|tail], n, new_text) when n in -26..26 do
		new_char = head + n
		if (head in ?a..?z and new_char < ?a) or (head in ?A..?Z and new_char < ?A),
			do: new_char = new_char + 26
		if (head in ?a..?z and new_char > ?z) or (head in ?A..?Z and new_char > ?Z),
			do: new_char = new_char - 26
		_caesar2(tail, n, new_text++[new_char])
	end
end
Generic-user-small
17 Jan 2016, 17:55
Riley White (4 posts)

I took the rem approach similar to several others above with my initial solution, and I also took advantage of the existing map function.

  def caesar(list, n) do
    map list, &(rem(&1 + n - ?a, 26) + ?a)
  end

Afterward, I took a stab at a version that encodes mixed case and passes through non-letters.

  def caesar_mixed_case(list, n) do
    encode = fn a_val, letter -> rem(letter + n - a_val, 26) + a_val end
    map list, fn
      lower when lower >= ?a and lower <= ?z -> encode.(?a, lower)
      upper when upper >= ?A and upper <= ?Z -> encode.(?A, upper)
      not_letter -> not_letter
    end
  end

The ?a syntax is nice :-)

Linyang_pragsmall
23 Jan 2016, 00:39
Lin Yang (1 post)

Some of the implementations doesn’t work with n > 26, including Dave’s.

Here’s my version:

defmodule MyList do
  def caesar([], _), do: []
  def caesar([head | tail], n) when head + n <= ?z, do: [head + n | caesar(tail, n)]
  def caesar([head | tail], n), do: caesar([head-26 | tail], n)
end
Generic-user-small
29 Jan 2016, 20:06
Arthur Granowski (7 posts)
defmodule MyList do
  def caesar([], _n) do
    []
  end

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

  #Private functions:
  defp _wrap?(char, rotate) when (char + rotate) <= 122 do
    #New char is a printable char of value <= 122 (ie 'z') .
    char + rotate
  end

  defp _wrap?(char, rotate) do
    #Rotated beyond 'z' (ie greater than 122) - need to wrap .
    char + rotate - 26
  end
end

#Calling the functions...
IO.puts MyList.caesar('ryvkve', 13)  #=> elixir

Now that I found out about ?z etc would have used that instead of 122. Also Luke Imhoff earlier on has shown a much more robust solution.

Img_2196_pragsmall
12 May 2016, 13:37
Diogo Neves (12 posts)
defmodule MyList do
  @offset ?a
  @wrap ?z - ?a + 1

  def caesar([], _), do: []
  def caesar([h|t], n) do
    [rem(h+n - @offset, @wrap) + @offset | caesar(t, n)]
  end
end

Not very readable I think but does the job. Initially I didn’t understand the wrap was to be done around the letters only and assumed offset was 0

Generic-user-small
22 May 2016, 21:43
Joe Eifert (1 post)

My Solution: I find it rather clean. :-)

def caesar([], _), do: []
def caesar([head | tail], increaser) do
  [ rem( head + increaser, List.first('z') ) | caesar(tail, increaser) ]
end
Generic-user-small
28 May 2016, 16:06
Michael Bishop (4 posts)

This might be cheating, but I used .map

defmodule MyList do
  def caesar(list,n) do
    Enum.map list, fn(item) ->
      rem(item - ?a + n, ?z-?a+1) + ?a
    end
  end
end
Generic-user-small
28 Jun 2016, 23:24
Krunoslav Husak (1 post)

This was my solution:

def caesar([], _), do: []
def caesar([head|tail], n), do: [rem(head - 97 + n, 26)+97|caesar(tail, n)]
Generic-user-small
01 Jul 2016, 16:10
Karlo Smid (10 posts)

Pipe solution

def wrap(n) when rem(n,122)!=n, do: 96+rem(n,122)
  def wrap(n), do: n
  def caesar(list,n), do: list |> map(&(&1+n)) |> map(&wrap(&1))

I could not figured out how to send wrap in second map in the pipe. I figured it out thanks to Andy Koch solution.

You must be logged in to comment