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

Получив список, я хотел бы вычислить все возможные комбинации пар в списке.

например 2) ввод представляет собой список (a,b,c) Я хотел бы получить пары (a,b) (a,c) (b,c)

например 1) ввод представляет собой список (a,b,c,d) Я хотел бы получить пары (a,b) (a,c) (a,d) (b,c) (b,d) и (c , г)


person user1319603    schedule 01.05.2012    source источник
comment
pairs (x:xs) = [ (x,y) | y<-xs ] ++ pairs xs ; pairs [] = [] в нотации Haskell. Проще всего перевести это в предикат Пролога, который будет создавать все эти пары одну за другой при возврате, как предлагает вам ответ Мога. y<-xs соответствует member(Y,XS) ; ++ соответствует дизъюнкции.   -  person Will Ness    schedule 02.05.2012


Ответы (4)


Использование select/3 дважды (или select/3 один раз и member/2 один раз) позволит вам достичь того, чего вы хотите. Я позволю вам разобраться в деталях и попрошу помощи, если это все еще проблематично.

Кстати, синтаксис Пролога для списка не (a, b, c), а [a, b, c] (ну, это синтаксический сахар, но я остановлюсь на этом).

редактировать: как указал @WillNess, вы не ищете какую-либо пару (X, Y), а только пары, где X находится перед Y в списке.

DCG действительно хорошо подходят: как описано @false, они могут создавать графически привлекательное решение:

... --> [] | [_], ... .

pair(L, X-Y) :-
    phrase((..., [X], ..., [Y], ...), L).

В качестве альтернативы, если вы используете SWI-Prolog, вызов append/2 тоже делает свое дело аналогичным образом, но менее эффективно, чем DCG:

pair2(L, X-Y) :-
    append([_, [X], _, [Y], _], L).

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

person m09    schedule 01.05.2012
comment
возможно, не select/3, а простая рекурсия по входному списку, так как нам, видимо, не нужны элементы, предшествующие тому, который выбран первым (судя по описанию, в котором пропущены пары (b,a), (c,b) и т. д.). - person Will Ness; 02.05.2012
comment
да, я недостаточно тщательно проверил ожидаемый результат, вы должны опубликовать свой комментарий в качестве ответа! - person m09; 02.05.2012

Итак, чтобы перевести Haskell

pairs (x:xs) = [ (x,y) | y<-xs ]
                ++ pairs xs 
pairs []     = []

как предикат Пролога с возвратом, это простой и короткий,

pair([X|XS],X-Y):- member( ... ,XS).  %% fill in the '...' here
pair([_|XS],P) :- pair(XS, ... ).     %%
%% pair([],_) :- false. 

Чтобы получить все возможные пары, используйте findall:

pairs(L,PS):- findall(P, pair(L,P), PS).

Рассмотрите возможность использования bagof, если ваши списки могут содержать логические переменные. Однако управление возвратом bagof может быть сложной проблемой.

pairs также можно записать как детерминированное, рекурсивное определение без возврата, конструирующее свой выходной список с помощью параметра-аккумулятора — здесь в порядке сверху вниз, что делает его действительно списком различий:

pairs([X|T],PS):- T=[_|_], pairs(X,T,T,PS,[]) ; T=[], PS=[].
pairs([],[]).

pairs(_,[],[],Z,Z).
pairs(_,[],[X|T],PS,Z):- pairs(X,T,T,PS,Z).
pairs(X,[Y|T],R,[X-Y|PS],Z):- pairs(X,T,R,PS,Z).
person Will Ness    schedule 02.05.2012
comment
+1: Некоторые комментарии: здесь есть фундаментальное отличие от Haskell: это переменные. Из-за этого findall/3 не работает со списками, содержащими переменные. Рассмотрим bagof/3. Для вашей последней версии: вам не нужен этот замаскированный красный разрез (->)/2. Вы можете сделать это во всей чистоте с помощью T=[_|_] ...; T = []. Также вместо (:)/2 используйте (-)/2, который является конструктором идиоматических пар в Прологе. - person false; 02.05.2012
comment
@false спасибо, изменил это. Хотя тут зеленый срез, подумал я. Что касается имени функтора, я также использовал ':' для пар, а '-' для меня является идиоматическим индикатором списка различий. :) - person Will Ness; 02.05.2012
comment
@false нет, совсем не зеленый разрез. красный разрез был. - person Will Ness; 02.05.2012
comment
Посмотрите: ?- catch(keysort([a],L),error(E,_),true). ответ type_error(pair,a). Итак, a-b это pair. Это стандартная терминология. Списки различий обычно записываются как два аргумента. И (-)/2, и (\)/2 для различий часто приводят к дополнительным ошибкам. - person false; 02.05.2012
comment
@false ах, я вижу, '-' используется в качестве парного функтора по умолчанию w.r.t. Вы имеете в виду keysort/2 в нескольких реализациях Prolog. Конечно, в общем пара — это все, что объединяет любые две вещи и может иметь любой функтор, '-', '/', not_a_pair и т. д. :) - person Will Ness; 03.05.2012
comment
несколько реализаций Пролога и в стандарте. Несколько средств SWI, B, GNU, SICStus, XSB... почти все с keysort/2. Впервые (-)/2 явно назвали парой, вероятно, в 1983 году или около того. Сравните его с Haskell: там также любой конструктор с двумя независимыми типами может быть парой, но все же это понятие относится к (a,b). - person false; 03.05.2012
comment
@false большое спасибо за объяснение! Я не помню, из какой именно книги у меня сложилось такое впечатление... (кстати, в Haskell есть есть абстрактный понятие Pair). - person Will Ness; 03.05.2012
comment
источники: отчет Haskell: zip берет два списка и возвращает список соответствующих < b>пары. Средство проверки типов Prolog от Mycroft&O'Keefe от 23 июня 1983 г.: :- type pair(X,Y) --> X-Y. % for keysort - person false; 03.05.2012

