【问题标题】:Records in patterns模式中的记录
【发布时间】:2011-07-31 19:17:14
【问题描述】:

我正在学习 erlang,但我偶然发现了一些我不太理解的行为。拿这段代码。 (我知道我正在编程的现有库,但正如我所说,我这样做是出于教育目的):

-module (codec).
-compile (export_all).
-record (node, {symbol, weight, order, left, right, parent} ).
-record (tree, {root, nodes} ).

highestOrderForWeight (Weight, Tree) ->
    lists:max ( [Node#node.order || Node <- Tree#tree.nodes, Node#node.weight == Weight] ).

swapMaybe (Node, Tree) ->
    case highestOrderForWeight (Node#node.weight, Tree) of
        Node#node.order -> pass; 
        Node#node.parent -> pass;
        Tree#tree.root -> pass;
        Partner -> io:format ("Swapping ~p with ~p.~n", [Node#node.order, Partner] )
    end.

编译器根本不喜欢我的代码:

./so.erl:11: illegal pattern
./so.erl:12: illegal pattern
./so.erl:13: illegal pattern
error

在模式中消化记录似乎有些麻烦,因为当我将代码更改为这种笨拙的解决方法时,它编译得很好:

swapMaybe2 (Node, Tree) ->
    [Order, Parent, Root] = [Node#node.order, Node#node.parent, Tree#tree.root],
    case highestOrderForWeight (Node#node.weight, Tree) of
        Order -> pass; 
        Parent -> pass;
        Root -> pass;
        Partner -> io:format ("Swapping ~p with ~p.~n", [Node#node.order, Partner] )
    end.

问题:

  • 如何以模式访问记录字段?
  • 如果不可能这样做,为什么会这样?
  • 如果不能这样做,解决此问题的常见做法是什么?

【问题讨论】:

    标签: erlang design-patterns record


    【解决方案1】:

    实际上记录只是编译时语法糖,您可以使用'E' compiler option 查看实际构造。例如Node#node.order 将被替换为:

    case Node of
        {node,_,_rec0,_,_,_} ->
            rec0;
        _ ->
            error({badrecord,node})
    end
    

    当然,当您尝试使用 Node#node.order 作为模式编译器会报告 illegal pattern 用于此构造。

    您的swapMaybe 函数可以这样重写:

    swapMaybe(#node{order=Order, parent=Parent}, Tree=#tree{root=Root}) ->
        case highestOrderForWeight (Weight, Tree) of
            Order -> pass; 
            Parent -> pass;
            Root -> pass;
            Partner -> io:format ("Swapping ~p with ~p.~n", [Order, Partner] )
        end.
    

    【讨论】:

    • 谢谢。为什么第二个参数以 Tree= 开头,而第一个参数不以 Node= 开头?还是这是一种类型?
    • @Hyperboreus 这是可选的,这意味着您有一个变量Tree 代表整个树记录,同时分解它以获得Root 变量。您也可以添加Node=,但编译器会抱怨Node 未被使用。
    • 整个#tree{} 记录将绑定到Tree 变量,但不需要Node 变量。您还可以像这样对记录进行模式匹配:funcName(Tree=#tree{}) - 在这种情况下,您只需对记录进行模式匹配,但不绑定任何记录变量。
    【解决方案2】:

    确实不可能像您那样在case 语句中使用记录。模式匹配记录的工作方式如下:

     swapMayBe2(#node{order=Order, parent=Parent, root=Root} = Node, Tree) ->
         ...
    

    这会将Order 绑定到字段order 等。

    查看 Erlang 编程示例用户指南:http://www.erlang.org/doc/programming_examples/records.html#id62786

    【讨论】:

      【解决方案3】:

      模式不是一个任意的表达式,它计算出你想要匹配的东西——你不能例如写:

        case ... of
            1 + 2 -> ...
      

      以及您尝试匹配记录字段的值:

        case some_integer(...) of
            Node#node.order -> ...
      

      真的只是同一种东西。 pattern 总是具有构造函数的形式——它描述了事物的形状,而不是它的计算方式。如您所述,可以使用预先实例化的变量:

        Order = Node#node.order,
        case some_integer(...) of
            Order -> ...
      

      更常见的解决方案是将计算值放入保护中,如果您想要的表达式非常简单以至于可以在保护中使用:

      case some_integer(...) of
          Value when Value =:= Node#node.order -> ...
      

      如果表达式很短,您可能希望将它们组合在一个子句中,在保护中使用分号作为分隔符:

      case some_integer(...) of
          V when V =:= Node#node.order ; V =:= Node#node.parent ; V =:= Node#node.root ->
              ...;
          Other ->
              ...
      end
      

      (最后,为了风格,请不要在函数名和参数列表的左括号之间放置空格。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-19
        • 2021-08-03
        • 1970-01-01
        • 2018-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-08
        相关资源
        最近更新 更多