Записи в шаблонах
Я учу эрланг и спотыкаюсь о каком-то поведении, которое не совсем понимаю. Возьми этот кусок кода. (Я знаю, что существуют библиотеки для того, что я программирую, но, как я уже сказал, я делаю это в образовательных целях):
-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.
Вопросы:
- Как получить доступ к полям записи в шаблонах?
- Если это невозможно, почему так?
- Если это невозможно сделать, какова обычная практика, чтобы обойти это?
3 ответа
На самом деле записи - это просто синтаксический сахар времени компиляции, и вы можете посмотреть на реальные конструкции, используя '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
Шаблон не является произвольным выражением, которое соответствует тому, с чем вы хотите сопоставить - вы не можете, например, написать:
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 -> ...
Если выражения короткие, вы можете объединить их в одном предложении, используя точку с запятой в качестве разделителя в Guard:
case some_integer(...) of
V when V =:= Node#node.order ; V =:= Node#node.parent ; V =:= Node#node.root ->
...;
Other ->
...
end
(Наконец, в качестве стиля, пожалуйста, не ставьте пробел между именем функции и открывающей скобкой списка аргументов.)