在序言中的特定元素之前和之后拆分列表(不使用“拆分”谓词?)

Car*_*rol 2 split list prolog

我正在尝试将列表拆分为特定元素(特别是“停止”一词)之前的项目以及该元素之后的项目。我知道你可以使用 split 来做到这一点,但我是 prolog 的新手,所以我正试图在不使用这些函数的情况下操作东西,所以我真的很想知道这是否可能?(也许还有一些指向正确方向的指针)

即与列表;

L = [tea,coffee,sugar,cake,stop,meat,fish,eggs,flour]
Run Code Online (Sandbox Code Playgroud)

理想情况下,我想在“停止”处拆分列表,让我留下,

L2 = [tea, coffee, sugar, cake] // and
L3 = [meat, fish, eggs, flour]
Run Code Online (Sandbox Code Playgroud)

fal*_*lse 5

使用seq//1

list_splitonstop(Xs, As, Bs) :-
   phrase( ( seq(As), [stop], seq(Bs) ), Xs).
Run Code Online (Sandbox Code Playgroud)

此版本按您的预期工作:

?- 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.
Run Code Online (Sandbox Code Playgroud)

但是,这真的是最好的解决方案吗?这; 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]
...
Run Code Online (Sandbox Code Playgroud)

看看每个答案!我们以第三个为例。L = [stop,_A,_B]意味着此答案包括具有三个元素的所有列表,其中第一个元素是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.
Run Code Online (Sandbox Code Playgroud)

因此,我们可以在单个查询中询问长度为 3 的所有相关情况。请注意,这些_A_B变量代表任何术语!请花点时间欣赏一下您所看到的内容:长度为 3 的列表的所有案例。没有其他案例需要考虑!

当您查看这些答案时,可能会出现一些问题。比如:这三个答案是重叠的,还是真的不相交?Prolog 知道答案。只需重复实际目标并计算结果答案:

| ?- L = [_,_,_], list_splitonstop(L, L1, L2), list_splitonstop(L, L1, L2).
(answers same as above)
Run Code Online (Sandbox Code Playgroud)

所以我们得到了完全相同的答案。没有固有的冗余。

另一个问题可能是:是否L总是有一个可能的分裂?(换句话说:是否存在函数依赖?)

我们可以通过要求L具有不同的L1和来实现这一点L2

| ?- 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.
Run Code Online (Sandbox Code Playgroud)

所以,我现在可以问你:你想要以上案例吗?如果多次出现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.
Run Code Online (Sandbox Code Playgroud)

如果您只想查看那些允许冗余的答案,您可以改为:

?- Xs = [_,_], member(e, Xs), \+ \+ call_nth(member(e, Xs), 2).
   Xs = [e, _A]
;  Xs = [_A, e].
Run Code Online (Sandbox Code Playgroud)

换句话说,所有的答案都member/2允许这种冗余。请注意,这member/2并不总是容易出现冗余。特别是,如果列表包含不同的(不可统一的)元素,则根本没有冗余。这是一个常见的用例。

?- Xs = [a,b], member(X, Xs), \+ \+call_nth(member(X, Xs),2).
false.
Run Code Online (Sandbox Code Playgroud)

事实上,在这种情况下,也就是在查询 时Xmember/2可能比 更有效memberd/2