Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List Prolog如何将两个列表打印为一个列表,而不使用任何附加代码?_List_Prolog_Union_Member - Fatal编程技术网

List Prolog如何将两个列表打印为一个列表,而不使用任何附加代码?

List Prolog如何将两个列表打印为一个列表,而不使用任何附加代码?,list,prolog,union,member,List,Prolog,Union,Member,我有以下代码,这显然是显示两个列表之间的并集的标准方式: union([Head|Tail],List2,Result) :- member(Head,List2), union(Tail,List2,Result). union([Head|Tail],List2,[Head|Result]) :- \+ member(Head,List2), union(Tail,List2,Result). union([],List2,List2). 并根据以下输入: union

我有以下代码,这显然是显示两个列表之间的并集的标准方式:

union([Head|Tail],List2,Result) :- 
    member(Head,List2),  union(Tail,List2,Result).
union([Head|Tail],List2,[Head|Result]) :- 
    \+ member(Head,List2), union(Tail,List2,Result).
union([],List2,List2).
并根据以下输入:

union([a,b,c,d,2,3], [b,c,3,99], Result).
将给我以下输出:

Result = [a,d,2,b,c,3,99] ? 

yes
我的问题是,prolog是如何做到这一点的?List2从来不会通过递归调用进行更改,但在最后,它会打印出构成两个原始列表之间的并集的所有元素

请帮我理解这个代码


谢谢。

假设您询问union([1,2],[2],R)

根据第一条规则,如果 成员(1,[2])-->false 然后prolog将检查第二个规则联合([1 |[2]],[2],[1 | R])是否为真,如果 +成员(1,[2])-->true 和并集([2],[2],R)

现在,如果 成员(2,[2])-->true 和联合([],[2],R)

如果R=[2],则联合([],[2],R)将为真(第三条规则)

因此R=[2],因此对union的第一次调用返回[1 |[2]]=[1,2]

跟踪/0是了解“prolog是如何工作的”的一个有用工具:

    2 ?- trace.
true.

[trace] 2 ?- union([1,2],[2],R).
   Call: (6) union([1, 2], [2], _G543) ? creep
   Call: (7) lists:member(1, [2]) ? creep
   Fail: (7) lists:member(1, [2]) ? creep
   Redo: (6) union([1, 2], [2], _G543) ? creep
   Call: (7) lists:member(1, [2]) ? creep
   Fail: (7) lists:member(1, [2]) ? creep
   Call: (7) union([2], [2], _G619) ? creep
   Call: (8) lists:member(2, [2]) ? creep
   Exit: (8) lists:member(2, [2]) ? creep
   Call: (8) union([], [2], _G619) ? creep
   Exit: (8) union([], [2], [2]) ? creep
   Exit: (7) union([2], [2], [2]) ? creep
   Exit: (6) union([1, 2], [2], [1, 2]) ? creep
R = [1, 2] .

总而言之:List2没有改变,但是谓词也没有返回List2;它返回由List2创建的列表和List1的唯一元素。这里使用的算法如下:

1) 将结果初始化为
List2
。本部分的实施得益于:

union([], List2, List2).
2) 浏览
列表1
并对每个项目执行以下操作:
2a)如果项目不在列表2中,则将其添加到结果中。该部分的实施得益于本条款:

union([Head|Tail], List2, [Head|Result]) :- 
    \+ member(Head, List2),
    union(Tail, List2, Result).
union([Head|Tail], List2, Result) :-
    member(Head, List2),
    union(Tail, List2, Result).
2b)如果项目在清单2中,则不要做任何事情。该部分的实施得益于本条款:

union([Head|Tail], List2, [Head|Result]) :- 
    \+ member(Head, List2),
    union(Tail, List2, Result).
union([Head|Tail], List2, Result) :-
    member(Head, List2),
    union(Tail, List2, Result).
对于序言执行的逐步理解,请参考@thanosQR答案


顺便说一句,注意这个谓词需要集合来返回一个好的并集,否则,
List1
中的一个副本将在
Result
中保持一个副本(在
List2
中的一个副本也是如此)。

这段代码实际上效率很低,所以我们不能假设它是计算并集的“标准方法”。乍一看,有两个简单的优化:避免重复成员资格测试,使用memberchk/2而不是member/2。因此,我们可以用以下方式重写它:

union([Head|Tail], List2, ResultT) :-
    (  memberchk(Head, List2)
    -> ResultT = Result
    ;  ResultT = [Head|Result] ),
    union(Tail, List2, Result).
union([],List2,List2).
性能上的差异是巨大的。相对较小的列表:

...
numlist(1, 10000, A),
numlist(5000, 10000, B),
union(A, B, C),
...
我们将62532499个推论传递到20002个推论,并且测试不会强制评估所有备选方案(来自成员的回溯点):加上这一点,我们还需要25015004个推论,尽管没有更多的解决方案可用。这里,SWI Prolog中的代码列出了库:


它与您的版本类似,请注意,当程序到达最基本的递归调用时,cut(!)

so:union([],List2,List2)。它将开始向上跳转,将列表2向上发送到:union([Head | Tail],List2,[Head | Result]),第三个参数中的[Head | Result]将使用列表2(Result)构造Head?然后继续构建结果,在每个递归级别的列表向上移动时,取列表的开头?是的,这就是它的工作方式,它从结尾构建结果(正如你所期望的那样,使用Head | Tail范例)。因此,为了实现这一点,“函数调用”联合中的“变量”([Head | Tail],List2,[Head | result]),必须从左到右初始化。首先,我们有一个列表,它被分解成两个varaibels头和尾,然后我们有一个名为list 2的变量,然后我们有第三个变量,它是由于Head已经存在而构造的。如果最后一个变量中不存在Head,会发生什么?progol如何区分第一个变量是列表分解变量和第三个变量是构造列表变量?prolog只是试图使谓词为真。这意味着,如果一个列表是一个自由变量(比如你的结果列表),prolog将尝试使它适合一种允许谓词为true的格式。在本例中,通过将其初始化为List2并按照前面的详细说明构造它……这是一种非常奇怪和混乱的语言。哇,我甚至不知道在prolog中可以有变量和赋值。我得研究一下你找到工会的新方法。它看起来比我最初做的方式简单。在序言中,“赋值”被称为统一,这是一个与“逻辑”变量相关的概念。它不是赋值,而是,把它看作模式匹配:)