本周是我第一次做递归。我能够解决的问题之一是Fibonacci的序列到第n个数字;在弄乱它5分钟后并不难。
但是,我无法理解为什么它适用于当前的return语句。
return array if num == 2
如果我推送到数组,它不起作用,如果我创建一个新的变量序列并推送到它,它会返回正确的答案。我很酷,但我的基础案例是返回数组,而不是序列。我最初将序列推送到数组,结果不是fibs序列。当我尝试看到如果我按下序列数组会发生什么时,我才解决了这个问题。
我希望有人可以解释幕后发生的事情,堆栈可能是什么以及问题是如何运作的,而不仅仅是让它发挥作用。
我理解递归到一定程度,并且通过某种方式直观地通过假设来使其工作,但我觉得好笑而不是真正了解其背后的所有原因。
def fib_seq(num)
return [0] if num == 1
return [] if num == 0
array = [0, 1]
return array if num <= 2
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
end
答案 0 :(得分:2)
通过删除临时In [130]: re.findall(r'(?<!\d|M)(?:-?[\d]+\.[\d]+)|M(?=,|$)', line, re.M)
Out[130]:
['-94.5930',
'39.1230',
'79.00',
'73.90',
'84.41',
'220.00',
'4.00',
'0.00',
'29.68',
'1003.90',
'10.00',
'M',
'M',
'M',
'M',
'9500.00',
'M',
'M',
'M',
'M']
变量,可以简化代码。这是一种分心。它也适用于array
; num == 2
将由其他基本案例处理。 num < 2
是非法的,应该通过错误检查来处理。
我还在一个明确的回报中添加了。显式返回使得返回的内容非常明显,这有助于理解递归。在这种情况下,它是num < 0
。 (“明确的回报是邪恶的!”所有Ruby风格的人都会哭。坚韧的饼干。好的风格不是绝对的。)
seq
现在更清楚一点def fib_seq(num)
# Error check
if num < 0 then
raise ArgumentError, "The number must be a positive integer"
end
# Terminating base cases
return [] if num == 0
return [0] if num == 1
return [0,1] if num == 2
# Recursion
seq = fib_seq(num - 1)
# The recursive function
seq << seq[-2] + seq[-1]
return seq
end
是递归的三个基本情况之一。这些是终止递归的终止条件。但处理并没有就此结束。结果不是return [0,1] if num == 2
,因为在第一次返回之后,堆栈必须放松。
让我们一起走过[0,1]
。
fib_seq(4)
我们已达到基本情况,现在我们需要解开那堆电话。
fib_seq(4) calls fib_seq(3)
fib_seq(3) calls fib_seq(2)
fib_seq(2) returns `[0,1]`
的电话会从中断的地方开始。 fib_seq(3)
返回的seq
为fib_seq(2)
。它会在末尾添加[0,1]
并返回seq[-2] + seq[-1]
。
[0,1,1]
从它停止的地方开始。 fib_seq(4)
返回的seq
为fib_seq(3)
。它会在末尾添加[0,1,1]
并返回seq[-2] + seq[-1]
。
堆栈已展开,因此我们返回[0,1,1,2]
。
如您所见,实际计算是向后发生的。 [0,1,1,2]
和f(n) = f(n-1) + f(n-2)
。它会向下递归f(2) = [0,1]
,基本情况,然后使用f(2)
的结果展开f(3)
,使用f(2)
的结果取消f(4)
等等。
答案 1 :(得分:2)
递归函数需要具有退出条件以防止它们永远运行。递归方法的主要部分如下:
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
在Ruby中,方法的最后一个表达式被认为是该方法的返回值,因此上面的行等同于:
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
return seq
让我们了解如果方法只包含这两行,会发生什么, num = 4:
call fib_seq(4)
call fib_seq(3)
call fib_seq(2)
call fib_seq(1)
call fib_seq(0)
call fib_seq(-1)
...
显然这会导致无限循环,因为我们没有退出条件。我们总是在第一行再次调用fib_seq
,因此代码最终无法到达return
语句。要解决这个问题,让我们在开头添加这两行:
array = [0, 1]
return array if num <= 2
这些可以简化为:
return [0, 1] if num <= 2
现在让我们看看当我们用 num = 4调用方法时会发生什么:
call fib_seq(4)
4 > 2, exit condition not triggered, calling fib_seq(n - 1)
call fib_seq(3)
3 > 2, exit condition not triggered, calling fib_seq(n - 1)
call fib_seq(2)
2 == 2, exit condition triggered, returning [0, 1]!
fib_seq(2) returned with seq = [0, 1]
add 0 + 1 together, push new value to seq
seq is now [0, 1, 1]
return seq
fib_seq(3) returned with seq = [0, 1, 1]
add 1 + 1 together, push new value to seq
seq is now [0, 1, 1, 2]
return seq
FINAL RESULT: [0, 1, 1, 2]
所以看起来这个方法适用于 num 的值>&gt; = 2:
def fib_seq(num)
return [0, 1] if num <= 2
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
end
还有一个错误: num = 0且 num = 1都返回[0, 1]
。我们来解决这个问题:
def fib_seq(num)
return [] if num == 0
return [0] if num == 1
return [0, 1] if num == 2
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
end
稍微清理一下:
def fib_seq(num)
return [0, 1].first(num) if num <= 2
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
end
答案 2 :(得分:0)
当人们将命令式样式突变与函数式递归混合在一起时,我总是觉得很困惑 - 如果您要进行所有重新分配和手动数组搜索,为什么还要使用递归作为循环机制呢?只需使用一个循环。
这并不是说这个程序不能以更实用的方式表达。在这里,我们将计算斐波那契数字和生成序列的问题分开 - 结果是一个非常容易理解的程序
def fib n
def aux m, a, b
m == 0 ? a : aux(m - 1, b, a + b)
end
aux n, 0, 1
end
def fib_seq n
(0..n).map &method(:fib)
end
fib_seq 10
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
另一种特别有效地生成序列的方法 - 下面,我定义了一个利用4个状态变量以相对简单的方式生成序列的axuiliary函数aux
。
请注意与输入10
的区别 - 这个与0
返回[]
尽管the 0th fibonacci number is actually 0
def fib_seq n
def aux acc, m, a, b
m == 0 ? acc << a : aux(acc << a, m - 1, b, a + b)
end
case n
when 0; []
when 1; [0]
when 2; [0,1]
else; aux [0,1], n - 3, 1, 2
end
end
fib_seq 10
# => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]