自搜索" Elixir堆栈溢出"只是导致对这个网站的点击,让我消除歧义,并在这里问:Elixir有哪些实现保证,以确保无限递归作为循环的方法'不会导致堆栈溢出,尤其是在沿途传输状态信息时?
答案 0 :(得分:16)
总结Hristo的优秀评论,一般机制被称为"尾部呼叫优化" (TCO)并且它确保如果函数做的最后一件事是调用另一个函数(或它自己),那么就不会有堆栈推送。相反,会发生简单的跳转。
关于什么是尾调,有一些微妙的细微差别。让我们看一些例子。最简单的是:
def foo do
# ...
bar(...) # tail call -> nothing is pushed to the stack
end
TCO也适用于条件表达式:
def foo do
# ...
if (...) do
# ...
bar(...) # tail call
else
# ...
baz(...) # tail call
end
end
这是有效的,因为函数的最后一件事是调用函数。 if
的结果是bar
或baz
的结果,因此无需将任何内容推送到堆栈上。
相反,如果调用者函数在调用另一个函数后执行了某些操作,则它不是尾调用,并且TCO不会发生:
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
中的函数:
def foo do
try do
bar(...) # not a tail call
rescue
# ...
end
end
还值得一提的是,由于TCO,当发生异常时,您无法在堆栈跟踪中看到某些函数:
def foo do
# ...
bar(...) # at this point foo "disappears" from stack trace
end
def bar(...) do
# ...
raise("error")
end
此错误的堆栈转储不会包含foo
,因为它不再存在于堆栈中(它实际上已替换为bar
)。