使用seq//1:
list_splitonstop(Xs, As, Bs) :-
phrase( ( seq(As), [stop], seq(Bs) ), Xs).
此版本按您的预期工作:
?- L = [tea,coffee,sugar,cake,stop,meat,fish,eggs,flour],
list_splitonstop(L, L1, L2).
L = [tea, coffee, sugar, cake, stop, meat, fish, eggs, flour],
L1 = [tea, coffee, sugar, cake],
L2 = [meat, fish, eggs, flour]
; false.
但是,这真的是最好的解决方案吗?最后的; false 可能表明它不是。但我们不能肯定地说。我们必须找出另一种情况,该解决方案无法按预期工作。您在其他编程语言中也面临着类似的问题,必须非常依赖程序员的想象力才能找出边界情况等。
幸运的是,我们在这里使用 Prolog,它可以帮助我们理解我们实际定义的内容。
一个非常简单的开始是询问最一般的查询。就这样:
| ?- list_splitonstop(L, L1, L2).
L = [stop], L1 = [], L2 = []
; L = [stop,_A], L1 = [], L2 = [_A]
; L = [stop,_A,_B], L1 = [], L2 = [_A,_B]
; L = [stop,_A,_B,_C], L1 = [], L2 = [_A,_B,_C]
...
看看每个答案!我们以第三个为例。 L = [stop,_A,_B] 表示此答案包括具有三个元素的 all 列表,其中第一个元素是 stop。因此,我们在这里寻找无限 的解决方案,这些解决方案都用几个字符进行了紧凑的描述!甚至bzip2 -99 都做不到!
这些是仅有的三个元素的列表吗?我们不能仅从这个单一的查询中说出这一点,因为 Prolog 可能会以 不公平 的方式列举答案。想象一下,你让某人告诉你所有的自然数,但那个人从 0、2、4 开始……显然,这种枚举对奇数是非常不公平的。同样,可能缺少一些答案...
在 Prolog 中,我们可以坚持只查看长度为 3 的列表:
| ?- L = [_,_,_], list_splitonstop(L, L1, L2).
L = [stop,_A,_B], L1 = [], L2 = [_A,_B]
; L = [_A,stop,_B], L1 = [_A], L2 = [_B]
; L = [_A,_B,stop], L1 = [_A,_B], L2 = []
; false.
所以我们可以在一个查询中查询所有长度为 3 的相关案例。请注意,这些 _A 和 _B 变量代表任何术语!请花点时间欣赏您正在查看的内容:长度为 3 的列表的所有案例。没有其他案例需要考虑!
当您查看此类答案时,可能会出现一些问题。比如:这三个答案是重叠的,还是真的不相交? Prolog 知道答案。只需重复实际目标并计算得到的答案:
| ?- L = [_,_,_], list_splitonstop(L, L1, L2), list_splitonstop(L, L1, L2).
(answers same as above)
所以我们得到完全相同的答案。没有固有的冗余。
另一个问题可能是:L 是否总是恰好有一个可能的拆分? (换句话说:是否存在函数依赖?)
我们可以通过询问具有不同 L1 和 L2 的 L 来解决此问题:
| ?- L = [_,_,_], dif(L1-L2,L1x-L2x),
list_splitonstop(L, L1, L2), list_splitonstop(L, L1x, L2x).
L = [stop,stop,_A], L1 = [], L2 = [stop,_A], L1x = [stop], L2x = [_A]
; L = [stop,_A,stop], L1 = [], L2 = [_A,stop], L1x = [stop,_A], L2x = []
; L = [stop,stop,_A], L1 = [stop], L2 = [_A], L1x = [], L2x = [stop,_A]
; L = [_A,stop,stop], L1 = [_A], L2 = [stop], L1x = [_A,stop], L2x = []
; L = [stop,_A,stop], L1 = [stop,_A], L2 = [], L1x = [], L2x = [_A,stop]
; L = [_A,stop,stop], L1 = [_A,stop], L2 = [], L1x = [_A], L2x = [stop]
; false.
那么,我现在可能会问你:你想要上述情况吗?如果stop 出现多次?显然,您没有具体说明这一点,我们需要您提供更多信息。 Prolog 至少有助于识别此类情况。
如何识别多余的答案。
在上述案例中,我们观察到没有多余的答案。但是当他们出现时,他们是如何出现的呢?这是一个这样的例子:member/2 是内置的,会产生(有时)冗余答案,memberd/2 没有这种冗余。实际问题是:
将e 作为元素/成员的二元素列表看起来如何?
?- Xs = [_,_], member(e, Xs).
Xs = [e, _A]
; Xs = [_A, e].
?- Xs = [_,_], member(e, Xs), member(e, Xs).
Xs = [e, _A]
; Xs = [e, e] % <--- redundant
; Xs = [e, e] % <--- redundant
; Xs = [_A, e].
?- Xs = [_,_], memberd(e, Xs).
Xs = [e, _A]
; Xs = [_A, e], dif(_A, e)
; false.
?- Xs = [_,_], memberd(e, Xs), memberd(e, Xs).
Xs = [e, _A]
; Xs = [_A, e], dif(_A, e)
; false.
如果您只想查看那些允许冗余的答案,您可以改为:
?- Xs = [_,_], member(e, Xs), \+ \+ call_nth(member(e, Xs), 2).
Xs = [e, _A]
; Xs = [_A, e].
换句话说,member/2 的所有答案都允许这种冗余。请注意,member/2 并不总是容易出现冗余。特别是,如果列表包含不同的(不可统一的)元素,则根本没有冗余。这是一个常见的用例。
?- Xs = [a,b], member(X, Xs), \+ \+call_nth(member(X, Xs),2).
false.
事实上,在这种情况下,也就是在查询X 时,member/2 可能比memberd/2 更有效。