Here is a solution using dcgs. Most interesting is here the usage of a non-terminal that is not context-free. I will start with an attempt that is too general:
grab_tentative(Xs, Ys, Zs) :-
phrase((seq(Ys),seq(Zs)), Xs).
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
grab_tentative/3
ensures that Xs
consists of Ys
concatenated with Zs
. That is way too general, but all intended solutions are already included.
?- Xs = [A,B,C], grab_tentative(Xs,Ys,Zs).
Xs = Zs, Zs = [A, B, C], Ys = []
; Xs = [A, B, C], Ys = [A], Zs = [B, C]
; Xs = [A, B, C], Ys = [A, B], Zs = [C]
; Xs = Ys, Ys = [A, B, C], Zs = []
; false.
The first answer says that Ys = []
, but (as has been clarified by @Sarah), Ys
should always be a non-empty list, so we can restrict the answers to non-empty lists:
grab_tentative(Xs, Ys, Zs) :-
Ys = [_|_],
phrase((seq(Ys),seq(Zs)), Xs).
The answers Xs = [A, B, C], Ys = [A, B], Zs = [C]
and Xs = Ys, Ys = [A, B, C], Zs = []
both permit that A
and B
are different. So we have to add that they are the same:
grab_tentative(Xs, Ys, Zs) :-
Ys = [A|_],
phrase((all_seq(=(A),Ys),seq(Zs)), Xs).
all_seq(_, []) --> [].
all_seq(C_1, [C|Cs]) -->
[C],
{call(C_1,C)},
all_seq(C_1, Cs).
Now, the answers are already a bit better:
?- Xs = [A,B,C], grab_tentative(Xs,Ys,Zs).
Xs = [A, B, C], Ys = [A], Zs = [B, C]
; Xs = [B, B, C], A = B, Ys = [B, B], Zs = [C]
; Xs = Ys, Ys = [C, C, C], A = B, B = C, Zs = []
; false.
The first answer includes that A = B
. So, it really should contain dif(A,B)
. To do so we need to introduce such a context. Here is way to do this. Note that or_end//1
is like []//0
, except that it ensures some extra condition.
grab_final(Xs, Ys, Zs) :-
Ys = [A|_],
phrase((all_seq(=(A),Ys), or_end(dif(A)), seq(Zs)), Xs).
or_end(C_1) -->
call(cond_or_end(C_1)). % interface to predicates
cond_or_end(_C_1, [], []).
cond_or_end(C_1, [E|Es], [E|Es]) :-
Now, the answers are as expected:
?- Xs = [A,B,C], grab_final(Xs, Ys, Zs).
Xs = [A, B, C], Ys = [A], Zs = [B, C], dif(A, B)
; Xs = [B, B, C], A = B, Ys = [B, B], Zs = [C], dif(B, C)
; Xs = Ys, Ys = [C, C, C], A = B, B = C, Zs = []
; false.
grab([],Xs,Ys)
should be true, on the other hand you state that you "need to look at the first item in a list" which implies thatgrab/3
will only succeed with a list containing at least one element. – Earhart