在Prolog中设置并集操作:说明

在Prolog中设置并集操作:说明,prolog,Prolog,我能够理解它正在遍历第一个列表,并根据元素是否是第二个列表的成员进行添加。我明白了逻辑。然而,工作流对我来说很神秘,它在第一个列表用尽后将“第二个列表”的元素添加到结果中 请有人举一个简单的例子,比如联合([1,2],[2,3],结果),并解释工作流程 我假设您正在调用union/3,并实例化了第一个和第二个参数。第三个参数可以在调用时未实例化,并在返回时与两个列表的并集统一;或者,如果已经实例化,则可以使用它来检查它是否匹配前两个列表的(有序)并集 第一个子句说明,如果第二个参数是空列表,并且

我能够理解它正在遍历第一个列表,并根据元素是否是第二个列表的成员进行添加。我明白了逻辑。然而,工作流对我来说很神秘,它在第一个列表用尽后将“第二个列表”的元素添加到结果中


请有人举一个简单的例子,比如
联合([1,2],[2,3],结果)
,并解释工作流程

我假设您正在调用union/3,并实例化了第一个和第二个参数。第三个参数可以在调用时未实例化,并在返回时与两个列表的并集统一;或者,如果已经实例化,则可以使用它来检查它是否匹配前两个列表的(有序)并集

第一个子句说明,如果第二个参数是空列表,并且第一个列表至少有一个元素,那么union就是第一个列表。 同样,第二个子句声明,如果第一个参数是空列表,而第二个列表至少有一个元素,那么union就是这第二个列表

第三个子句在第一个列表上递归,并检查第二个列表以查看该项是否已经存在。在这种情况下,它只是用第一个列表的尾部调用自己

第四个子句测试第一个列表的头部,以检查它是否不包含在第二个列表中,并使用尾部递归调用(就像第三个子句一样)。但是,在返回递归时,它会将该项添加到第三个列表的头部,从而将该项添加到联合中

请注意,在您的实现中,两个空集的并集总是会失败。可以通过修改第一个或第二个子句以允许空列表,或者针对这种情况修改add Other子句来解决此问题。例如

union([H|T],[],[H|T]).     
union([],[H|T],[H|T]).    
union([H|T], SET2, RESULT) :- member(H,SET2), union(T,SET2,RESULT).    
union([H|T], SET2, [H|RESULT]) :- not(member(H,SET2)), union(T,SET2,RESULT).
现在让我们看看调用
并集([1,2],[2,3],Result)
时会发生什么:

前两个子句将不匹配,因为它们都不是空列表

我们输入第三个子句并检查元素1是否不是第二个列表的成员,因此在那里失败

我们现在尝试第四个子句并测试元素1i不在第二个列表中,因此我们调用
联合([2],[2,3],Result)
,我们标记这个执行点(*1

同样,前两个条款将不匹配,因此我们进入第三个条款。这里我们测试元素2确实包含在第二个列表中,因此我们调用
联合([],[2,3],Result)
,我们标记这个执行点(*2

现在第一个子句失败,因为第一个参数是空列表。 现在我们进入第二个子句,将第三个参数与第二个列表([2,3])统一起来

此时,我们返回(*2),其中现在使用[2,3]实例化结果。这些子句到此结束,因此我们将第三个参数与[1,2,3]绑定,然后返回(*1

我们现在在(*1)中,结果和第三个参数被实例化为[1,2,3]

这给了我们第一个结果[1,2,3]

然而,当我们在(*2中获得成功时,留下了一个选择点,因此如果我们要求Prolog搜索另一个答案,它仍然必须尝试union的第四个子句([2],[2,3],Result)


因此,我们输入第四个子句来测试2是否不是[2,3]中失败的成员,因此Prolog将告诉我们没有其他答案。

您没有检查
SET2
,因此假设其中没有重复项,那么基递归可以是单个谓词

union([],[],[]).
@gusbro已经解释过,当
H
不属于SET2时,在递归调用成功后,它被放在结果的(本地)前面

该解释应该可以消除您的疑虑,但请注意,
not
/1基本上重复了上一次(失败)调用中已执行的相同测试。那么就可以使用更好的代码了

union([], U, U).
这相当于您的代码,假设
成员
/2没有副作用(确实如此)

not
/1可以使用
实现/0。

在回答一个相关问题“我提供了一个逻辑上纯粹的交叉和并集实现


与你问题的其他答案不同,纯变体是单调的,因此当与非基本术语一起使用时,在逻辑上保持合理。

。。。只“读一次”就够了。。你真是一位伟大的老师。非常感谢:)谢谢你的编辑。。这使它更清楚了1.谢谢你的答复。。我想我明白你指的把戏了。。它很有帮助,帮助我理解了“!”的用例。。但是,检查(至少注释掉)可以提高可读性。。
union([H|T], SET2, RESULT) :-
   member(H,SET2), !, % note the commit
   union(T,SET2,RESULT).    
union([H|T], SET2, [H|RESULT]) :- 
   % useless now 
   % not(member(H,SET2)),
   union(T,SET2,RESULT).