Разделение списка целых чисел на список положительных и список отрицательных целых чисел
Я пытался создать предикат в 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
не монотонно!