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

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.

【讨论】:

以上是关于当用于模式匹配作为映射中的键时,变量是未绑定的的主要内容,如果未能解决你的问题,请参考以下文章

Erlang 映射中的模式匹配键

函数返回与函数参数中的 Haskell 模式匹配

如何删除与 Redis 集群中的模式匹配的键

如何从redis-cli repl中删除Redis匹配模式中的所有键?

键绑定中的修饰符(SHIFT +(随机键))

匹配 Map 中的键时替换列值