pattern-matching - 如何在保护条款中使用 'in' 运算符?

标签 pattern-matching elixir anagram

我正在尝试在 Elixir 中编写一个字谜检查器。它需要 2 个单词,第一个是引用,第二个将作为第一个的可能字谜进行测试。

我正在尝试使用递归和模式匹配来编写它。我收到关于使用 in 的错误保护子句中的运算符:

(ArgumentError) invalid args for operator in, it expects a compile time list or range on the right side when used in guard expressions



我不知道该怎么做才能修复它。这是代码(错误在第四个定义中):
defmodule MyAnagram do
  def anagram?([], []), do: true

  def anagram?([], word) do
    IO.puts 'Not an anagram, the reference word does not contain enough letters'
    false
  end

  def anagram?(reference, []) do
    IO.puts 'Not an anagram, some letters remain in the reference word'
    false
  end

  def anagram?(reference, [head | tail]) when head in reference do
    anagram?(reference - head, tail)
  end

  def anagram?(_, [head | _]) do
    IO.puts 'Not an anagram, #{head} is not in the reference word.'
    false
  end
end

最佳答案

这是由以下代码(如您所确定的)引起的:

def anagram?(reference, [head | tail]) when head in reference do
  anagram?(reference - head, tail)
end

您可以找到 in 的定义。宏 in the source code ,但为方便起见,我将其复制到此处 - 它还包含文档中的以下内容:

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


定义宏的代码:
  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 语句的最后一部分,因为在编译时无法保证您的变量 referencelist 类型(或 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从列表中删除项目。

关于pattern-matching - 如何在保护条款中使用 'in' 运算符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31185006/

相关文章:

algorithm - 基于点和法线识别边缘

string - 使用 awk 对单词进行特定于行的重新排序

与枚举匹配的 Swift guard 语句模式

elixir - 在 Elixir 应用程序中加载 Erlang 项目

Elixir 混合格式忽略行长选项

python - 打印输入字谜中字谜的最大出现次数和字谜本身

python - 通过 Python 查找和分组字谜

string - 是否可以使用 KMP 算法找到最长的子串?

regex - Elixir 案例中的正则表达式

java - 为什么我得到 "Duplicate modifier for the type Test"以及如何修复它