当用于将匹配模式化为映射中的键时,变量未绑定

时间:2017-03-02 03:54:13

标签: erlang pattern-matching

当第二个参数是name的值与第一个参数的映射中的键相同时,我希望模式匹配键上的映射。

相反,我得到了这个:

8> c(room).
room.erl:23: variable 'PlayerName' is unbound
error

status({_Players, _Tables, ChallengerName}, #{name := ChallengerName}) ->
  #{status => challenging};
status({#{PlayerName := PlayerStatus}, _, _}, #{name := PlayerName}) ->
  PlayerStatus;
status(_, _) ->
  #{status => null}.

奇怪的是,第一个函数声明在我将元组中的元素与地图中的值进行模式匹配时工作正常。

我发现Pattern matching key in erlang maps - 是否无法在密钥上进行模式匹配?什么是最有效的解决方法?

2 个答案:

答案 0 :(得分:2)

这不是答案,而是Alexey答案的补充。

正如他所做的那样,我认为更改参数顺序应足以解决您的问题。我想到了一个执行模型,其中变量在每个函数头的模式匹配期间从左到右被绑定。因此,如果PlayerName绑定在第一个参数匹配中,则可以在下一个变量中将其重用为地图关键字以检索PlayerStatus

它不像这样工作。实际上,在eq2(X,X) -> ...中使用函数头中的变量两次几乎等同于添加eq1(Y,X) when Y =:= X -> ...中的保护条件为了说明这一点,我编写了一个带有代码版本的最小模块,编译它和拆解并评论结果。您将看到,除了比较的参数顺序的反转之外,2个代码是相同的。

-module(bar).

-compile([export_all]).

eq1(Y, X) when Y =:= X -> Y;
eq1(_, _) -> false.

eq2(X, X) -> true;
eq2(_, _) -> false.

% result of "rp(beam_disasm:file(bar))." command in the shell
%
% {beam_file,bar,
%            [{eq1,2,2},{eq2,2,5},{module_info,0,8},{module_info,1,10}],
%            [{vsn,[196965018902586160349592469625435725005]}],
%            [{options,[]},
%             {version,"7.0.3"},
%             {source,"c:/git/fourretout/src/bar.erl"}],
%            [{function,eq1,2,2,
%                       [{label,1},
%                        {line,1},
%                        {func_info,{atom,bar},{atom,eq1},2},
%                                         ^        ^      ^
%                                         |        |      |_ arity
%                                         |        |________ fuction name
%                                         |_________________ module name
%                        {label,2},
%                          ^
%                          |________________________________ first function head
%                        {test,is_eq_exact,{f,3},[{x,0},{x,1}]},
%                          ^        ^        ^      ^     ^
%                          |        |        |      |     |_ compare the second term of the function stack
%                          |        |        |      |_______ with the first term of the function stack
%                          |        |        |______________ branch to label 3 if test fails
%                          |        |_______________________ which test to perfom (here =:= exact equal)
%                          |________________________________ Instruction to perform: test
%                        return,
%                          ^
%                          |________________________________ Instruction to perform: return. the value returned is the first element of the
%                                                            function stack, in this case it is the first function parameter
%                        {label,3},
%                          ^
%                          |________________________________ second function head
%                        {move,{atom,false},{x,0}},
%                          ^        ^        ^
%                          |        |        |______________ where to move the value: the first element of the function stack
%                          |        |_______________________ what to move: the atom false
%                          |________________________________ Instruction to perform: move a value to some place
%                        return]},
%                          ^    ^
%                          |    |___________________________ last statement of the function
%                          |________________________________ Instruction to perform: return. the value returned is the first element of the
%                                                            function stack, in this case it is the atom false
%             {function,eq2,2,5,
%                       [{line,2},
%                        {label,4},
%                        {func_info,{atom,bar},{atom,eq2},2},
%                        {label,5},
%                        {test,is_eq_exact,{f,6},[{x,1},{x,0}]},
%                        {move,{atom,true},{x,0}},
%                        return,
%                        {label,6},
%                        {move,{atom,false},{x,0}},
%                        return]},
%             {function,module_info,0,8,
%                       [{line,0},
%                        {label,7},
%                        {func_info,{atom,bar},{atom,module_info},0},
%                        {label,8},
%                        {move,{atom,bar},{x,0}},
%                        {line,0},
%                        {call_ext_only,1,{extfunc,erlang,get_module_info,1}}]},
%             {function,module_info,1,10,
%                       [{line,0},
%                        {label,9},
%                        {func_info,{atom,bar},{atom,module_info},1},
%                        {label,10},
%                        {move,{x,0},{x,1}},
%                        {move,{atom,bar},{x,0}},
%                        {line,0},
%                        {call_ext_only,2,{extfunc,erlang,get_module_info,2}}]}]}

即使使用更复杂的函数头,您也可以通过对函数参数进行测试(比较,类型检查,大小...)或从函数参数中提取一些术语来验证编译器的工作原理。

修改

奇怪的是,它可以相对容易地完成,我已经将以下代码编译成汇编程序(使用命令c(bar1,['S'])):

-module(bar1).

-compile([export_all]).

status(#{name := ChallengerName},{_Players, _Tables, ChallengerName}) ->
  #{status => challenging};
status(#{name := PlayerName},{#{test := PlayerStatus}, _, _}) ->
  {PlayerName,PlayerStatus};
status(_, _) ->
  #{status => null}.

status1(Triple, #{name := Name}) ->
  case Triple of
    {_Players, _Tables, Name} -> #{status => challenging};
    {#{Name := PlayerStatus}, _, _} -> PlayerStatus;
    _ -> #{status => null}
  end.

然后将汇编程序文件修改为:

{module, bar1}.  %% version = 0

{exports, [{module_info,0},{module_info,1},{status,2},{status1,2}]}.

{attributes, []}.

{labels, 13}.


{function, status, 2, 2}.
  {label,1}.
    {line,[{location,"bar1.erl",5}]}.
    {func_info,{atom,bar1},{atom,status},2}.
  {label,2}.
    {test,is_map,{f,4},[{x,0}]}.
    {get_map_elements,{f,4},{x,0},{list,[{atom,name},{x,2}]}}.
    {test,is_tuple,{f,4},[{x,1}]}.
    {test,test_arity,{f,4},[{x,1},3]}.
    {get_tuple_element,{x,1},0,{x,3}}.
    {get_tuple_element,{x,1},2,{x,4}}.
    {test,is_eq_exact,{f,3},[{x,4},{x,2}]}.
    {move,{literal,#{status => challenging}},{x,0}}.
    return.
  {label,3}.
    {test,is_map,{f,4},[{x,3}]}.
    {get_map_elements,{f,4},{x,3},{list,[{x,2},{x,0}]}}.
// modif here: reuse the name that was stored in the third place {X,2}, assign the result to top of stack {x,0} and return if ok
    return.
  {label,4}.
    {move,{literal,#{status => null}},{x,0}}.
    return.


{function, status1, 2, 6}.
  {label,5}.
    {line,[{location,"bar1.erl",12}]}.
    {func_info,{atom,bar1},{atom,status1},2}.
  {label,6}.
    {test,is_map,{f,5},[{x,1}]}.
    {get_map_elements,{f,5},{x,1},{list,[{atom,name},{x,2}]}}.
    {test,is_tuple,{f,8},[{x,0}]}.
    {test,test_arity,{f,8},[{x,0},3]}.
    {get_tuple_element,{x,0},0,{x,1}}.
    {get_tuple_element,{x,0},2,{x,3}}.
    {test,is_eq_exact,{f,7},[{x,3},{x,2}]}.
    {move,{literal,#{status => challenging}},{x,0}}.
    return.
  {label,7}.
    {test,is_map,{f,8},[{x,1}]}.
    {get_map_elements,{f,8},{x,1},{list,[{x,2},{x,4}]}}.
    {move,{x,4},{x,0}}.
    return.
  {label,8}.
    {move,{literal,#{status => null}},{x,0}}.
    return.


{function, module_info, 0, 10}.
  {label,9}.
    {line,[]}.
    {func_info,{atom,bar1},{atom,module_info},0}.
  {label,10}.
    {move,{atom,bar1},{x,0}}.
    {line,[]}.
    {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.


{function, module_info, 1, 12}.
  {label,11}.
    {line,[]}.
    {func_info,{atom,bar1},{atom,module_info},1}.
  {label,12}.
    {move,{x,0},{x,1}}.
    {move,{atom,bar1},{x,0}}.
    {line,[]}.
    {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.

从汇编程序(c(bar1,[from_asm]).)重新编译模块,然后在shell中按预期工作(在此代码中我已经恢复了修改函数status的参数顺序比较来自Alexey的status1,但没有必要),我想知道这种模式匹配是否可以在将来的编译器版本中使用?

1> M1 = #{name => "Name"}.
#{name => "Name"}
2> T_chal = {none,none,"Name"}.
{none,none,"Name"}
3> T_stat = {#{"Name" => "Status"},none,none}.
{#{"Name" => "Status"},none,none}
4> T_null = {#{"Other" => "no show"},none,"Unknown"}.
{#{"Other" => "no show"},none,"Unknown"}
5> c(bar1,[from_asm]).
{ok,bar1}
6> bar1:status1(T_chal,M1).
#{status => challenging}
7> bar1:status1(T_stat,M1).
"Status"
8> bar1:status1(T_null,M1).
#{status => null}
9> bar1:status(M1,T_chal).
#{status => challenging}
10> bar1:status(M1,T_stat).
"Status"
11> bar1:status(M1,T_null).
#{status => null}
12>

答案 1 :(得分:1)

我希望只是更改参数顺序会起作用,但显然不行。所以你需要在绑定Name之后在体内进行模式匹配:

status(Triple, #{name := Name}) ->
  case Triple of
    {_Players, _Tables, Name} -> #{status => challenging};
    {#{Name := PlayerStatus}, _, _} -> PlayerStatus;
    _ -> #{status => null}
  end.