我正在阅读this blog并遇到以下代码
RunAgain = Class.new(Exception)
def fib(i, n = 1, result = 0)
if i == -1
result
else
raise RunAgain
end
rescue RunAgain
i, n, result = i - 1, n + result, n
retry
end
似乎上面的代码工作,一旦异常被引发,那么ruby必须清除整个堆栈跟踪并用异常堆栈替换它。
我的理解是对的吗?
答案 0 :(得分:2)
此代码的工作方式实际上与堆栈跟踪无关。整个时间堆栈上有两个条目fib
和fib
的调用者。异常的提升对你的问题来说是一种红色的鲱鱼 - 除了作为retry
行为的证明之外,它在这个例子中没有任何用处。
Ruby的retry
与其他控制关键字类似,例如next
和break
以及redo
。 redo
关键字表示从顶部重试当前循环或块。 retry
关键字在救援内部起作用,以重试引发异常的当前阻止。
所以这里发生的是为i
,n
和result
设置了一些初始值,检查了基本情况(i == -1
),如果不满意,我们更新值并从顶部重试。请注意,由于这些值是方法参数而不是局部变量,因此不会重新初始化它们。
小心,因为Fibonacci是递归的一个非常常见的例子(并且是非常差的一个),而不是将其误认为是递归算法。 RunAgain
引发和救援功能就像一个循环,无需重新调用函数或修改callstack。
您的示例代码等同于
def fib(i)
n, result = 1, 0
(i+1).times { n, result = n + result, n }
result
end
请注意,在这两种情况下,i
只是一个计数器,仅此而已。我们运行代码i+1
次。另请注意,交换值的临时变量的典型需求由ruby的多重赋值构造替换。