Разделение списка целых чисел на список положительных и список отрицательных целых чисел

Я пытался создать предикат в Prolog, который разбивает список целых чисел на список положительных целых чисел и на список отрицательных целых чисел.

пример запроса с ожидаемым результатом:

?- split([1,-2,3,4,-8],X,Y).
X = [1,3,4],
Y = [-2,-8].

это код, который я получил до сих пор:

split([], [], []).
split([Head|Tail], List1, List2) :- split(Tail, [Head|List1], List2), Head>=0.
split([Head|Tail], List1, List2) :- split(Tail, List1, [Head|List2]), Head<0.

Я не могу понять, что я делаю неправильно.

4 ответов


рекурсивная часть не совсем корректна.

split([], [], []).
split([Head|Tail], [Head|List1], List2) :- Head>=0, split(Tail, List1, List2).
split([Head|Tail], List1, [Head|List2]) :- Head<0, split(Tail, List1, List2).

на Head следует добавить в положительный список, если Head >= 0 и в отрицательный список, когда Head < 0.

более того, проверка знака Head в начале лучше, потому что это предотвратит ненужные рекурсивные вызовы.


В SWI-Prolog вы можете использовать предикат partition/4 (который обычно загружается автоматически из apply модуль):

?- partition(=<(0), [1,-2,3,4,-8,0], X, Y).
X = [1, 3, 4, 0],
Y = [-2, -8].

вот определение с использованием ограничений. В этом случае это library(clpfd) SWI и YAP (возможно, также XSB). Эта библиотека настолько общая, что она включает регулярные целочисленные использования.

:- use_module(library(clpfd)).

используя овеществления:

split([], [], []).
split([E|Es], Poss, Negs) :-
   E #>= 0 #<==> B,
   i_split(B, E, Es, Poss, Negs).

i_split(1, E, Es, [E|Poss], Negs) :-
   split(Es, Poss, Negs).
i_split(0, E, Es, Poss, [E|Negs]) :-
   split(Es, Poss, Negs).

кроме того, вы можете использовать zcompare/3, Я предпочитаю эту версию:

split([], [], []).
split([E|Es], Poss, Negs) :-
   zcompare(Order, E, 0),
   c_split(Order, E, Es, Poss, Negs).

c_split(>, E, Es, [E|Poss], Negs) :-
   split(Es, Poss, Negs).
c_split(=, E, Es, [E|Poss], Negs) :-
   split(Es, Poss, Negs).
c_split(<, E, Es, Poss, [E|Negs]) :-
   split(Es, Poss, Negs).

вы можете использовать его для регулярных запросов и для более общих, таких как

?- split(Es,[A],[]).
Es = [A],
A in 1..sup ;
Es = [0],
A = 0 ;
false.

остаться логически чисто и эффективными. Как? Используя мета-предикат tpartition/4 и (#=<)/3!

во-первых, давайте определимся (#=<)/3, овеществленная версия (#=<)/2, на основании on bool01_t/2.
для полноты, давайте также определим (#<)/3, (#>)/3 и (#>=)/3!

#=<(X,Y,Truth) :- X #=< Y #<==> B, bool01_t(B,Truth).

#<( X,Y,Truth) :- X #<  Y #<==> B, bool01_t(B,Truth).

#>( X,Y,Truth) :- X #>  Y #<==> B, bool01_t(B,Truth).

#>=(X,Y,Truth) :- X #>= Y #<==> B, bool01_t(B,Truth).

вот именно! теперь, давайте сделаем разделение OP хотел:

?- tpartition(#=<(0),[1,-2,3,4,-8,0],Ts,Fs).
Ts = [1,3,4,0], Fs = [-2,-8].                   % succeeds deterministically

это монотонно, так что мы получить звук ответы даже при использовании общих, не первом плане:

?- tpartition(#=<(0),[A,B,C],Ts,Fs).
Ts = [     ], Fs = [A,B,C], A in inf.. -1, B in inf.. -1, C in inf.. -1 ;
Ts = [    C], Fs = [A,B  ], A in inf.. -1, B in inf.. -1, C in   0..sup ;
Ts = [  B  ], Fs = [A,  C], A in inf.. -1, B in   0..sup, C in inf.. -1 ;
Ts = [  B,C], Fs = [A    ], A in inf.. -1, B in   0..sup, C in   0..sup ;
Ts = [A    ], Fs = [  B,C], A in   0..sup, B in inf.. -1, C in inf.. -1 ;
Ts = [A,  C], Fs = [  B  ], A in   0..sup, B in inf.. -1, C in   0..sup ;
Ts = [A,B  ], Fs = [    C], A in   0..sup, B in   0..sup, C in inf.. -1 ;
Ts = [A,B,C], Fs = [     ], A in   0..sup, B in   0..sup, C in   0..sup .

изменить 2015-06-02

что делать, если мы использовали предикат библиотеки SWI-Prolog partition/4 в приведенном запросе?

?- partition(#=<(0),[A,B,C],Ts,Fs).
Ts = [A,B,C], Fs = [], A in 0..sup, B in 0..sup, C in 0..sup.

мы потеряем 7 из 8 решений, потому что partition/4 не монотонно!