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).
Run Code Online (Sandbox Code Playgroud)
我能够理解它是遍历第一个列表并根据元素是否是第二个列表的成员添加.我得到了逻辑.但是,工作流程对我来说是神秘的,它会在第一个列表耗尽后将"第二个列表"的元素添加到结果中.
请有人可以举一个简单的例子来union([1,2], [2,3], Result)
解释工作流程.
我假设您正在调用union/3,并实例化第一个和第二个参数.第三个参数可以在调用时未实例化,并且将在返回时与两个列表的并集统一,或者如果已经实例化,则可以使用它来检查它是否与前两个列表的(有序)并集匹配.
第一个子句指出,如果第二个参数是空列表,并且第一个列表至少有一个元素,那么union就是第一个列表.同样,第二个子句声明如果第一个参数是空列表而第二个列表至少有一个元素,那么union就是第二个列表.
第三个子句递归第一个列表并检查第二个列表以查看该项是否已存在.在这种情况下,它只是用第一个列表的尾部调用自己.
第四个子句测试第一个列表的头部,检查它是否包含在第二个列表中,并用尾部递归调用(就像第三个子句一样).但是在返回递归时,它将项添加到第三个列表的头部,从而将项添加到联合.
请注意,在您的实现中,两个空集的并集始终会失败.您可以通过修改第一个或第二个子句以允许空列表或为此案例添加另一个子句来解决此问题.例如
union([],[],[]).
Run Code Online (Sandbox Code Playgroud)
现在让我们看看当我们打电话时会发生什么union([1,2],[2,3], Result)
:
前两个子句将无法匹配,因为它们都不是空列表.
我们输入第三个子句并检查元素1是否不是第二个列表的成员,从而在那里失败.
我们现在尝试第四个子句并测试元素1我不在第二个列表中,所以我们调用union([2], [2,3], Result)
,我们标记这个执行点(*1).
同样,这两个第一个子句将无法匹配,因此我们输入第三个子句.这里我们测试确实元素2包含在第二个列表中,所以我们调用union([], [2,3], Result)
,我们标记这个执行点(*2)
现在第一个子句失败,因为第一个参数是空列表.我们现在输入第二个子句,用第二个列表([2,3])统一第三个参数.
此时我们返回到(*2)现在结果用[2,3]实例化了.这个子句在那里结束,所以我们用[1,2,3]绑定第三个参数,然后返回(*1).
我们现在在(*1),其中Result和第三个参数用[1,2,3]实例化.
这给了我们第一个结果[1,2,3].
然而,当我们在(*2)中成功时,我们选择了一个选择点,所以如果我们要求Prolog寻找另一个答案,它仍然必须尝试联合的第四个条款([2],[2,3],结果).
所以我们输入第四个子句来测试2是不是[2,3]的成员而失败了,所以Prolog会告诉我们没有其他的答案.