这是使用dcg的解决方案。最有趣的是这里使用了非上下文无关的非终端。我将从一个过于笼统的尝试开始:
grab_tentative(Xs, Ys, Zs) :-
phrase((seq(Ys),seq(Zs)), Xs).
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
grab_tentative/3
确保Xs
由与Ys
连接组成Zs
。这太笼统了,但所有预期的解决方案都已包含在内。
?- 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.
第一个答案说, 但是(正如@SarahYs = []
已澄清的那样),应该始终是一个非空列表,因此我们可以将答案限制为非空列表:Ys
grab_tentative(Xs, Ys, Zs) :-
Ys = [_|_] ,
短语((序列(Ys),序列(Zs)),Xs)。
答案Xs = [A, B, C], Ys = [A, B], Zs = [C]
和Xs = Ys, Ys = [A, B, C], Zs = []
两者都允许A
并且B
是不同的。所以我们必须补充一点,它们是相同的:
grab_tentative(Xs, Ys, Zs) :-
Ys = [A|_],
短语(( all_seq(=(A),Ys) ,seq(Zs)), Xs)。
all_seq(_, []) --> []。
all_seq(C_1, [C|Cs]) -->
[C],
{呼叫(C_1,C)},
all_seq(C_1, Cs)。
现在,答案已经好一点了:
?- 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.
第一个答案包括A = B
. 所以,它真的应该包含dif(A,B)
. 为此,我们需要引入这样的上下文。这是执行此操作的方法。请注意,or_end//1
就像[]//0
,除了它确保了一些额外的条件。
grab_final(Xs, Ys, Zs) :-
Ys = [A|_],
短语((all_seq(=(A),Ys), or_end(dif(A)) , seq(Zs)), Xs)。
or_end(C_1) -->
调用(cond_or_end(C_1))。% 谓词接口
cond_or_end(_C_1, [], [])。
cond_or_end(C_1, [E|Es], [E|Es]) :-
现在,答案如预期:
?- 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.