【问题标题】:How to use the 'in' operator in guard clauses?如何在保护子句中使用“in”运算符?
【发布时间】:2015-09-20 00:12:18
【问题描述】:

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

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

(ArgumentError) 运算符 in 的参数无效,它需要编译 用于守卫表达式时右侧的时间列表或范围

我不知道该怎么做才能解决它。这是代码(错误在第四个定义中):

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

【问题讨论】:

    标签: pattern-matching elixir anagram


    【解决方案1】:

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

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

    您可以找到inin the source code 的定义,但为了方便起见,我在这里复制了它——它在文档中还包含以下内容:

    警卫

    in 运算符可以在保护子句中使用,只要 因为右侧是范围或列表。在这种情况下,Elixir 将运算符扩展为有效的保护表达式。例如:

      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 以从列表中删除项目。

    【讨论】:

    • 谢谢!你能详细说明你的最后两点吗?为什么我要使用“字符串”而不是字符列表? - 的好消息,我的意图是使用 --,但我的手指对计划的理解很差。这适用于字符列表,而不是字符串。
    • @svarlet 通常,char_lists 仅用于维护对 Erlang 库的支持。 Elixir 库通常更喜欢字符串,因为它们具有出色的 UTF-8 支持。我试图为您找到可靠的参考,但不幸的是,大多数资源都停留在“除非与 Erlang 交互,否则您应该使用字符串。”
    • 虽然char列表是在IO上工作的,但是你应该有使用双引号的习惯,否则以后肯定会咬你。例如,字符串模块仅适用于双引号的字符串。
    猜你喜欢
    • 2019-12-26
    • 1970-01-01
    • 2017-02-02
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多