我正在尝试将列表拆分为特定元素(特别是“停止”一词)之前的项目以及该元素之后的项目。我知道你可以使用 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)
使用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)
事实上,在这种情况下,也就是在查询 时X
,member/2
可能比 更有效memberd/2
。