Ответ 1
Это вызвано следующим кодом (как вы определили):
def anagram?(reference, [head | tail]) when head in reference do
anagram?(reference - head, tail)
end
Вы можете найти определение макроса in
в исходном коде, но я скопировал его здесь для удобства - он также содержит в документации:
гвардейской
Оператор
in
может использоваться в сторожевых предложениях как долго поскольку правая часть - это диапазон или список. В таких случаях эликсир будет расширять оператор до действительного защитного выражения. Например:when x in [1, 2, 3]
переводит на:
when x === 1 or x === 2 or x === 3
Код, определяющий макрос:
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
Ваш код блокирует последнюю часть в аргументе case, потому что во время компиляции не может быть гарантировано, что ваша переменная reference
имеет тип list
(или range
.)
Вы можете увидеть, какое значение передается макросу, вызывая:
iex(2)> quote do: head in reference
{:in, [context: Elixir, import: Kernel],
[{:head, [], Elixir}, {:reference, [], Elixir}]}
Здесь атом :reference
передается макросу in
, который не соответствует ни одному из предыдущих предложений, поэтому он попадает в предложение _
(что вызывает ошибку).
Чтобы исправить это, вам нужно объединить два последних предложения в одну функцию:
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
Также стоит отметить, что вы, вероятно, хотите использовать "strings"
вместо 'char_lists'
http://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#char-lists
Другое дело, что вызов reference - head
не будет работать (он поднимет ArithmeticError
). Вы можете посмотреть List.delete/2, чтобы удалить элемент из списка.