我是Prolog的新手,并且在递归算法方面并不是那么好,因此我对以下两个条款感到困惑:
size([], 0).
size([H|T], N) :- size(T, N1), N is N1+1.
我无法追踪此问题:
?- size([a,b,c,d], N).
这将与第二个条款统一起来形成:
size([a,b,c,d], N) :- size([b,c,d], N1), N is N1+1.
但我对N
是N1+1
感到困惑,因为这些变量永远不会统一。这些变量有什么价值?
非常感谢有关此问题的任何帮助,或该算法的简单描述。
答案 0 :(得分:4)
N是N1 + 1,因为这些变量永远不会统一。
我认为你的意思是他们永远不会被实例化,即他们永远不会获得价值。统一两个变量可以实例化它们,但这不是必需的。例如,您可以在prolog REPL中运行它:
?- N = N1.
N = N1.
虽然N
和N1
没有价值(但尚未),但它们是统一的;如果N1稍后被实例化,N也将被实例化为具有相同的值。另一个不那么琐碎的例子:
?- [H|T] = L, L = [1|M], writeln(H).
1
H = 1,
T = M,
L = [1|M].
但是,N
永远不会与N1+1
统一! is
将评估N1+1
和 值将与N
统一。这将在评估内部size([b,c,d],N1)
之后发生,因此N1
变量将被实例化。
基本上,执行将是这样的:
size([a,b,c,d],N).
size([b,c,d],N1)
size([c,d],N1)
size([d],N1)
size([],0)
N is 0+1.
N is 1+1.
N is 2+1.
N is 3+1.
由于我们需要将所有函数调用保留在内存中,因此效率有点低;查看尾递归和累加器来解决这个问题。
另请注意,N1
只需要实例化,因为is
将评估表达式。你可以这样写:
size([],0).
size([_|T], 1+ N1):-size(T,N1).
如果你打电话:
?- size([1,2],N).
N = 0+1+1.
有趣,不是吗?您可能想要评估最后一个N,例如将其称为size([1,2],N), Result is N
。然而,一个问题是我们在内存中保留了一个0+1+1+1+1+......
链,它可以非常快速地变大。所以最好将这个技巧用于那些不会增长的东西,例如:表达模板。