【问题标题】:Erlang for each list of listsErlang 用于每个列表列表
【发布时间】:2016-03-10 06:44:48
【问题描述】:

我想创建一个仅包含长度为 1 的“列表列表”元素的新列表。

我提供的代码给出了异常错误:没有函数子句匹配。

lists:foreach(fun(X) if length(X) =:= 1 -> [X] end, ListOfLists).

我是 erlang 新手,我很难找到编写这段代码的替代方法。

有人可以给我一些建议吗?

【问题讨论】:

  • 一个简单的列表理解就可以了:[L || L
  • 我刚刚意识到,如果您真的是 Erlang 和函数式编程的新手,我最初的解释可能毫无意义。我进一步扩展了我的答案,并讨论了一些关于您可能完成此任务的方法以及原因。

标签: list foreach erlang


【解决方案1】:

您可以在列表推导中进行匹配以非常自然地得到它:

[L || L = [_] <- ListOfLists]

例如:

1> LoL = [[a], [b,c], d, [e], [f,g]].
[[a],[b,c],d,[e],[f,g]]
2> [L || L = [_] <- LoL].
[[a],[e]]

如果您想要元素本身(如结果 [a, e] 而不是 [[a], [e]]),您可以匹配形状内的元素:

3> [L || [L] <- LoL].    
[a,e]

根据LoL 中包含的列表的大小,匹配将比在每个成员上调用length/1 快得多。调用length/1然后测试结果需要遍历整个列表,返回一个值,然后进行测试。这比检查列表的第二个元素是否为终止(换句话说,如果数据的“形状”匹配)的开销更大。

关于您的上述尝试...

作为 Erlang 的新手,熟悉基本的函数式列表操作可能会有所帮助。它们在功能(和逻辑)编程中反复出现,并且通常具有相同的名称。 "maps", "folds", "filters", "cons", "car" ("head" or "hd" or [X|_]), "cdr" ("tail" or "tl" or [_|X]),等等。

你最初的尝试:

lists:foreach(fun(X) if length(X) =:= 1 -> [X] end, ListOfLists).

这行不通,因为foreach/2 只返回ok,从不返回任何值。 在您想要遍历列表以获得副作用时使用,而不是因为您想要获得返回值。例如,如果我有一个聊天系统,聊天室有一个当前成员列表,并且广播消息实际上是向列表中的每个成员发送每个聊天消息,我可能会这样做:

-spec broadcast(list(), unicode:chardata()) -> ok.
broadcast(Users, Message) ->
    Forward = fun(User) -> send(User, Message) end,
    lists:foreach(Forward, Users).

我真的不关心返回值,而且我们不会更改列表UsersMessage 中的任何内容。 (请注意,这里我们使用匿名函数来捕获它所需的相关状态——本质上是对Message 值进行currying,以便我们可以向列表操作@987654341 提供一个arity 1 的函数@. 这就是 lambda 在 Erlang 与命名函数中最有用的地方。)

当您想将列表作为输入并返回单个聚合值(使用一些操作将列表中的所有值合并为一个)时,您可以使用折叠(您几乎总是希望使用 foldl/3 ,具体来说):

4> lists:foldl(fun(X, A) when length(X) =:= 1 -> [X|A]; (_, A) -> A end, [], LoL).
[[e],[a]]

分解为:

Single =
    fun
        (X, A) when length(X) =:= 1 -> [X|A];
        (_, A) -> [X|A]
    end,
ListOfSingles = lists:foldl(Single, [], LoL).

这是一个有两个子句的匿名函数。

用另一种方式写一个我们可以做的案例:

Single =
    fun(X, A) ->
        case length(X) of
            1 -> [X|A];
            _ -> A
        end
    end,

这是一个偏好问题,就像在对 foldl/3 的调用中将其作为匿名函数内联的选择一样。

真正想要做的是过滤列表,并且有一个通用的列表函数就是这样。您提供一个返回布尔值的测试函数——如果测试为真,则该元素将出现在输出中,否则不会:

5> lists:filter(fun([X]) -> true; (_) -> false end, LoL).
[[a],[e]]

像以前一样分解 lambda:

6> Single =
6>     fun([X]) -> true;
6>        (_)   -> false
6>     end.
#Fun<erl_eval.6.54118792>
7> lists:filter(Single, LoL).
[[a],[e]]

这里我们匹配了匿名函数头中元素的形状。这个过滤器几乎完全等同于上面的列表解析(唯一的区别,真的,是列表解析的底层实现——语义上它们是相同的)。

【讨论】:

  • 好吧,现在我的回答是多余的:)
  • @bob 如果您不熟悉 IRC 上的 #erlang 编程摇摆,请访问 SO 上的 Erlang chat,并查看 erlang-questions 邮件列表。它们都是相当低的容量。 Erlang 实际上是相当不错的第一语言,尽管它确实倾向于宠坏人们。此外,如果您有时间SICP,这将非常宝贵。
  • @AlexeyRomanov 我完全错过了它(一旦我意识到 OP 可能需要更多上下文,就回到编辑器中)。删除了吗?
  • @zxq9 是的,我首先看到了您答案的未编辑版本,并且在我完成时已被编辑。
  • @zxq9 了解更多信息。
猜你喜欢
  • 1970-01-01
  • 2015-12-16
  • 1970-01-01
  • 2013-05-27
  • 2014-09-17
  • 1970-01-01
  • 1970-01-01
  • 2012-06-08
  • 2016-02-19
相关资源
最近更新 更多