【问题标题】:What is the canonical way to handle conditionals in Erlang?在 Erlang 中处理条件的规范方法是什么?
【发布时间】:2017-06-01 01:00:59
【问题描述】:

我正在研究 Erlang 中的简单列表函数来学习语法。 在我实现“交集”之前,一切看起来都与我为这些函数的 Prolog 版本编写的代码非常相似。

我能想到的最干净的解决方案:

myIntersection([],_) -> [];
myIntersection([X|Xs],Ys) ->
    UseFirst = myMember(X,Ys),
    myIntersection(UseFirst,X,Xs,Ys).

myIntersection(true,X,Xs,Ys) ->
    [X|myIntersection(Xs,Ys)];
myIntersection(_,_,Xs,Ys) ->
    myIntersection(Xs,Ys).

对我来说,这感觉有点像 hack。有没有更规范的方法来处理这个问题?我所说的“规范”是指真正符合 Erlang 设计精神的实现。

注意:这个问题的本质是用户定义的谓词函数的条件处理。我不是要求有人将我指向一个库函数。谢谢!

【问题讨论】:

  • 我会使用if(或case):stackoverflow.com/a/4330106/320615。例如。 if myMember(X, Ys) -> [X|myIntersection(Xs,Ys)]; ....
  • 嗯....我认为我们应该避免使用 if 语句——甚至是 case 语句。我想我听过 Francesco Cesarini 在视频中说他帮助 初学者 将 case 语句转换为函数子句。这是风格指南必须说的:github.com/inaka/…。这是乔·阿姆斯特朗(Joe Armstrong)关于此事的有趣读物:erlang.org/pipermail/erlang-questions/2009-December/048101.html
  • @7stud。感谢您的链接。您的评论是唯一明确回答我的问题的评论。由于您仅将其包含在评论中,因此我写了一个引用您的评论的答案。

标签: erlang


【解决方案1】:

我喜欢这个:

inter(L1,L2) -> inter(lists:sort(L1),lists:sort(L2),[]).

inter([H1|T1],[H1|T2],Acc) -> inter(T1,T2,[H1|Acc]);
inter([H1|T1],[H2|T2],Acc) when H1 < H2 -> inter(T1,[H2|T2],Acc);
inter([H1|T1],[_|T2],Acc) -> inter([H1|T1],T2,Acc);
inter([],_,Acc) -> Acc;
inter(_,_,Acc) -> Acc.

它给出了确切的交点:

inter("abcd","efgh") -> []
inter("abcd","efagh") -> "a"
inter("abcd","efagah") -> "a"
inter("agbacd","eafagha") -> "aag"

如果您希望一个值只出现一次,只需将lists:sort/1 函数之一替换为lists:usort/1


编辑

正如@9000 所说,一个子句是没用的:

inter(L1,L2) -> inter(lists:sort(L1),lists:sort(L2),[]).

inter([H1|T1],[H1|T2],Acc) -> inter(T1,T2,[H1|Acc]);
inter([H1|T1],[H2|T2],Acc) when H1 < H2 -> inter(T1,[H2|T2],Acc);
inter([H1|T1],[_|T2],Acc) -> inter([H1|T1],T2,Acc);
inter(_,_,Acc) -> Acc.

给出相同的结果,并且

inter(L1,L2) -> inter(lists:usort(L1),lists:sort(L2),[]).

inter([H1|T1],[H1|T2],Acc) -> inter(T1,T2,[H1|Acc]);
inter([H1|T1],[H2|T2],Acc) when H1 < H2 -> inter(T1,[H2|T2],Acc);
inter([H1|T1],[_|T2],Acc) -> inter([H1|T1],T2,Acc);
inter(_,_,Acc) -> Acc.

删除输出中的所有重复项。

如果你知道输入列表中没有重复值,我认为

inter(L1,L2) -> [X || X <- L1, Y <- L2, X == Y].

是更短的代码解决方案,但要慢得多(评估 10 000 个元素的 2 个列表的交集需要 1 秒,而之前的解决方案需要 16 毫秒,O(2) 复杂度与@David Varela 提议相当;比率为70 秒与 280 毫秒相比,有 2 个 100 000 个元素的列表!,我猜更大的列表内存不足的风险非常高)

【讨论】:

  • inter 的最后两个模式不等同于最后一个模式吗?
  • 顺便说一句,如果您使用对值进行排序的功能(所有值都可比较吗?),您可以非常便宜地在排序列表中仅保留唯一值,并消除结果中的任何重复项,如果假定设置的语义。
  • 是的,inter([],_,Acc) -&gt; Acc; 这条线没用,我在睡觉后 1 分钟就意识到了:o)。
  • 在erlang中你可以比较任何值,&gt;是为所有类型定义的,类型顺序是:number &lt; atom &lt; reference &lt; fun &lt; port &lt; pid &lt; tuple &lt; map &lt; nil &lt; list &lt; bit string。为避免重复,只需将 inter(L1,L2) -&gt; inter(lists:sort(L1),lists:sort(L2),[]). 更改为 inter(L1,L2) -&gt; inter(lists:usort(L1),lists:sort(L2),[]).
【解决方案2】:

规范方法(“SICP”中的“规范”)是使用累加器。

myIntersection(A, B) -> myIntersectionInner(A, B, []).

myIntersectionInner([], _, Acc) -> Acc;
myIntersectionInner(_, [], Acc) -> Acc;
myIntersectionInner([A|As], B, Acc) -> 
  case myMember(A, Bs) of
    true -> 
      myIntersectionInner(As, Bs, [A|Acc]);
    false -> 
      myIntersectionInner(As, Bs, [Acc]);
  end.

如果两个输入中都存在重复,则此实现当然会产生重复。这可以通过调用myMember(A, Acc) 来解决,并且仅附加A 是结果是否定的。

对于大概的语法,我深表歉意。

【讨论】:

  • 你不能在if 表达式的守卫中使用自定义函数。因为它存在case of
  • @Atomic_alarm:谢谢!实际上,只有一些 BIF 是允许的。我希望我现在已经修复了我的代码(我没有方便检查的 Erlang 安装)。
  • 这个解决方案似乎有一个不便之处,就是ValA列表中出现了多次,而B列表中只有一个,结果中会出现多次。
  • @Pascal:确实,如答案中所述。此外,它具有 O(|A| * |B|) 时间复杂度;对于唯一性所需的查找,情况会更糟。如果数据结构具有更快的查找速度,例如使用树或哈希表代替普通列表,情况会更好。
【解决方案3】:

虽然我很欣赏建议的高效实现,但我的目的是更好地理解 Erlang 的实现。作为初学者,我认为@7stud 的评论,尤其是http://erlang.org/pipermail/erlang-questions/2009-December/048101.html,是最有启发性的。本质上,函数中的“大小写”和模式匹配在底层使用相同的机制,尽管为了清晰起见应该首选函数。

在真实系统中,我会使用@Pascal 的一种实现;取决于“相交”是否有任何繁重的工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-28
    • 2011-10-31
    • 2010-09-21
    • 2020-08-02
    • 2013-08-18
    • 1970-01-01
    • 2014-12-08
    • 1970-01-01
    相关资源
    最近更新 更多