以模式记录



我正在学习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.

问题:

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

实际上记录只是编译时的语法糖,您可以通过使用'E'编译器选项查看实际结构。例如,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.

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

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

Order绑定到字段order等。

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

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

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

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

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

实际上是同样的东西。模式总是具有构造函数的形式——它描述了事物的形状,而不是如何计算它。如前所述,可以使用预实例化的变量:

  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

(最后,出于风格的考虑,请不要在函数名和参数列表的圆括号之间放空格)

最新更新