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):
-mod开发者_JAVA百科ule (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. For example Node#node.order
will be replaced by something like this:
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.
Your swapMaybe
function can be rewritten like this:
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. Pattern matching records works like this:
swapMayBe2(#node{order=Order, parent=Parent, root=Root} = Node, Tree) ->
...
This binds Order
to the field order
etc.
Take a look at the Erlang Programming Examples User's Guide: 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.)
精彩评论