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_Recursion_Prolog - Fatal编程技术网

List Prolog:递归函数分支&&引用;“返回”;

List Prolog:递归函数分支&&引用;“返回”;,list,recursion,prolog,List,Recursion,Prolog,我写了几个递归的Prolog谓词,遇到了一个我目前不太理解的问题 例如,我编写了谓词split/3,它将整数列表分为非负整数列表和负整数列表: 第1版 split([], [], []). split([ListHead|ListTail], [ListHead|PList], NList) :- ListHead >= 0, split(ListTail, PList, NList). split([ListHead|ListTail], PList, [ListHead|

我写了几个递归的Prolog谓词,遇到了一个我目前不太理解的问题

例如,我编写了谓词
split/3
,它将整数列表分为非负整数列表和负整数列表:

第1版

split([], [], []).
split([ListHead|ListTail], [ListHead|PList], NList) :- 
   ListHead >= 0,
   split(ListTail, PList, NList).
split([ListHead|ListTail], PList, [ListHead|NList]) :- 
   ListHead < 0,
   split(ListTail, PList, NList).
split([]、[]、[])。
拆分([ListHead | ListTail],[ListHead | PList],NList):-
列表头>=0,
拆分(列表尾、PList、NList)。
拆分([ListHead | ListTail],PList,[ListHead | NList]):
列表头<0,
拆分(列表尾、PList、NList)。
但在得出该解决方案之前,我将该解决方案写在下面,并思考了一会儿为什么它不起作用:

第2版

split([], [], []).
split([ListHead|ListTail], PList, NList) :- 
   ListHead >= 0,
   split(ListTail, [ListHead|PList], NList).
split([ListHead|ListTail], PList, NList) :- 
   ListHead < 0,
   split(ListTail, PList, [ListHead|NList]).
split([]、[]、[])。
拆分([ListHead | ListTail]、PList、NList):-
列表头>=0,
拆分(列表尾,[列表头| PList],NList)。
拆分([ListHead | ListTail]、PList、NList):-
列表头<0,
拆分(列表尾、列表头、[ListHead | NList])。
其中:

  • 第一个给定的参数分为
    ListHead
    ListTail
  • 如果
    ListHead
    元素(integer)大于或等于0,则它将被添加到非负整数列表中,并使用一个参数作为递归调用的参数,该参数带有一个未经处理的
    Nlist
  • 如果
    ListHead
    元素(integer)小于0,则它被添加到负整数列表的前面,并用作带有未填充
    PList
    的递归调用的参数
我不明白为什么版本2不起作用;它编译时没有任何警告,但只返回false。与上述版本的唯一区别在于,整数元素对
Nlist
PList
的前置是在谓词定义中完成的(在
:-
运算符之后),而不是在谓词调用的参数中完成的。对我来说,将结果作为下一次调用的参数的一部分进行预处理是有意义的

我觉得我错过了Prolog递归地“搜索”调用的方法


有人能解释一下为什么版本1能按预期工作,而版本2不能吗?

它不能工作,因为在回溯过程中返回时,您正在丢失元素

在版本1中,PList用[]实例化。当返回开始时,您开始像这样堆叠元素:

[ListHead|PList]    the same as   [ListHead| [] ] at first level.
split([],A,B,A,B).
split([ListHead|ListTail], PList, NList, PListAcum, NListAcum) :- ListHead >= 0, 
    split(ListTail, PList, NList, [ListHead|PListAcum], NListAcum).
split([ListHead|ListTail], PList, NList, PListAcum, NListAcum) :- ListHead < 0, 
    split(ListTail, PList, NList,  PListAcum, [ListHead|NListAcum]).
split([1,2,3,-1,-2,-3] , P, N, [], []);
最后你有了所有的清单

在版本2中,Plist保持未实例化,并且切割条件永远不会满足,因为您有如下情况:

[[]|1,2,3,4,5,6]
和任何东西都不匹配

在版本2中,需要使用累加器(或辅助变量),最后需要将累加器复制到实变量中,如下所示:

[ListHead|PList]    the same as   [ListHead| [] ] at first level.
split([],A,B,A,B).
split([ListHead|ListTail], PList, NList, PListAcum, NListAcum) :- ListHead >= 0, 
    split(ListTail, PList, NList, [ListHead|PListAcum], NListAcum).
split([ListHead|ListTail], PList, NList, PListAcum, NListAcum) :- ListHead < 0, 
    split(ListTail, PList, NList,  PListAcum, [ListHead|NListAcum]).
split([1,2,3,-1,-2,-3] , P, N, [], []);
让我解释一下,累加器是初始化的,它们会累加您的数据。当列表为空时,第一行仅将累加器复制到实变量中,然后累加器通过递归丢失其元素(如果查看不同回溯级别上的变量名称,您就会理解),但实变量在回溯过程中保持不变

对于要返回结果的每个变量,都需要一个累加器,或者像第一个版本那样执行

您可以搜索有关蓄能器的信息


欢迎。

模式匹配它是Prolog的一个关键功能,占语言功能的一半,并与回溯一起使用,允许以优雅但不同寻常的方式表达控制。下面是如何“纠正”第二个版本

split([],[],[]).
split([ListHead|ListTail], PList, NList) :-
    ListHead >= 0, split(ListTail, Temp, NList), PList=[ListHead|Temp].
split([ListHead|ListTail], PList, NList) :-
    ListHead < 0, split(ListTail, PList, Temp), NList=[ListHead|Temp].
split([]、[]、[])。
拆分([ListHead | ListTail]、PList、NList):-
ListHead>=0,拆分(ListTail,Temp,NList),PList=[ListHead | Temp]。
拆分([ListHead | ListTail]、PList、NList):-
ListHead<0,拆分(ListTail,PList,Temp),NList=[ListHead | Temp]。

当然,问题是基本情况无法与原始版本匹配。

如果使用较短的变量名重新编写代码,并将规则头分配移到规则体中,则可能更易于阅读

我们将假设它是用一个给定的列表调用的,以生成它的两部分,一部分是非负元素(我们将它们简称为“正”,但包括0),另一部分是负元素

第1版内容如下:

split([],[],[]).
split(X, Y, Z) :-        X = [A|B], Y = [A|POS], Z = NEG,
    A >= 0, split(B, POS, NEG).
split(X, Y, Z) :-        X = [A|B], Y = POS, Z = [A|NEG],
    A <  0, split(B, POS, NEG).
split([],[],[]).
split(X, Y, Z) :-        X = [A|B], Y = POS, Z = NEG,
    A >= 0, split(B, [A|POS], NEG).
split(X, Y, Z) :-        X = [A|B], Y = POS, Z = NEG,
    A <  0, split(B, POS, [A|NEG]).
  • 一个空列表拆分为两个空列表
  • 为了将一个列表
    X
    与头
    a
    分开,我们将它的尾部
    B
    分成两个列表,正面列表和负面列表,我们要求
    B
    正面的头与
    a
    相同(这是不太可能的);然后,我们只将
    B
    的正数(即
    POS
    )的尾部返回为
    X
    的正数(即短一个元素…?)
  • 与负头元素类似
我想你会发现这毫无意义


这里没有回溯,因为所有规则的条款都是互斥的(保证回溯失败)。

非常感谢您的详细解释,现在就有意义了!第一个版本显然更整洁(我应该坚持使用这个版本),但要知道,累加器可以用来解决第二个版本的问题。再次感谢!感谢您的帮助-我将上面的一个标记为答案,因为其他细节可能会有所帮助,但您也清楚地发现了问题。