Elixir无限递归是否曾经使堆栈溢出? [英] Does Elixir infinite recursion ever overflow the stack?

查看:86
本文介绍了Elixir无限递归是否曾经使堆栈溢出?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个数字在Elixir编程上的不同的操作指南的观点通过将数据旋转到代理或任务中,或者通过无限递归需要状态的功能,可以惯常地完成状态存储或无限循环运行。他们没有提及对递归进行的深度或任何其他警告的限制。

A number of different how-tos on Elixir programming express the view that storing state or running an infinite loop is done idiomatically either by spinning the data off into an Agent or Task, or by infinite recursion of the function that needs state. They don't mention any limits on how deep the recursion can go or any other caveats.

由于搜索 Elixir堆栈溢出只会导致对该网站的点击,让我消除歧义,在这里问:Elixir中有哪些实现保证可以确保无限递归作为循环方法不会导致堆栈溢出,尤其是在沿途携带状态信息时?

Since searching for "Elixir stack overflow" just results in hits to this website, let me remove the ambiguity and ask here: What implementation guarantees are there in Elixir to make sure that infinite recursion as a method of 'looping' won't result in a stack overflow, especially when state information is being carried around along the way?

推荐答案

总结一下Hristo的出色评论,通用机制称为尾部调用优化(TCO),它可以确保函数执行的最后一件事是调用另一个函数(或自身),因此不会进行堆栈推送。而是会发生简单的跳转。

To summarize excellent comments by Hristo, the general mechanism is called "Tail Call Optimization" (TCO) and it ensures that if the last thing a function does is invocation of another function (or itself), then there won't be stack push. Instead, a simple jump will occur.

关于什么是尾叫,有些细微的差别。我们来看几个例子。最简单的一个是:

There are some subtle nuances as to what is a tail call. Let's see a few example. The simplest one is:

def foo do
  # ...

  bar(...)  # tail call -> nothing is pushed to the stack
end

TCO也将适用于条件表达式:

TCO will also apply for conditional expressions:

def foo do
  # ...

  if (...) do
    # ...
    bar(...)            # tail call
  else
    # ...
    baz(...)            # tail call
  end
end

之所以有用,是因为函数执行的最后一件事是对函数的调用。 if 的结果是 bar baz

This works because again the last thing a function does is an invocation of a function. The result of if is the result of either bar or baz so there's no need to push anything onto stack.

相反,如果调用方函数在调用另一个函数后又执行了某些操作,则这不是尾部调用,TCO不会t发生:

In contrast, if the caller function does something after calling another function, it's not a tail call, and TCO won't happen:

def foo do
  # ...

  # Not a tail call since we're doing something after bar returns
  # (increment the result by 1)
  1 + bar(...)    
end

另一个打破TCO的示例是在 try 中调用该函数:

Another example of breaking TCO is calling the function in try:

def foo do
  try do
    bar(...)    # not a tail call
  rescue
    # ...
  end
end

TCO,在发生异常时,您不会在堆栈跟踪中看到某些函数:

It's also worth mentioning that due to TCO you won't see some functions in the stack trace when an exception occurs:

def foo do
  # ...
  bar(...)  # at this point foo "disappears" from stack trace
end

def bar(...) do
  # ...
  raise("error")
end

此错误的堆栈转储将不包含 foo 不再位于堆栈中(实际上已由 bar 代替)。

The stack dump of this error won't include foo since it is not on the stack anymore (it is effectively replaced with bar).

这篇关于Elixir无限递归是否曾经使堆栈溢出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