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

前端 未结 4 834
不知归路
不知归路 2020-11-29 13:12

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

相关标签:
4条回答
  • 2020-11-29 13:50

    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].
    
    0 讨论(0)
  • 2020-11-29 13:53

    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!

    0 讨论(0)
  • 2020-11-29 14:00

    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.

    0 讨论(0)
  • 2020-11-29 14:03

    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.
    
    0 讨论(0)
提交回复
热议问题