函数式语言中的程序是否更有可能出现堆栈溢出?

a_m*_*m0d 5 stack-overflow functional-programming imperative-programming tail-call-optimization

我开始学习ocaml,我非常欣赏语言中递归的力量.但是,我担心的一件事是堆栈溢出.

如果ocaml使用堆栈进行函数调用,它最终是否会溢出堆栈?例如,如果我有以下功能:

let rec sum x =
  if x > 1 then f(x - 1) + x
  else x;;
Run Code Online (Sandbox Code Playgroud)

它必须最终导致堆栈溢出.如果我在c ++(使用递归)中做相同的事情,我知道它会溢出.

所以我的问题是,是否有内置的安全措施来防止函数式语言溢出堆栈?如果不是这样,它们是不是没那么有用,因为上面的求和算法,用带有for循环的过程样式编写,可以处理任何数字(不相关的整数溢出)?

Ale*_*lli 10

所有(()函数语言的优秀实现都优化了尾递归,但这不是你在这里所做的,因为递归调用不是LAST操作(需要后跟添加).

因此,很快就会学会使用一个IS尾递归的辅助函数(并将当前总数作为参数累积),这样优化器就可以完成它的工作,即可能的O'Caml语法,我生锈了:

let sum x =
  aux(x)(0);;

let rec aux x accum =
  if x > 1 then aux(x - 1)(accum + x)
  else (accum + x);;
Run Code Online (Sandbox Code Playgroud)

这里,总和作为递归调用的ARGUMENT发生,即在递归本身之前,因此尾部优化可以启动(因为递归是最后需要发生的事情!).


A. *_*evy 6

功能语言通常具有更大的堆栈.例如,我已经编写了一个专门用于测试OCaml中的堆栈限制的函数,并且在它被禁止之前它已经超过10,000次调用.但是,您的观点是有效的.堆栈溢出仍然是功能语言中需要注意的事项.

函数式语言用于减轻其对递归的依赖的策略之一是使用尾调用优化.如果对当前函数的下一个递归的调用是函数中的最后一个语句,则可以从堆栈中丢弃当前调用,并在其位置实例化新调用.生成的汇编指令与命令式样式中的while循环基本相同.

您的函数不是尾部调用可优化的,因为递归不是最后一步.它需要先返回,然后才能将x添加到结果中.通常这很容易解决,您只需创建一个辅助函数,它将累加器与其他参数一起传递

let rec sum x =
  let sum_aux accum x =
    if x > 1 then sum_aux (accum + x) (x - 1)
    else x
  in sum_aux 0 x;;
Run Code Online (Sandbox Code Playgroud)

  • 功能语言没有"更大的堆栈".堆栈的大小(对于本机代码可执行文件)由OS定义.OCaml可能会因为ABI而进行更多的递归调用 - 默认情况下,参数在寄存器中传递,因此使用的堆栈空间更少.我相信你可以通过fastcall调用约定在C中实现相同的功能. (2认同)