[英]Prolog : Recursive Function Branching & “Returning”
I was writing few recursive Prolog predicates and ran into a certain point that I don't quite understand at the moment. 我写了一些递归的Prolog谓词,但遇到了我目前不太了解的某个问题。
For example, I wrote the predicate split/3
that divides a list of integers into a list of non-negative integers and a list of negative integers: 例如,我写了谓词split/3
,将整数列表分为非负整数列表和负整数列表:
Version 1 版本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).
But before arriving at that solution, I wrote the solution below and wondered for a while why it wasn't working: 但是在获得该解决方案之前,我在下面编写了解决方案,想知道为什么它不起作用:
Version 2 版本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]).
where: 哪里:
ListHead
and ListTail
. 给定的第一个参数分为ListHead
和ListTail
。 ListHead
element (integer) is greater than or equal to 0, it's prepended to the list of non-negative integers, and used an argument for the recursive call with an unmanipulated Nlist
. 如果ListHead
元素(整数)大于或等于0,则将其放在非负整数列表的前面,并使用带有参数的Nlist
进行递归调用。 ListHead
element (integer) is less than 0, it's prepended to the list of negative integers and used as an argument for the recursive call with an unmanipulated PList
. 如果ListHead
元素(整数)小于0,则它会放在负整数列表的前面,并用作未操纵的PList
的递归调用的参数。 I'm not getting why version 2 doesn't work; 我不明白为什么版本2无法正常工作; it compiles without any warnings, but only ever returns false. 它编译时不会发出任何警告,但只会返回false。 The only difference with the above version is that the prepending of integer elements to Nlist
or PList
is done within the predicate definition (after the :-
operator), and not in the parameters for the predicate call. 与上述版本的唯一区别在于,将整数元素添加到Nlist
或PList
是在谓词定义内(在:-
运算符之后),而不是在谓词调用的参数中完成。 For me, it makes sense to prepend the result as part of the argument for the next call... 对我来说,将结果作为下一个调用的参数的一部分放在前面是很有意义的...
I feel like I'm missing something about Prolog's way of "searching" recursively calls! 我感觉好像缺少有关Prolog的“搜索”递归调用方式的某些东西!
Could someone explain why version 1 works as intended whereas version 2 does not? 有人可以解释为什么版本1可以按预期工作而版本2不能按预期工作吗?
It does not work because you are loosing the elements when you go back in the backtracking. 它不起作用,因为在回溯中您失去了元素。
In version 1, PList is instantiated with [] when the returns begins, you start to stack elements like this: 在版本1中,当返回开始时,PList用[]实例化,您开始像这样堆叠元素:
[ListHead|PList] the same as [ListHead| [] ] at first level.
At the end you have all the list. 最后,您拥有所有列表。
In version 2, Plist remains uninstantiated and cutting condition never satisfy, because you have something like: 在版本2中,Plist仍未实例化,并且切割条件永远无法满足,因为您具有以下内容:
[[]|1,2,3,4,5,6]
and don't match with anything. 而且什么都不配
In version 2 you need to use accumulators (or auxiliary variables) at the end you need to copy the accumulators into real variables like this: 在版本2中,最后需要使用累加器(或辅助变量),您需要将累加器复制到真实变量中,如下所示:
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]).
You call that like this: 您这样称呼:
split([1,2,3,-1,-2,-3] , P, N, [], []);
Let me explain that, the accumulator are initialized and they accumulate your data. 让我解释一下,累加器已初始化,它们会累加您的数据。 The first line only copy the accumulators into the real variables when the list is empty an then the accumulators lost its elements back y recursion (you'll understand if you look at the names of the variables on different backtracking levels) but the real variables remain unchanged through the backtracking. 第一行仅在列表为空时将累加器复制到真实变量中,然后累加器将其元素丢失并递归返回(如果您查看不同回溯级别的变量名称,您将了解到),但是真实变量仍然存在通过回溯保持不变。
You'll need an accumulator for each variable you want to return as a result, or do it like your first version. 对于要作为结果返回的每个变量,您将需要一个累加器,或者像第一个版本一样使用累加器。
You can search for information about accumulators. 您可以搜索有关累加器的信息。
Greets. 问候
Pattern matching it's a key feature of Prolog, accounting for half of the power of the language, and plays together to backtracking, allowing to express control in a elegant, but unusual, way. 模式匹配是Prolog的主要功能,占语言能力的一半,并且可以一起回溯,从而以一种优雅但不寻常的方式来表达控制。 Here is how you could 'correct' the second version 这是您可以“更正”第二个版本的方法
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].
Of course the problem it's that the base case cannot be matched with your original version. 当然,问题在于基本案例无法与您的原始版本匹配。
Re-writing your code with shorter variable names and rule head assignments moved into rule body, it might be easier to read. 用较短的变量名和规则头分配移入规则主体重新编写代码,这可能更易于阅读。
We'll assume it is called with a given list, to produce its two halves, one of its non-negatives (we'll call them "positives", for short, but 0 is included), and another, of its negative elements. 我们假设用给定的列表调用它,以产生它的两半,其中一个是非负数(我们称它们为“正数”,简称,但包括0),而另一个则是它的负数。
Version 1 reads, 第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).
X
with head A
which is non-negative, we split its tail B
into two lists, list of positives POS
and list of negatives NEG
, and (naturally) prepend A
to B
's positives to be returned as X
's positives; 分割一个列表X
与头A
其是非负的,我们拆分它的尾巴B
成两个列表,阳性列表POS
和底片的列表NEG
,和(自然)前置A
到B
的肯定作为要返回X
'正面 X
with head A
which is negative, we split its tail B
into two lists, list of positives POS
and list of negatives NEG
, and (naturally) prepend A
to B
's negatives, to be returned as X
's negatives. 要将列表X
的头A
设为负数,我们将其尾部B
分为两个列表,即正数POS
列表和负数NEG
列表,并且(自然地) A
置于B
的负数之前,以X
的形式返回底片。 Version 2 reads, 第2版内容如下:
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
with head A
which is non-negative, we split its tail B
into two lists, list of positives and list of negatives, and we demand that the head of B
's positives be the same as A
( which is most unlikely ); 分割一个列表X
与头A
其是非负的,我们拆分它的尾巴B
成两个列表,肯定和否定的列表的列表,我们要求的头部B
的阳性是相同的A
( 这是最不可能的 ); and then we return only the tail of B
's positives (ie POS
) as X
's positives ( ie one element shorter...?? ); 然后我们只返回B
的正数(即POS
)的尾部作为X
的正数( 即,短一个元素... ?? ); I think you can see that this makes no sense. 我认为您可以看到这没有任何意义。
There is no backtracking here , because all the rule's clauses are mutually exclusive (guaranteed to fail on backtracking). 这里没有回溯 ,因为所有规则的子句都是互斥的(保证在回溯时失败)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.