【问题标题】:Matching tuples with don't-care variables in Erlang在 Erlang 中将元组与无关变量匹配
【发布时间】:2011-10-18 21:27:18
【问题描述】:

我正在寻找一种使用部分元组在 Erlang 列表中查找元组的方法,类似于 Prolog 中的仿函数匹配。比如我想用下面的代码返回true:

member({pos, _, _}, [..., {pos, 1, 2}, ...])

由于以下错误,此代码无法立即运行:

variable '_' is unbound

有没有简单的方法可以达到同样的效果?

【问题讨论】:

    标签: erlang pattern-matching


    【解决方案1】:

    您可以使用列表推导来做到这一点:

    Matches = [ Match || {Prefix, _, _} = Match <- ZeList, Prefix == pos].

    【讨论】:

    • 这很好,但是可以重写它以便将模式作为函数参数给出(例如,用于编写“my_member”函数)?
    • 你可以写得更简单 [Match || {pos, _, _}=Match <- ZeList] 但它是 filter 而不是 member 功能。
    • @Little Bobby Tables [Elem || Elem
    【解决方案2】:

    请改用lists:keymember/3

    【讨论】:

    • 这也允许模式匹配吗?因为问题不在于获得平等匹配。
    • @WardB lists:keymember(pos, 1, List) 例如在问题中
    【解决方案3】:

    对于简单的情况,最好使用已经提到的lists:keymember/3。但是如果你真的需要member 函数,你可以自己实现它:

    member(_, []) ->
        false;
    member(Pred, [E | List]) ->
        case Pred(E) of
            true ->
                true;
            false ->
                member(Pred, List)
        end.
    

    例子:

    >>> member(fun ({pos, _, 2}) -> true; (_) -> false end, [..., {pos, 1, 2}, ...]).
    

    【讨论】:

    • 我希望避免这种情况,但似乎谓词函数是将模式作为参数传递的唯一方法。
    • lists:any 实际上和这个member 函数完全一样:)
    • @Little Bobby Tables 我相信如果将member 实现为宏,则可以避免谓词函数
    【解决方案4】:

    另一种可能性是执行匹配规范所做的事情并使用原子'_' 而不是原始_。然后,您可以编写类似于以下的函数:

    member(X, List) when is_tuple(X), is_list(List) ->
        member2(X, List).
    
    % non-exported helper functions:
    
    member2(_, []) ->
        false;
    member2(X, [H|T]) when not is_tuple(H); size(X) =/= size(H) ->
        member2(X, T);
    member2(X, [H|T]) ->
        case is_match(tuple_to_list(X), tuple_to_list(H)) of
            true -> true;
            false -> member2(X, T)
        end.
    
    is_match([], []) ->
        true;
    is_match(['_'|T1], [_|T2]) ->
        is_match(T1, T2);
    is_match([H|T1], [H|T2]) ->
        is_match(T1, T2);
    is_match(_, _) ->
        false.
    

    那么,你的电话现在是:

    member({pos, '_', '_'}, [..., {pos, 1, 2}, ...])
    

    这不会让你匹配像{A, A, '_'} 这样的模式(检查前两个元素在哪里相同),但如果你不需要变量,这应该可以工作。

    您还可以扩展它以使用类似语法的变量来匹配规范('$1''$2' 等),但需要做更多工作——将第三个参数添加到 is_match 并使用您的变量绑定到目前为止已经看到了,然后为它们编写函数子句,类似于'_' 的子句。

    当然,这不是最快的方法。需要注意的是,我还没有实际测量过,我希望使用有趣的语言在语言中使用模式匹配会带来更好的性能,尽管它确实会使调用站点更加冗长。这是你必须考虑的权衡。

    【讨论】:

      【解决方案5】:

      您可以通过使用列表推导的宏来做到这一点:

      -define(member(A,B), length([0 || A <- B])>0).
      
      ?member({pos, _, _}, [{width, 17, 42}, {pos, 1, 2}, totally_irrelevant]).
      

      它不是很有效(它贯穿整个列表)但它是我能想到的最接近原始语法的。

      如果您想实际提取匹配的元素,只需删除“长度”并添加一个变量:

      -define(filter(A,B), [_E || A =_E <- B]).
      

      【讨论】:

        【解决方案6】:

        可以使用ets:match:

        6> ets:match(T, '$1'). % Matches every object in the table
        [[{rufsen,dog,7}],[{brunte,horse,5}],[{ludde,dog,5}]]
        7> ets:match(T, {'_',dog,'$1'}).
        [[7],[5]]
        8> ets:match(T, {'_',cow,'$1'}).
        []
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-15
          • 2010-11-12
          • 1970-01-01
          • 2015-05-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多