您可以在列表推导中进行匹配以非常自然地得到它:
[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).
我真的不关心返回值,而且我们不会更改列表Users 或Message 中的任何内容。 (请注意,这里我们使用匿名函数来捕获它所需的相关状态——本质上是对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]]
这里我们匹配了匿名函数头中元素的形状。这个过滤器几乎完全等同于上面的列表解析(唯一的区别,真的,是列表解析的底层实现——语义上它们是相同的)。