Erlang中列表理解中的多个(基于元组的)生成器

时间:2016-12-22 03:16:20

标签: erlang

我试图根据元组的第一个元素是否与另一个列表中的元素匹配来过滤元组列表。

例如,原始列表是

163>Shoppinglist.
[{oranges,2},{milk,1},{apples,2}]

如果我尝试使用带有<-表示法的多个生成器,它会循环并导致元素重复多次,如下所示:

164> [(Item)||{Item,Unit}<-Shoppinglist, Item<-[apples,milk]].
[apples,milk,apples,milk,apples,milk]

Alexey Romanov在这个问题的答案中很好地解释了这一点:Multiple filters in list comprehension in Erlang。基于这个答案,我可以做到以下,它工作正常。

165>[{Item}||{Item,Unit}<-Shoppinglist,(Item==apples) or (Item==milk)].
[apples, milk]

但这并不能真正满足我的需要,因为我希望第二个列表Item<-[apples,milk]作为输入进入,并且因为手动列出所有匹配项{{}}可能并不总是切实可行1}}一个很长的清单。

那么有没有办法真正使用多个列表作为生成器?或者更聪明的方法来解决我的问题..我最近才开始使用Erlang(使用实用编程和LYSE书籍)所以我仍然知道的很少。

2 个答案:

答案 0 :(得分:3)

1> Shoppinglist = [{oranges,2},{milk,1},{apples,2}].
[{oranges,2},{milk,1},{apples,2}]
2> Filterlist = [apples,milk].
[apples,milk]

第一种方法,直接过滤列表:在列表推导中,过滤器可以是任何返回false或true的语句。如果过滤器列表很大,则有利于内存占用,不利于执行时间。

3> [Item || {Item,_} <- Shoppinglist, lists:member(Item,Filterlist)].
[milk,apples]

第二种方法,生成交叉产品和过滤器。交叉产品看起来像[{{oranges,2},apples},{{milk,1},apples},{{apples,2},apples},{{oranges,2},milk},{{milk, 1},牛奶},{{apples,2},牛奶}],我不认为它是真正构建的,但它有助于理解过滤器的工作原理。对内存占用不利,性能问号。

4> [Item1 || {Item1,_} <- Shoppinglist, Item2 <- Filterlist, Item1 == Item2].   
[milk,apples]

如果列表可能很大,我认为第一个解决方案更好,如果代码是关键的(大列表,很多调用),那么我建议从过滤器列表创建一个映射,然后使用此映射作为过滤器:

5> [Item || {Item,_} <- Shoppinglist, maps:get(Item,lists:foldl(fun(X,Acc) -> maps:put(X,true,Acc) end, #{},Filterlist),false)].
[milk,apples]

答案 1 :(得分:2)

对于我的观点,列表理解不适合这项任务。我宁愿创建集合并找到交集点:

 SList = [{oranges,2},{milk,1},{apples,2}].
 IList = [apples, milk].
 ISet = sets:from_list(IList).
 SSet = sets:from_list([I|{I,_} <- SList]).
 6> sets:to_list(sets:intersection(ISet, SSet)).
 [apples,milk]