Splitting a list of integers into a list of positive integers and a list of negative integers

柔情痞子 提交于 2019-12-28 01:35:27

问题


I've been trying to create a predicate in Prolog which splits a list of integers into a list of positive integers and into a list of negative integers.

Sample query with expected result:

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

This is the code I got so far:

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.

I can't seem to figure out what I'm doing wrong.


回答1:


The recursive part is not quite correct.

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).

The Head should be added to the positive list if Head >= 0 and to the negative list when Head < 0.

Moreover, checking the sign of Head at the beginning is better, because it will prevent unnecessary recursive calls.




回答2:


In SWI-Prolog you can use the predicate partition/4 (which is typically autoloaded from the apply module):

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



回答3:


Here is a definition using constraints. In this case, it is library(clpfd) of SWI and YAP (maybe also XSB). This library is so general that it subsumes regular integer uses.

:- use_module(library(clpfd)).

Using reification:

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).

Alternatively, you can use zcompare/3, I prefer that version:

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).

You can use it for the regular queries, and for more general ones, like

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



回答4:


Stay logically pure and efficient. How? By using meta-predicate tpartition/4 and (#=<)/3!

First, let us define (#=<)/3, the reified version of (#=<)/2, based on bool01_t/2.
For the sake of completeness, let's also define (#<)/3, (#>)/3, and (#>=)/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).

That's it! Now, let's do the split the OP wanted:

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

This is monotone, so we get sound answers even when using general, non-ground terms:

?- 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 .

Edit 2015-06-02

What if we used the SWI-Prolog library predicate partition/4 in above query?

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

We would lose 7 out of 8 solutions because partition/4 is not monotone!



来源:https://stackoverflow.com/questions/9547062/splitting-a-list-of-integers-into-a-list-of-positive-integers-and-a-list-of-nega

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!