我正在学习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
(最后,出于风格的考虑,请不要在函数名和参数列表的圆括号之间放空格)