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不为空,我们需要在某处获取HXX的头部)。如果我们找到它,我们可以决定“接受”它,然后弹出两个列表,或者我们可以决定寻找另一个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 中的子列表谓词的主要内容,如果未能解决你的问题,请参考以下文章

同时应用谓词来过滤列表(SWI Prolog)

谓词用于返回prolog中给定列表的元素顺序的排列

如何通过Prolog将字符串连接到列表中的多个元素?

Prolog 中的返回值

Prolog 中的谓词控制

Prolog 中的“行为良好的谓词”是啥?