Вот возможный способ решения этого.

Следующий предикат combine/3 истинен, если третий аргумент соответствует списку, содержащему пары, где первый элемент каждой пары равен первому аргументу combine/3. Второй элемент каждой пары будет соответствовать элементу списка во втором аргументе предиката combine/3. Несколько примеров того, как должен работать combine/3:

?- combine(a,[b],X).
X = [pair(a,b)]

?- combine(a,[b,c,d],X).
X = [pair(a,b), pair(a,c), pair(a,d)]

Возможный способ определения combine/3:

combine(A,[B],[pair(A,B)]) :- !.

combine(A,[B|T],C) :-
  combine(A,T,C2),          % Create pairs for remaining elements in T.
  append([pair(A,B)],C2,C). % Append current pair and remaining pairs C2.
                            % The result of append is C.

Теперь combine/3 можно использовать для определения pair/2:

pairs([],[]).      % Empty list will correspond to empty list of pairs.
pairs([H|T],P) :-  % In case there is at least one element.
  nonvar([H|T]),   % In this case it expected that [H|T] is instantiated.
  pairs(H,T,P).

pairs(A,[B],[pair(A,B)]) % If remaining list contains exactly one element,
  :- !.                  % then there will be only one pair(A,B).
pairs(A,[B|T],P) :-      % In case there are at least two elements.
  combine(A,[B|T],P2),   % For each element in [B|T] compute pairs
                         % where first element of each pair will be A.
  pairs(B,T,P3),         % Compute all pairs without A recursively.
  append(P2,P3,P).       % Append results P2 and P3 together.

Пример использования:

?- pairs([a,b,c],X).
X = [pair(a, b), pair(a, c), pair(b, c)].

?- pairs([a,b,c,d],X).
X = [pair(a, b), pair(a, c), pair(a, d), pair(b, c), pair(b, d), pair(c, d)].
person maltindal    schedule 24.05.2019

Вы можете использовать append/ для перебора списка:

?- append(_,[X|R],[a,b,c,d]).
X = a,
R = [b, c, d] ;
X = b,
R = [c, d] ;
X = c,
R = [d] ;
X = d,
R = [] ;
false.

Затем используйте member/2, чтобы сформировать пару X-Y для каждого Y в R:

?- append(_,[X|R],[a,b,c,d]), member(Y,R), Pair=(X-Y).
X = a,
R = [b, c, d],
Y = b,
Pair = a-b ;
X = a,
R = [b, c, d],
Y = c,
Pair = a-c ;
X = a,
R = [b, c, d],
Y = d,
Pair = a-d ;
X = b,
R = [c, d],
Y = c,
Pair = b-c ;
X = b,
R = [c, d],
Y = d,
Pair = b-d ;
X = c,
R = [d],
Y = d,
Pair = c-d ;
false.

Затем используйте findall/3, чтобы собрать все пары в список:

?- findall(X-Y, (append(_,[X|R],[a,b,c,d]), member(Y,R)), Pairs).
Pairs = [a-b, a-c, a-d, b-c, b-d, c-d].

Таким образом, ваше окончательное решение может быть выражено как:

pairs(List, Pairs) :-
    findall(X-Y, (append(_,[X|R],List), member(Y,R)), Pairs).

Пример использования:

?- pairs([a,b,c,d], Pairs).
Pairs = [a-b, a-c, a-d, b-c, b-d, c-d].
person slago    schedule 25.05.2019