Are programs in functional languages more likely to have stack overflows?

青春壹個敷衍的年華 提交于 2019-12-05 10:32:28

All (decent implementations of;-) functional languages optimize tail recursion, but that's not what you're doing here, since the recursive call is not the LAST operation (it needs to be followed by the addition).

So, one soon learns to use an auxiliary function that IS tail recursive (and takes the current total being accumulated as an argument) so the optimizer can do its job, i.e., net of possible O'Caml syntax in which I'm rusty:

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

let rec aux x accum =
  if x > 1 then aux(x - 1)(accum + x)
  else (accum + x);;

Here, the sum happens as an ARGUMENT to the recursive call, i.e., BEFORE the recursion itself, and so tail optimization can kick in (because the recursion IS the last thing that needs to happen!).

Functional languages typically have a MUCH larger stack. For example, I've written a function specifically to test stack limits in OCaml, and it got to over 10,000 calls before it barfed. However, your point is valid. Stack-overflows are still something you need to watch out for in functional languages.

One of the strategies that functional languages use to mitigate their reliance on recursion is the use of tail-call optimization. If the call to the next recursion of the current function is the last statement in the function, the current call can be discarded from the stack and the new call instantiated in its place. The assembly instructions that are generated will be basically the same as the ones for while-loops in imperative style.

Your function is not tail-call optimizable because the recursion is not the last step. It needs to return first and then it can add x to the result. Usually this is easy to get around, you just create a helper function that passes an accumulator along with the other parameters

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;;

Some functional languages such as Scheme specify that tail recursion must be optimized to be equivalent to iteration; hence, a tail-recursive function in Scheme will never result in a stack overflow, no matter how many times it recurses (presuming, of course, that it doesn't also recurse or engage in mutual recursion in other places besides the end).

Most other functional languages don't require tail recursion to be implemented efficiently; some choose to do so, others do not, but it's relatively easy to implement, so I would expect that most implementations do so.

It's certainly easy for novices to write deep recursions that blow the stack. Objective Caml is unusual in that the library List functions are not stack-safe for long lists. Applications like Unison have actually replaced the Caml standard List library with a stack-safe version. Most other implementations do a better job with the stack. (Disclaimer: my information describes Objective Caml 3.08; the current version, 3.11, may be better.)

Standard ML of New Jersey is unusual in that it doesn't use a stack, so your deep recursions keep going until you run out of heap. It's described in Andrew Appel's excellent book Compiling with Continuations.

I don't think there's a serious problem here; it's more a "point of awareness" that if you are going to be writing a lot of recursive code, which you're more likely to do in a functional language, you have to be aware of non-tail calls and of stack size as compared to the size of the data you'll be processing.

This is tricky -- in principle yes, but the compilers and runtimes for functional languages account for the increased degree of recursion in functional languages. The most basic is that most functional language runtimes request a much larger stack than normal iterative programs would use. But in addition to that a functional language compiler is much more able to transform recursive code into a non-recursive due to the much stricter constraints of the language.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!