在 Erlang 中将元组与无关变量进行匹配

发布于 2024-11-28 03:52:14 字数 268 浏览 1 评论 0原文

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

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

由于以下错误,该代码无法立即工作:

variable '_' is unbound

有没有一种简单的方法可以达到相同的效果?

I am looking for a way to find tuples in a list in Erlang using a partial tuple, similarly to functors matching in Prolog. For example, I would like to following code to return true:

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

This code does not work right away because of the following error:

variable '_' is unbound

Is there a brief way to achieve the same effect?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

半窗疏影 2024-12-05 03:52:14

对于简单的情况,最好使用已经提到的 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}, ...]).

For simple cases it's better to use already mentioned lists:keymember/3. But if you really need member function you can implement it yourself like this:

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

Example:

>>> member(fun ({pos, _, 2}) -> true; (_) -> false end, [..., {pos, 1, 2}, ...]).
木緿 2024-12-05 03:52:14

您可以使用列表理解通过宏来完成此操作:

-define(member(A,B), length([0 || A <- B])>0).

?member({pos, _, _}, [{width, 17, 42}, {pos, 1, 2}, totally_irrelevant]).

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

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

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

You can do it with a macro using a list comprehension:

-define(member(A,B), length([0 || A <- B])>0).

?member({pos, _, _}, [{width, 17, 42}, {pos, 1, 2}, totally_irrelevant]).

It is not very efficient (it runs through the whole list) but it is the closest I can think to the original syntax.

If you want to actually extract the elements that match you just remove 'length' and add a variable:

-define(filter(A,B), [_E || A =_E <- B]).
扎心 2024-12-05 03:52:14

您可以使用列表理解来做到这一点:

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

You could do it using list comprehension:

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

似梦非梦 2024-12-05 03:52:14

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

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 以及您目前看到的变量绑定,然后为它们编写类似于 '_' 子句的函数子句。

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

Another possibility would be to do what match specs do and use the atom '_' instead of a raw _. Then, you could write a function similar to the following:

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.

Then, your call would now be:

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

This wouldn't let you match patterns like {A, A, '_'} (checking where the first two elements are identical), but if you don't need variables this should work.

You could also extend it to use variables using a similar syntax to match specs ('$1', '$2', etc) with a bit more work -- add a third parameter to is_match with the variable bindings you've seen so far, then write function clauses for them similar to the clause for '_'.

Granted, this won't be the fastest method. With the caveat that I haven't actually measured, I expect using the pattern matching in the language using a fun will give much better performance, although it does make the call site a bit more verbose. It's a trade-off you'll have to consider.

寄离 2024-12-05 03:52:14

可以使用 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'}).
[]

May use 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'}).
[]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文