简体   繁体   English

记录模式

[英]Records in patterns

I am learning erlang and I stumbles over some behaviour I cannot quite understand. 我正在学习二郎,我偶然发现了一些我无法理解的行为。 Take this piece of code. 拿这段代码。 (I know there are existing libraries for what I am programming, but as I stated, I do this for educational purposes): (我知道我正在编程的是现有的库,但正如我所说,我这样做是出于教育目的):

-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.

The compiler is not at all amused about my code: 编译器对我的代码一无所知:

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

It has appearently some trouble digesting records in patterns, because when I change my code to this clumsy work-around, it compiles fine: 在模式中消化记录显然有些麻烦,因为当我将我的代码更改为这种笨拙的解决方案时,它编译得很好:

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.

Questions: 问题:

  • How do I access record fields in patterns? 如何访问模式中的记录字段?
  • If it is not possible to do so, why is that so? 如果不可能这样做,为什么会这样呢?
  • If it is not possible to do so, what is the common practice to work around that? 如果不可能这样做,解决这个问题的常见做法是什么?

Actually records are just a compile time syntactic sugar and you can look at the actual constructs by using 'E' compiler option . 实际上,记录只是一个编译时的语法糖,你可以通过使用'E' 编译器选项来查看实际的构造。 For example Node#node.order will be replaced by something like this: 例如, Node#node.order将被替换为以下内容:

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

And of course when you try to use Node#node.order as a patter compiler reports illegal pattern for this construct. 当然,当您尝试使用Node#node.order作为模式编译器报告此构造的illegal pattern

Your swapMaybe function can be rewritten like this: 您的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.

It's indeed not possible to use records in case statements the way you did. 确实不可能像你所做的那样在case语句中使用记录。 Pattern matching records works like this: 模式匹配记录的工作方式如下:

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

This binds Order to the field order etc. 这会将Order绑定到字段order等。

Take a look at the Erlang Programming Examples User's Guide: http://www.erlang.org/doc/programming_examples/records.html#id62786 看看Erlang编程实例用户指南: http//www.erlang.org/doc/programming_examples/records.html#id62786

A pattern is not an arbitrary expression that evaluates to the thing you want to match against - you can't for example write: 模式不是一个任意表达式,它会计算出你要匹配的东西 - 你不能写例如:

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

and your attempt to match against the value of a field of a record: 以及您尝试匹配记录字段的值:

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

is really just the same kind of thing. 真的是同样的事情。 A pattern always has the form of a constructor - it describes the shape of a thing, not how it is computed. 模式始终具有构造函数的形式 - 它描述事物的形状,而不是如何计算。 As you noted, pre-instantiated variables can be used: 如您所述,可以使用预先实例化的变量:

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

The more common solution is to put the computed value in a guard, if the expression you want is so simple that it's allowed in guards: 更常见的解决方案是将计算值放在一个保护中,如果你想要的表达式如此简单以至于它在守卫中是允许的:

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

If the expressions are short, you might want to combine them in a single clause, using a semicolon as separator in the guard: 如果表达式很短,您可能希望将它们组合在一个子句中,使用分号作为保护中的分隔符:

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

(Finally, as a matter of style, please don't put a space between the function name and the opening parenthesis of the argument list.) (最后,作为一种风格问题,请不要在函数名称和参数列表的左括号之间放置一个空格。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM