我真的不明白Prolog中的递归是如何工作的。假设您有以下两个功能:
my_length([],0).
my_length([_|T], Length) :-
my_length(T, T_length),
Length is T_length + 1.
my_nth0(Position, [Element|_], Element, Position).
my_nth0(Position, [_|Tail], X, CurrentPos) :-
NextPos is CurrentPos + 1,
my_nth0(Position, Tail, X, NextPos).
我理解my_nth0
:每次递归调用函数时,都会将NextPos
增加1.但是,我不会低估my_length
。我这样写:
my_length([],0).
my_length([_|T], Length) :-
T_Length is Length + 1.,
my_length(T, T_length).
这似乎与my_nth0
的风格相同。但是,第一个看似正确的函数实现恰恰相反(它基本上就是T_length is Length - 1
)。为什么这样做?
答案 0 :(得分:2)
阅读my_length/2
从右到左的第二个条款!
如果
T
的长度为T_length
然后
[_|T]
的长度为T_length+1
。
答案 1 :(得分:1)
您是否真的尝试过my_length/2
版本,看看会发生什么?修复语法错误(:)后,如果您查询my_length([1,2,3], N).
,则会收到“变量未实例化的错误”,因为它会在T_Length is Length + 1
没有问题时尝试评估Length
有一个价值。因此,如果您使用is/2
,则递归调用必须首先使Length
具有递归调用的值,如第一个示例所示。
此外,在您的示例中,您尝试通过T_Length is Length + 1
增加变量,但您的递归基本案例的最终长度为0
。你不能将长度“增长”到0
。
my_length([], 0). % Base case is length = 0
my_length([_|T], Length) :-
% Length is UNINSTANTIATED on my_length([1,2,3], N) query
% so the following 'is/2' will fail to evaluate
T_Length is Length + 1,
my_length(T, T_length). % T_length > Length, but base case is 0!
或者,如果你使用了CLPFD并修正了长度与T_Length的评估顺序,你可以按照你要显示的顺序放置它:
my_length([], 0).
my_length([_|T], Length) :-
% The following defines an arithmetic 'relation' between Length and T_Length
% and is permitted as a constraint
Length #= T_Length + 1,
my_length(T, T_length).
您还可以使用累加器来获取尾部递归,以便确实可以增加长度值,直到它到达结尾:
my_length(L, N) :-
my_length(L, 0, N).
my_length([], N, N). % Reached the length
my_length([_|T], A, N) :- % Here, `A` is instantiated by design
A1 is A + 1, % So this is/2 expression will evaluate
my_length(T, A1, N).