2016-12-22 2 views
1

Я пытаюсь отфильтровать список кортежей в зависимости от того, соответствует ли первый элемент кортежей элементам в другом списке.Множественные (основанные на кортеже) генераторы в понимании списка в Erlang

Например, первоначальный список

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

Если я пытаюсь использовать несколько генераторов с <- нотации, то петли и результаты в элементах повторяется несколько раз, как это:

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

Это объясняется в ответе Алексея Романова в этом вопросе: Multiple filters in list comprehension in Erlang. Основываясь на этом ответе, я могу сделать ниже, и он отлично работает.

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

Но это не реально удовлетворить свою потребность, так как я хотел бы второй список Item<-[apples,milk] прийти в качестве входных данных, а потому, что не всегда может быть практически вручную перечислить все матчи, как (Item==apples) or (Item==milk) для очень длинный список.

Итак, есть ли способ использовать несколько списков в качестве генераторов? Или более умный способ решить мою проблему. Я только недавно начал работать с Erlang (с помощью Прагматического программирования и книг LYSE), поэтому я все еще знаю очень мало.

+0

вскоре после написания вопроса, мне пришло в голову, что я мог бы преобразовать второй список в список кортежей и использовать только один генератор: '[товар | | {Item, _Unit} <- [{apples, 0}, {milk, 0}]]. Это вводит новые вещи, которые я еще не знаю, и продолжаю изучать кривую http://stackoverflow.com/questions/5214821/list-to-list-of-tuples-convertion и http://stackoverflow.com/questions/3936613/erlang-prepending-an-element-to-a-tuple – Yogesch

ответ

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] 

Второй метод, сгенерируйте кросс-продукт и фильтр. Крест выглядит как {{{апельсины, 2}, яблоки}, {{молоко, 1}, яблоки}, {{яблоки, 2}, яблоки}, {{апельсины, 2}, молоко}, {{молоко, 1}, молоко}, {{яблоки, 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] 
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] 
+0

Преобразование в множество и выполнение обычного пересечения множеств очень интересный подход, я думаю, он будет полезен и для других проблем ... – Yogesch

Смежные вопросы