首页 > 解决方案 > Prolog:如何从列表中删除子列表?(后续问题)

问题描述

Prolog 的新手,不太了解 Prolog 中的递归,所以我不确定如何取出已经考虑过的列表中的元素。

这是我上一个问题的后续问题: Prolog:您如何在两个列表之间进行迭代(嵌套 for 循环)?

我有两个清单:

stringList([hello, hi], [bye,later], X).

函数 stringList 如下所示:

stringList(As, Bs, [A,B]) :-
  member(A, As),
  member(B, Bs).

这会产生输出:

X = [hi, bye] ;
X = [hi, later] ;
X = [hello, bye] ;
X = [hello, later].

现在我想从两个列表中删除子列表。例如,如果X = [hi, later],我想拿出你好以后等我的下一个X = [hello, bye]。当我的列表很大时,这变得更加重要。

我知道我需要递归,所以我创建了基本案例:

stringList([],[],[]).

标签: listprolog

解决方案


一种方法是使用findall/3delete/3谓词。目前您stringList/3一次生成一个解决方案,因此您需要将所有这些解决方案收集在一个列表中findall/3,然后删除您想要的任何内容:

deleteSublist(As, Bs, Elem, L):- 
       findall([X,Y], stringList(As, Bs, [X,Y]), L1),
       delete(L1, Elem, L).

例子:

?- deleteSublist([hello, hi], [bye,later], [hi,_], L).
L = [[hello, bye], [hello, later]].

在上面的示例中,您要删除的元素可能只是,[hi,bye]但我们[hi,_]用来删除所有出现的 hi。

现在要回答您的问题,这可以通过递归轻松完成,但是为了使用递归,您需要一个列表中的所有解决方案,以便稍后您可以递归地从该列表中提取元素。上述解决方案中的递归部分隐藏在内置的delete/3. 因此,如果您想使用自己的递归解决方案,只需编写自己的递归定义delete/3(我认为这必须已经在堆栈溢出中询问,因此您可以搜索它或用谷歌搜索它......)

另一件事,如果您希望您的解决方案像谓词一样一次生成一个解决方案,stringList/3您也可以使用下面的这种简单方法:

?- stringList([hello, hi], [bye,later], X),(X \= [hi,_]).
X = [hello, bye] ;
X = [hello, later] ;
false.

在上面我们利用了\=失败的优势,例如比较[hi,bye] \= [hi,_],如果你使用纯,dif/2这将成功:

?- stringList([hello, hi], [bye,later], X),dif(X,[hi,_]).
X = [hello, bye] ;
X = [hello, later] ;
X = [hi, bye] ;
X = [hi, later].

所以使用 dif/2 你需要更详细的限制:

?- stringList([hello, hi], [bye,later], X),dif(X,[hi,bye]), dif(X,[hi,later]).
X = [hello, bye] ;
X = [hello, later] ;
false.

推荐阅读