有人可以给我这两种递归和示例(特别是在OCaml)之间的差异?尾递归与前向递归
尾递归与前向递归
回答
尾递归函数是一个函数,其中唯一的递归调用是函数中的最后一个。非尾递归函数是一种不是这种情况的函数。
向后递归是一个递归,其中在每次递归调用中参数的值小于上一步中的值。前向递归是递归,每一步都会递增。
这些是两个正交的概念,即前向递归可能是也可能不是尾递归,同样适用于后向递归。
例如阶乘函数通常这样写的命令式语言:
fac = 1
for i from 1 to n:
fac := fac * i
向后阶乘计数的共同递归版本(即它与n-1
自称为参数),如果你愿意,但是直接翻译上面的强制性解决方案,你会想出一个递归的版本。这将是这个样子:
let fac n =
let rec loop i =
if i >= n
then i
else i * loop (i+1)
in
loop 1
这是向前递推,正如你可以看到它比落后的递归variant稍微繁琐,因为它需要辅助功能。现在这不是尾递归,因为最后一次调用loop
是乘法,而不是递归。因此,为了使尾递归,你会做这样的事情:
let fac n =
let rec loop acc i =
if i >= n
then acc
else loop (i*acc) (i+1)
in
loop 1 1
现在,这既是前向递归和尾递归,因为递归调用是)一个尾调用和b)调用自身具有更大的价值(i+1
)。
这里的一个尾递归阶乘函数的一个例子:
let fac n =
let rec f n a =
match n with
0 -> a
| _ -> f (n-1) (n*a)
in
f n 1
这里是它的非尾递归对应物:
let rec non_tail_fac n =
match n with
0 -> 1
| _ -> (non_tail_fac n-1) * n
尾部递归函数使用的储液器,一个,以存储前一次调用结果的值。这允许OCaml执行尾部呼叫优化,从而导致堆栈不会溢出。通常,尾递归函数将使用累加器值来允许尾部调用优化发生。
例如,递归函数build_word
需要char list
并将它们组合成一个字符串,即['f'; 'o'; 'o']
到字符串"foo"
。感应过程可以用这种方式显示:
build_word ['f'; 'o'; 'o']
"f"^(build_word ['o'; 'o'])
"f"^("o"^(build_word ['o']) // base case! return "o" and fold back
"f"^("o"^("o"))
"f"^("oo")
"foo"
这是一个正常的递归。请注意,每对括号代表一个新的堆栈帧或递归调用。这个问题的解决方案(即“f”,“fo”或“foo”)不能在递归结束之前(满足基本情况)派生。只有这样,最后一帧才会在“弹出”之前将最后一个结果返回到前一个结果,反之亦然。
从理论上讲,每次调用都会创建一个新的堆栈框架(或范围,如果您愿意的话)来保存碎片解决方案的“地点”以便返回并收集到开始处。这可能导致stackoverflow(这个链接是一个递归顺便说一句)。
尾调用的版本将是这个样子:
build_word ['f'; 'o'; 'o'] ""
build_word ['o'; 'o'], "f"
build_word ['o'] ("f"^"o")
build_word [] ("f"^"o"^"o")
"foo"
这里,累积的结果(通常存储在被称为accumulator
变量)正在向前传递。通过优化,尾部调用不需要创建新的堆栈帧,因为它不必保留前一个。解决方案正在解决“前进”而不是“后退”。
下面是build_word
功能有两个版本:
非尾
let build_word chars =
match chars with
| [] -> None
| [c] -> Some Char.to_string c
| hd :: tl -> build_word tl
;;
尾
let build_word ?(acc = "") chars =
match chars with
| [] -> None
| [c] -> Some Char.to_string c
| hd::tl -> build_word ~acc:(acc^Char.to_string hd) tl
;;
向前递推是公认的答案很好解释由@ sepp2k。
- 1. Erlang中的尾递归与前向递归
- 2. 尾递归与List.fold_left
- 3. Javascript尾递归
- 4. 尾递归算法归并
- 5. 尾递归vs原始递归
- 6. 方案尾递归
- 7. 尾递归连续
- 8. 尾递归函数
- 9. 尾v头递归
- 10. 计划。尾递归?
- 11. 斯卡拉尾递归为未尾递归
- 12. Java尾递归:低于斐波那契码尾递归?
- 13. 递归反向
- 14. 尾递归归并排序OCaml中
- 15. Java - SubSet和递归递归递归图
- 16. 递归锁(Mutex)与非递归锁(Mutex)
- 17. java中的尾递归
- 18. 尾递归和迭代SML
- 19. Bash中的尾递归
- 20. R中的尾递归
- 21. 折叠左尾递归?
- 22. 对象的尾递归
- 23. java.lang.StackOverflowError的Clojure中尾递归
- 24. 可以函数尾递归
- 25. 了解F#尾递归
- 26. Scala中的尾递归findNextAndTail
- 27. 汇编中的尾递归
- 28. 斯卡拉的尾递归
- 29. 斯卡拉尾递归
- 30. 尾递归堆栈溢出
除非我误解了递归递归意味着什么(我承认我不得不谷歌它,因为我以前从未听过这个词),你的函数都不是递归递归的。 – sepp2k 2010-06-15 07:50:22
看起来我错过了问题的'前进'递归部分。 – sashang 2010-06-15 07:58:15