This is caused by the following code (as you identified):
def anagram?(reference, [head | tail]) when head in reference do
anagram?(reference - head, tail)
end
You can find the definition of the in
macro in the source code, but I have copied it here for convenience - it also contains the following in the documentation:
Guards
The in
operator can be used in guard clauses as long
as the right-hand side is a range or a list. In such cases, Elixir
will expand the operator to a valid guard expression. For example:
when x in [1, 2, 3]
translates to:
when x === 1 or x === 2 or x === 3
The code that defines the macro:
defmacro left in right do
in_module? = (__CALLER__.context == nil)
right = case bootstraped?(Macro) and not in_module? do
true -> Macro.expand(right, __CALLER__)
false -> right
end
case right do
_ when in_module? ->
quote do: Elixir.Enum.member?(unquote(right), unquote(left))
[] ->
false
[h|t] ->
:lists.foldr(fn x, acc ->
quote do
unquote(comp(left, x)) or unquote(acc)
end
end, comp(left, h), t)
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last]} ->
in_range(left, Macro.expand(first, __CALLER__), Macro.expand(last, __CALLER__))
_ ->
raise ArgumentError, <<"invalid args for operator in, it expects a compile time list ",
"or range on the right side when used in guard expressions, got: ",
Macro.to_string(right) :: binary>>
end
end
Your code block is hitting the last part in the case statement because it can't be guaranteed at compile time that your variable reference
is of type list
(or range
.)
You can see what value is being passed to the macro by calling:
iex(2)> quote do: head in reference
{:in, [context: Elixir, import: Kernel],
[{:head, [], Elixir}, {:reference, [], Elixir}]}
Here, the atom :reference
is being passed to the in
macro, which doesn't match any of the previous clauses so it falls through to the _
clause (which raises the error.)
To fix this, you will need to combine your last two clauses into one function:
def anagram?(reference, [head | tail]) do
case head in reference do
false ->
IO.puts 'Not an anagram, #{head} is not in the reference word.'
false
true ->
anagram?(reference - head, tail)
end
end
It is also worth noting that you probably want to use "strings"
instead of 'char_lists'
http://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#char-lists
The other thing is that calling reference - head
won't work (it will raise an ArithmeticError
). You might want to look at List.delete/2 to remove an item from a list.