Пролог: как мне разделить список на два списка в зависимости от категории?

#prolog

#пролог

Вопрос:

Мой запрос будет следующим:

 separate([eat(chips),drink(water),eat(burger),eat(banana),drink(coke)],food,drink).
food = [eat(chips),eat(burger),eat(banana)]
drink = [drink(water),drink(coke)]
  

Я хочу разделить список, но я не смог понять, как это сделать.

 separate(X,Cat1,Cat2):-
     [Cat1|Cat2] = X,
     Cat2 = X,
     separate(X,Cat1,Cat2).
  

В настоящее время я могу использовать рекурсию только для просмотра каждого элемента списка, но я действительно не имею ни малейшего представления о том, как начать разделять их на отдельные списки.

Комментарии:

1. Вероятно, это повторяющийся вопрос, но я просто не хочу тратить время на его поиск.

2. @GuyCoder Чувствуешь себя ассистентом преподавателя, не так ли? 😀

3. @DavidTonhofer Feeling like a teaching assistant, are you? Нет. Почему вы так говорите.

4. @GuyCoder Потому что это то, что сказал бы TA.

Ответ №1:

Вы могли бы использовать операции фильтрации более высокого порядка, используя цель для фильтрации:

Например (обратите внимание, что переменные в Прологе должны начинаться с прописных букв):

 separate(TaggedList,Food,Drink) :-
   include(isFood,TaggedList,Food),   % isFood/1 will be called for each element
   include(isDrink,TaggedList,Drink). % same as above

isFood(eat(_)).   % no need to be complex; just succeed if argument matches
isDrink(drink(_)).  % same as above
  

И так:

 ?- separate(
   [eat(chips),drink(water),eat(burger),eat(banana),drink(coke)],
   Food,Drink).
Food = [eat(chips), eat(burger), eat(banana)],
Drink = [drink(water), drink(coke)].
  

Комментарии:

1. Это решение дважды пересекает список. Одним из вариантов было бы использовать раздел / 4 из той же библиотеки с одним из двух вспомогательных предикатов, которые вы показываете в своем решении.

2. @TA_intern Хорошая идея. Мне нужно запомнить. partition/4 и partition/5 .

Ответ №2:

Сопоставление с образцом. Вы хотите сопоставить шаблон.

Когда список продуктов питания и напитков пуст, у вас есть простой базовый предикат:

 separate([],[],[]).
  

Когда вы хотите разделить еду и напитки, это так просто:

 separate([eat(X)|T],[eat(X)|F],D) :- separate(T,F,D).
separate([drink(X)|T],F,[drink(X)|D]) :- separate(T,F,D).
  

Когда заголовок списка совпадает eat , поместите элемент на первое место Food списка. Когда drink затем в Drink списке. Просто.

Когда я запускаю это:

?- разделять ([есть (чипсы), пить (воду), есть (бургер), есть (банан), пить (кока-колу)], Еда, напиток).
Еда = [есть (чипсы), есть (бургер), есть (банан)],
Пить = [пить (вода), пить (кока-кола)].

Ответ №3:

Оба ответа хороши сами по себе. Другим вариантом для SWI-Prolog было бы использовать partition/4 следующее:

 separate(List, Eats, Drinks) :-
    partition(is_eat, List, Eats, Drinks).

is_eat(eat(_)).
  

Дополнительный кредит: почему это не работает?

 ?- partition(eat(_),
             [eat(chips),drink(water),eat(burger),eat(banana),drink(coke)],
             Eats, Drinks).