Prolog 中的子列表谓词
Posted
技术标签:
【中文标题】Prolog 中的子列表谓词【英文标题】:Sublist predicate in Prolog 【发布时间】:2017-08-14 15:31:27 【问题描述】:我正在尝试在 Prolog 中定义一个谓词 sublist(X,Y)
,当列表 X
的元素都出现在列表 Y
中时,该谓词为真,其顺序与它们在 X
中的顺序相同。
我的方法是取X
的头部,在Y
中找到头部的第一个实例,并在第一个实例之前剪切列表,然后用X
的尾部重复此操作,直到尾部等于空列表。
我已经编写了辅助谓词cut(X,Y,Z)
,如果Z
是在Y
列表中的第一个X
实例之前切割所有内容的结果列表,则为true。
我的代码如下:
cut(_,[],[_|_]) :- false.
cut(X,[H|T],[H|T]) :- X = H, !.
cut(X,[_|T],Y) :- cut(X,T,Y).
sublist([],[]).
sublist([],[_|_]).
sublist([A|B],C) :- cut(A,C,Z), sublist(B,Z).
当我查询时
?- sublist(L,[1,2,3]).
Prolog 没有给出[1,2,3]
的所有子列表,而是给出了以下输出:
L = [] ;
L = [1] ;
L = [1, 1] ;
L = [1, 1, 1] ;
L = [1, 1, 1, 1] ;
L = [1, 1, 1, 1, 1] ;
L = [1, 1, 1, 1, 1, 1] ;
L = [1, 1, 1, 1, 1, 1, 1] ;
L = [1, 1, 1, 1, 1, 1, 1, 1] ;
L = [1, 1, 1, 1, 1, 1, 1, 1, 1] ;
L = [1, 1, 1, 1, 1, 1, 1, 1, 1|...] ;
L = [1, 1, 1, 1, 1, 1, 1, 1, 1|...] ;
L = [1, 1, 1, 1, 1, 1, 1, 1, 1|...]
我看不到我的错误。谁能给我指点一下?
【问题讨论】:
你不需要规则cut(_,[],[_|_]) :- false.
只是它的缺失会导致看起来像 cut(_,[],[_|_]).
的查询失败,因为它与后续规则不匹配。你试过trace
吗?或者您是否尝试仅测试cut/3
本身?您创建 cut/3
而不仅仅是使用 select/3
是否有原因?
【参考方案1】:
简答:cut/3
如果找到元素,不会从列表中弹出。
第一个答案L = [].
是第二个子句触发的结果。现在下一个答案由最后一个子句生成。那么会发生什么:
sublist(A,[1,2,3]) :- % C = [1,2,3]
cut([A|B],[1,2,3],[H|T]) :- % H = 1, T = [2,3], Z = [1,2,3]
A = H, % A=1
!.
sublist(B,[1,2,3]).
所以由于cut/3
的第二个子句触发,第二个和第三个参数是等效的,因此cut/3
不会从列表中“弹出”。
解决这个问题并保证进度的方法正在从列表中弹出,其中:
cut(_,[],[_|_]) :- false.
cut(H,[H|T],T) :-
!.
cut(X,[_|T],Y) :-
cut(X,T,Y).
(我还冒昧地将子句头部的X
替换为H
,使其更加优雅)。现在它生成:
?- sublist(L,[1,2,3]).
L = [] ;
L = [1] ;
L = [1, 2] ;
L = [1, 2, 3] ;
false.
请注意,答案不会生成所有列表:例如L = [2,3]
也是有效答案。这不起作用的原因是因为cut/3
具有!
,因此不允许引入其他元素。您可以通过以下方式解决此问题:
cut(_,[],[_|_]) :- false.
cut(H,[H|T],T).
cut(X,[_|T],Y) :-
cut(X,T,Y).
现在我们得到:
?- sublist(L,[1,2,3]).
L = [] ;
L = [1] ;
L = [1, 2] ;
L = [1, 2, 3] ;
L = [1, 3] ;
L = [2] ;
L = [2, 3] ;
L = [3] ;
false.
尽管如此,您完全可以在不使用帮助谓词的情况下非常优雅地解决问题。我们可以首先构造一个验证/创建列表的谓词:
list([]).
list([_|T]) :-
list(T).
接下来我们声明如果X
是空的,你并不关心右边是什么类型的列表:
sublist([],L) :-
list(L).
最后如果X
不为空,我们需要在某处获取HX
(X
的头部)。如果我们找到它,我们可以决定“接受”它,然后弹出两个列表,或者我们可以决定寻找另一个HX
,例如:
sublist([HX|TX],[HX|TY]) :-
sublist(TX,TY).
sublist(X,[_|TY]) :-
X = [_|_],
sublist(X,TY).
最后一个子句中的X = [_|_]
不是绝对必要的,但可以防止使用sublist([],X)
子句生成重复的结果。
或者把它放在一起:
list([]).
list([_|T]) :-
list(T).
sublist([],L) :-
list(L).
sublist([HX|TX],[HX|TY]) :-
sublist(TX,TY).
sublist(X,[_|TY]) :-
X = [_|_],
sublist(X,TY).
这会生成:
?- sublist(L,[1,2,3]).
L = [] ;
L = [1] ;
L = [1, 2] ;
L = [1, 2, 3] ;
L = [1, 3] ;
L = [2] ;
L = [2, 3] ;
L = [3] ;
false.
或者:
?- sublist([1,2,3],L).
L = [1, 2, 3] ;
L = [1, 2, 3, _G2440] ;
L = [1, 2, 3, _G2440, _G2443] ;
L = [1, 2, 3, _G2440, _G2443, _G2446] ;
L = [1, 2, 3, _G2440, _G2443, _G2446, _G2449] ;
L = [1, 2, 3, _G2440, _G2443, _G2446, _G2449, _G2452] ;
L = [1, 2, 3, _G2440, _G2443, _G2446, _G2449, _G2452, _G2455] ;
L = [1, 2, 3, _G2440, _G2443, _G2446, _G2449, _G2452, _G2455|...] ;
或者:
?- sublist([1,2,3],[1,2,4,5,3,6]).
true ;
【讨论】:
您不需要规则cut(_,[],[_|_]) :- false.
。这是多余的,因为cut(_,[],[_|_])
将由于缺少成功/匹配规则而失败。
@lurker:其实根本不需要cut
。目前我正在写一个更优雅的方法来解决这个问题。
对。但是如果你想定义这样一个cut/3
,你就不需要那个子句了。此外,在 ISO Prolog 中已经定义了 select/3
。 :) 但是,您确实在他们的谓词中指出了 OP 的错误,这就是他们所要求的。 :)以上是关于Prolog 中的子列表谓词的主要内容,如果未能解决你的问题,请参考以下文章