【问题标题】:iteration without a loop in python在python中没有循环的迭代
【发布时间】:2016-04-22 08:00:40
【问题描述】:

以下示例来自 Allen Downey 的“Think Python”一书。在解释字典中“备忘录”的概念时,他引用了下面的例子。

known = {0:0, 1:1}
def fibonacci(n):
    if n in known:
        return known[n]
    res = fibonacci(n-1) + fibonacci(n-2)
    known[n] = res
    return res
fibonacci(5)
print known

我期待这段代码返回 {0:0, 1:1, 5:5},因为我没有看到任何迭代来计算 1 到 5 之间的每个值的函数。但我看到的是运行代码时返回的 {0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 5}(作为书上说它会),但我不明白为什么函数计算表达式res = fibonacci(n-1) + fibonacci(n-2) for n=2、n=3 和 n=4。

有人可以向我解释一下吗?书里的解释我不太明白。

【问题讨论】:

  • 这是递归,而不是迭代。为什么你认为 SO 能比书更好地解释它?
  • 代码分析不是模式匹配,为了理解发生了什么,你应该按照每一行代码,拿一张纸来模拟发生了什么——这是最好的学习方式,而且这个问题的答案很容易出现
  • 远离电脑,自己跟着算法走。您将完成与计算机完全相同的操作。
  • 顺便说一句,一对递归调用是朴素的递归斐波那契实现。
  • jonrsharpe:我认为在这里发帖会对我有所帮助,因为我不能指望图书作者花时间与所有读者互动,而与 SO 一样,我可以与整个社区互动,并且可以可能得到我的问题的答案,因为我能够。谢谢。

标签: python python-2.7 dictionary iteration memo


【解决方案1】:

尝试将打印语句放入代码中以跟踪known 的状态:

def fibonacci(n):
    print(n, known)
    if n in known:
        return known[n]
    res = fibonacci(n-1) + fibonacci(n-2)

    known[n] = res
    return res
fibonacci(5)
print(known)

产量

5 {0: 0, 1: 1}
4 {0: 0, 1: 1}
3 {0: 0, 1: 1}
2 {0: 0, 1: 1}
1 {0: 0, 1: 1}
0 {0: 0, 1: 1}
1 {0: 0, 1: 1, 2: 1}
2 {0: 0, 1: 1, 2: 1, 3: 2}
3 {0: 0, 1: 1, 2: 1, 3: 2, 4: 3}
{0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 5}

第一个(整数)值是n 的值。如您所见,fibonacci(5) 是 调用,然后是 fibonacci(4),然后是 fibonacci(3),然后是 fibonacci(2) 等等 在。这些调用都是由于Python遇到

    res = fibonacci(n-1) + fibonacci(n-2)

并递归调用fibonacci(n-1)。请记住,Python 计算表达式 从左到右。所以只有在fibonacci(n-1) 返回之后是fibonacci(n-2) 调用。

为了更好地理解递归函数调用的顺序,您可以使用 这个装饰器:

import functools

def trace(f):
    """This decorator shows how the function was called.
    Especially useful with recursive functions."""
    indent = ' ' * 2

    @functools.wraps(f)
    def wrapper(*arg, **kw):
        arg_str = ', '.join(
            ['{0!r}'.format(a) for a in arg]
            + ['{0} = {1!r}'.format(key, val) for key, val in kw.items()])
        function_call = '{n}({a})'.format(n=f.__name__, a=arg_str)
        print("{i}--> {c}".format(
            i=indent * (trace.level), c=function_call))
        trace.level += 1
        try:
            result = f(*arg, **kw)
            print("{i}<-- {c} returns {r}".format(
                i=indent * (trace.level - 1), c=function_call, r=result))
        finally:
            trace.level -= 1
        return result
    trace.level = 0
    return wrapper

known = {0:0, 1:1}
@trace
def fibonacci(n):
    # print(n, known)
    if n in known:
        return known[n]
    res = fibonacci(n-1) + fibonacci(n-2)

    known[n] = res
    return res
fibonacci(5)
print(known)

产生

--> fibonacci(5)                         
  --> fibonacci(4)                        # fibonacci(5) calls fibonacci(4)
    --> fibonacci(3)                      # fibonacci(4) calls fibonacci(3)
      --> fibonacci(2)                    # fibonacci(3) calls fibonacci(2)
        --> fibonacci(1)                  # fibonacci(2) calls fibonacci(1)
        <-- fibonacci(1) returns 1
        --> fibonacci(0)                  # fibonacci(2) calls fibonacci(0)
        <-- fibonacci(0) returns 0
      <-- fibonacci(2) returns 1
      --> fibonacci(1)                    # fibonacci(3) calls fibonacci(1)
      <-- fibonacci(1) returns 1
    <-- fibonacci(3) returns 2
    --> fibonacci(2)                      # fibonacci(4) calls fibonacci(2) 
    <-- fibonacci(2) returns 1
  <-- fibonacci(4) returns 3
  --> fibonacci(3)                        # fibonacci(5) calls fibonacci(3) 
  <-- fibonacci(3) returns 2
<-- fibonacci(5) returns 5
{0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 5}

您可以通过每个递归调用的缩进级别来判断。

【讨论】:

  • 感谢您的解释。这对消除我的困惑非常有帮助。我是编码初学者,没有遇到函数评估上下文中的递归。我熟悉我的专业领域(IP 路由)中的“递归”,这是一个更简单的概念。感谢您花时间解释这一点。
【解决方案2】:

如您所见,fibonacci 是一个递归函数,这意味着它在函数内部调用自身。

例如考虑fibonacci(2)2 不在knowndictionary 中,所以res = fibonacci(1)+fibonacci(0) 被执行。因为01 是已知的,所以它们的值(0 和1)添加到res 所以res=1 所以fibonacci(2) = 1 和2 也添加到known 字典中,直到达到5。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-28
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-02
    • 2017-05-05
    • 1970-01-01
    相关资源
    最近更新 更多