我想创建一个新列表,其中只包含长度为1的"列表列表"的元素.
我提供的代码给出了一个异常错误:没有函数子句匹配.
lists:foreach(fun(X) if length(X) =:= 1 -> [X] end, ListOfLists).
我是erlang的新手,我很难找到另一种编写这段代码的方法.
有人可以就如何这样做给我一些建议吗?
你可以在列表理解中匹配以获得这个很自然:
[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的新手,熟悉基本的功能列表操作可能会有所帮助.它们在功能(和逻辑)编程中反复弹出,并且通常具有相同的名称."map","folds","filters","cons","car"("head"或"hd"或[X|_]
),"cdr"("tail"或"tl"或[_|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
值,因此我们可以向列表操作提供arity 1的函数foreach/2
.这是lambda在Erlang中最有用的地方vs named功能.)
如果要将列表作为输入并返回单个聚合值(使用某些操作将列表中的所有值都滚动到一个),您可以使用折叠(您几乎总是想要使用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. #Fun7> lists:filter(Single, LoL). [[a],[e]]
这里我们匹配匿名函数头中元素的形状.这个过滤器几乎完全等同于上面的列表推导(唯一的区别,实际上,在列表推导的底层实现中 - 在语义上它们是相同的).