Python递归挑战[关闭]

Posted

技术标签:

【中文标题】Python递归挑战[关闭]【英文标题】:Python recursion challenge [closed] 【发布时间】:2017-01-25 10:58:08 【问题描述】:

我目前正在学习 Python 和计算理论课程,最近在期中考试中遇到了一个我根本无法解决的难题。它涉及为添加数字的程序编写代码。我相信这个问题应该使用递归。我不记得问题的具体措辞,但这是基本思想。

实现multiadder(n) 函数,该函数接受一个非负整数n 并将n 任意值相加。要添加的每个值都必须作为单独的调用编写。例如:

>>> multi_three = multiadder(3)
>>> multi_three(1)(2)(3)
6

>>> multiadder(5)(1)(2)(3)(4)(5)
15

代码必须通过填空来编写。

def multiadder(n):
    assert n > 0
    if _________________________ :
        return _________________________
    else:
        return _________________________

我们在课堂上讨论的主题是高阶函数、递归、lambda 和控制语句。我们不允许使用列表和集合之类的数据结构,也不允许导入任何东西。

请有人帮忙。这是我唯一不能参加考试的问题!

【问题讨论】:

如果允许您向multiadder 添加具有默认值的第二个参数,这很容易。既然你说你不记得确切的问题,你确定这里的空格是正确的吗? 我真的不确定是否可以填充给定的结构。您需要同时跟踪累加器状态和级别状态(知道何时停止返回可调用对象并返回累加值),并且您只有一个变量可以使用,n 加数为任意整数。我不记得问题的确切措辞,但我肯定知道的代码结构是正确的。教授有可能犯了错误,但据我所知,这将是他在考试中的第一个错误。 这不是将 Stack Overflow 用作代码编写服务的例子吗? 这并不宽泛。答案很简短。 【参考方案1】:

试试这个:

def multiadder(n):
  assert n > 0
  if n == 1:
    return lambda t: t
  else:
    return lambda a: lambda b: multiadder(n-1)(a+b)

if __name__ == '__main__':
  print(multiadder(5)(1)(2)(3)(4)(5))

对于n == 1,结果必须是返回输入的函数。

对于n > 1,通过添加输入包装n-1 的结果。

这也适用于连接字符串和其他累加操作:

>>> multiadder(3)('p')('q')('r')
pqr

【讨论】:

如果我为 multiadder(1)(2)(3) 运行这个,我会得到 TypeError: 'int' object is not callable @AnthonyBlackshaw 这种行为是意料之中的。 multiadder(2)(2)(3) 是你应该做的 @MosesKoledoye 对不起,你是对的,我错过了阅读最初的例子。 第一个参数是连续调用的次数,试试:multiadder(3)(1)(2)(3)【参考方案2】:

你可以这样做,但它几乎是不可读的。我希望这些解释会有所帮助:

def multiadder(n):
    assert n > 0
    if n == 0:
        return 0
    else:
        return (lambda f: lambda i, n, sm: f(f, i, n, sm))(
                    lambda rec, i, n, sm: sm+i if n == 0 else lambda j: rec(rec, j, n-1, sm+i)
                )(0, n, 0)

查看它在repl.it 上运行。

工作原理

返回值由三大部分组成:

(lambda f: lambda i, n, sm: f(f, i, n, sm))

简而言之,这个函数给函数赋值,所以可以递归调用。更详细地说: 它需要一个函数f,它本身必须接受 4 个参数,其中第一个参数应该是自引用。 这里返回的函数接受其他三个参数,并返回对f 的递归调用。

第二部分才是真正的核心:

(lambda rec, i, n, sm: sm+i if n == 0 else lambda j: rec(rec, j, n-1, sm+i))

这作为参数传递给上面的第一个函数,使递归成为可能。 这个函数接受上面提到的 4 个参数,并应用特定的逻辑:

