当用于模式匹配作为映射中的键时,变量是未绑定的
Posted
技术标签:
【中文标题】当用于模式匹配作为映射中的键时,变量是未绑定的【英文标题】:Variable is unbound when used to pattern match as a key in a map 【发布时间】:2017-07-21 15:44:29 【问题描述】:当第二个参数是 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 - 没有办法在键上进行模式匹配吗?最有效的解决方法是什么?
【问题讨论】:
如果您有兴趣,我会在新答案中发表一些评论。 【参考方案1】:这不是答案,而是对 Alexey 答案的补充。
正如他所做的那样,我认为更改参数顺序应该足以解决您的问题。我想到了一个执行模型,其中变量在每个函数头的模式匹配期间从左到右进行绑定。因此,如果PlayerName
绑定在第一个参数匹配中,它可以在下一个变量中作为映射键重用以检索PlayerStatus
。
它不是这样工作的。事实上,像eq2(X,X) -> ...
那样在函数头中使用两次变量几乎等同于像eq1(Y,X) when Y =:= X -> ...
那样添加一个保护条件为了说明这一点,我用代码版本编写了一个最小模块,编译它并反汇编并注释了结果。您会看到,除了用于比较的参数顺序倒置之外,这两个代码是相同的。
-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
的参数顺序与 status1
进行比较来自 Alexey,但没有必要),我想知道这种模式匹配是否会在编译器的未来版本中可用?
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>
【讨论】:
【参考方案2】:我希望只是更改参数顺序会起作用,但显然不行。因此,您需要在绑定 Name
之后在正文内部进行模式匹配:
status(Triple, #name := Name) ->
case Triple of
_Players, _Tables, Name -> #status => challenging;
#Name := PlayerStatus, _, _ -> PlayerStatus;
_ -> #status => null
end.
【讨论】:
以上是关于当用于模式匹配作为映射中的键时,变量是未绑定的的主要内容,如果未能解决你的问题,请参考以下文章