Reputation: 2725
I've been trying to split a Prolog at a given element. I've come close but I can't get the left part of the list.
split(X,[Y|L]) :- split(X,[Y|L],[Y|K],M).
split(_,[],[],[]).
split(X,[Y|L],K,[Y|M]) :- X < Y, split(X,L,K,M).
split(X,[Y|L],[Y|K],M) :- X >= Y, split(X,L,K,M).
split(X,[Y|L],[Y|K],M) :- X = Y, write(Y), write(' '), write(L).
Input:
split(2,[1,2,3,4,5]).
Returns:
2 [3,4,5]
I want it to return
[1,2] [3,4,5].
Upvotes: 3
Views: 1930
Reputation: 60034
I would write this way, so you can 'reuse' the predicate if you need some other use case (writing to output stream isn't what we need, most of time)
split(X, Xs) :-
split(X, Xs, L, R), format('~w ~w', [L, R]).
split(_, [], [], []).
split(X, [Y|Xs], [], [Y|Xs]) :-
X < Y.
split(X, [X|Xs], [X], Xs).
split(X, [Y|Xs], [Y|Ls], Rs) :-
X > Y,
split(X, Xs, Ls, Rs).
test
?- split(2,[1,2,3,4,5]).
[1,2] [3,4,5]
true ;
false.
Upvotes: 1
Reputation: 22803
I'm not altogether sure what you want to do here. For example, what is split(5, [1,2,3,4,5])
supposed to do? For that matter, is it safe to assume the input list is sorted? I'm going to assume so.
The basic problem with your rule split/2
is that you don't have any out parameters. Prolog works differently than most programming languages; there is no such thing as return
. At first this feels like a crippling limitation, but Prolog actually allows you to "return" as many results as you like. Looking at this code it seems to me you're about halfway to this understanding, because you have split/2
which doesn't seem to have any out parameters, but it calls split/4
which has both of the out parameters you need.
For starters, I'd abandon the idea of split/2
and try to get split/4
to work. What you have doesn't look too insane there:
?- split(2, [1,2,3,4,5], X, Y).
X = [1, 2],
Y = [3, 4, 5] .
?- split(5, [1,2,3,4,5], X, Y).
X = [1, 2, 3, 4, 5],
Y = [] .
?- split(1, [1,2,3,4,5], X, Y).
X = [1],
Y = [2, 3, 4, 5] .
This suggests that most of your rules are correct. Where you get into trouble is with the additional solutions:
?- split(2, [1,2,3,4,5], X, Y).
... ;
2 [3,4,5]
X = [1, 2|_G317] ;
false.
?- split(5, [1,2,3,4,5], X, Y).
... ;
5 []
X = [1, 2, 3, 4, 5|_G326] ;
false.
?- split(1, [1,2,3,4,5], X, Y).
... ;
1 [2,3,4,5]
X = [1|_G314].
You can see from the writing going on there that these additional solutions were generated by your last rule, X = Y, write(Y)...
etc. Simply removing that rule produces the desired behavior in split/4
.
Moving on, I think probably rather than split/2
what you actually want is split/3
with one output parameter, a list of lists. It would not be terribly hard to generate based on what we have so far:
split(X, List, [Before, After]) :- split(X, List, Before, After).
Running it we see we get the desired result you mentioned:
?- split(3, [1,2,3,4,5], X).
X = [[1, 2, 3], [4, 5]] ;
false.
?- split(1, [1,2,3,4,5], X).
X = [[1], [2, 3, 4, 5]] ;
false.
?- split(5, [1,2,3,4,5], X).
X = [[1, 2, 3, 4, 5], []] ;
false.
Hope this helps!
Upvotes: 2