Splitting a list into two Separate lists

Viewed 288

I'm trying to iterate through a given list and put all the positive numbers into Y and all negatives into Z. My code works until I go to add a second element to either Y or Z. If I run the code like so "divide([1,-2],Y,Z)" the code executes with no errors its only if I were to enter "divide([1,-2,3],Y,Z)" it will fail when trying to add 3 to Y.

divide([],[Y],[Z]):- write(Y), write(Z).

divide([H|T],[Y],[Z]):- split(H,Y,Z), divide(T,Y,Z).

split(H,Y,Z):- (H>0 -> append([H],[],Y); append([H],[],Z)).
4 Answers

SWI-Prolog library(apply) offers partition/4, a builtin for your problem, but since I think that for learning you're better to correct your own code here is my advise. Keep it simpler: the base case, i.e. when you are given an empty list, would be just this simple clause:

divide([],[],[]).

Then you must handle a non empty list. If the value is positive, put it in the second list. Otherwise, put it in the third list. You see, we need two more clauses, I will show partially the second one:

divide([V|Vs],[V|Ps],Ns) :-
  V>=0,
  ...

As you see, the head parameters act as both destructuring as well as constructing the relevant values. Put a recursive call instead of the three dots, and write the third clause to handle the case V<0.

I've attempted to use descriptive variables names: Vs stands for values, Ps for positives, Ns for negatives.

I see the other answer and I am confuse. I have been learning by copying others who know better than me my whole life. Maybe this is why I ended up as a teaching assistant intern, instead of a real job.

Here is what I get when I follow the instructions and try to learn by imitating:

list_pos_neg([], [], []).
list_pos_neg([H|T], P, N) :-
    (   H >= 0
    ->  P = [H|P0],
        list_pos_neg(T, P0, N)
    ;   N = [H|N0],
        list_pos_neg(T, P, N0)
    ).

I usually try to avoid the cut and use -> ; instead. But in this case, I think it is clearer this way:

div_pos_neg([], [], []).
div_pos_neg([H|T], [H|P], N) :- H >= 0, !, div_pos_neg(T, P, N).
div_pos_neg([H|T], P, [H|N]) :- H <  0,    div_pos_neg(T, P, N).

Note that the condition is still necessary in the last clause so that calls like div_post_neg([1,2], [], [1, 2]) return the right answer (failure!).

That seems ... complicated. How about just

partition( []     , []     , []     ) .
partition( [X|Xs] , [X|Ns] , Ps     ) :- X <  0 , partition(Xs,Ns,Ps) .
partition( [X|Xs] , Ns     , [X|Ps] ) :- X >= 0 , partition(Xs,Ns,Ps) .
Related