i 是必须添加的数字 n 是在此之后仍期望的值的数量 sm 是迄今为止的累计总和(不包括i

根据是否需要更多值,此函数返回最终结果 (sm+i) 或 一个带有一个参数的函数,它的作用与此处描述的相同(递归),减少了n,并调整了总和。

最后将初始值传递给上面:

(0, n, 0)

意思是,我们从数字 0(虚拟)、n 期望值和当前总和 0 开始。

注意

由于上述代码中的递归不涉及对multiladder的调用,并且断言确实排除了if条件为真,所以我们可以不用if...else

def multiadder(n):
    assert n > 0
    return (lambda f: lambda i, n, sm: f(f, i, n, sm))(
                lambda rec, i, n, sm: sm+i if n == 0 else lambda j: rec(rec, j, n-1, sm+i)
            )(0, n, 0)

【讨论】:

看起来像教堂算术,有点 如果assert n > 0 那么if n == 0: return 0 将永远不会被执行,不是吗? 确实,@boardrider,我在接近尾声的回答中写到了这一点。【参考方案3】:

您还可以定义一个内部辅助函数 (loop),它将和状态 (acc) 跟踪为计数器状态 (n) 递减

def multiadder(n):
  def loop(acc,n):
    if n == 0:
      return acc
    else:
      return lambda x: loop(acc+x, n-1)
  return loop(0,n)

print(multiadder(3)(1)(2)(3)) # 6
print(multiadder(5)(1)(2)(3)(4)(5)) # 15

它不像 DarkKnight 的答案那么优雅,但对于初学者来说可能更容易概念化。事实上,这种模式非常有用且用途广泛,我用它来定义几乎所有的递归过程。

def some_recursive_func:
  def loop(...state_parameters):
    if base_condition:
      return answer
    else:
      return loop(...updated_state_variables)
  return loop(...initial_state_variables)

我们对该模式所做的唯一调整是将if 表达式的递归分支包装在lambda x: ... 中——这是因为multiadd 旨在返回函数,直到应用n 返回的函数。


还有一个,只是为了好玩,使用传奇的Y-combinator。这可能不符合您的讲师提供的标准,但看到它还是很酷的。 Multiline lambdas in python are not really a thing 所以可读性会受到一点影响。

U = lambda f: f(f)
Y = U (lambda h: lambda f: f (lambda x: h (h) (f) (x)))

multiadder = Y (lambda f: lambda acc: lambda n:
  acc if n == 0 else lambda x: f (acc+x) (n-1)
) (0)

print(multiadder(3)(1)(2)(3)) # 6
print(multiadder(5)(1)(2)(3)(4)(5)) # 15

或者,如果您愿意,您仍然可以使用 def 语法定义 multiadder

def multiadder(n):
  return Y (lambda f: lambda acc: lambda n:
    acc if n == 0 else lambda x: f (acc+x) (n-1)
  ) (0) (n)

它的工作方式相同。

我强烈建议您逐步跟踪评估以了解其工作原理。在理解这些高阶函数时可以获得一些巨大的见解。

【讨论】:

您介意扩展一下您的答案吗?这里似乎有什么有价值的东西,但我还没有弄清楚。 (这里是初学者) @DanielG 当然,我很乐意提供帮助。是否有特定的部分令人困惑?也许让我知道您理解的部分,我可以尝试详细说明其余部分 不,答案并不令人困惑。我只是不太明白嵌套是从哪里来的。在 DarkKnight 的回答中,我们在 else 分支中有两个 lambda,因此在每次递归时,嵌套都会增加。 是的,本地定义的函数是我们在课堂上学到的,并在测试中的许多地方使用过。如果可能的话,我会这样做。 @DanielG 对 DarkKnight 的回答的“诀窍”是她/他在双 lambda 下应用了两个函数,因此嵌套级别永远不会超过 2 深。【参考方案4】:

起初,multiadder 似乎需要两个输入才能工作,但答案很简单。我不认为“Python 简介”课程需要兰巴函数等。

这是最终答案:

def multiadder(n):
    assert n > 0
    if n == 1:
        return 1
    else:
        return n + multiadder(n - 1)

下图解释了 n = 4 的工作原理:

multiadder(4)
     |
 return 4 + multiadder(3)
                  |
               return 3 + multiadder(2) 
                               |
                            return 2 + multiadder(1)
                                             |
                                         return 1

所以 n = 4 的最终结果是 4 + 3 + 2 + 1 = 10。

【讨论】:

我认为您误读了问题描述。它不应该将第一个 n 数字相加,而是链式函数调用中提供的特定 n 数字相加。 哦,是的,你是对的。我的答案只是一个累积的总和......无论如何我都会留下它。感谢您指出这一点 Blckknght

以上是关于Python递归挑战[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

使用递归和回溯查找所有可能的多米诺骨牌链[关闭]

python中的递归thribonacci函数[关闭]

修复达到的最大setlocal递归级别

在线医学图像处理的巨大挑战[关闭]

如何理解和完成下面提供的嵌套循环挑战[关闭]

在 iMac 上做 C 教程,遇到挑战 [关闭]