在Python中,为什么不能用lambdas编写累加器生成器?

时间:2013-04-13 04:01:18

标签: python functional-programming

Paul Graham describes以下问题:

  

我们想要编写一个生成累加器的函数 - 一个取n的函数,并返回一个函数,该函数接受另一个数字i并返回n递增i。

他说,虽然这样的函数可以在Lisp / Ruby / Perl中实现,就像

一样
(defun foo (n)
  (lambda (i) (incf n i)))

,在Python中它将被编写

class foo:
  def __init__(self, n):
      self.n = n
  def __call__(self, i):
      self.n += i
      return self.n

所以我的问题是,Python究竟是什么(除了缺乏对多行lambda的支持)会阻止你在上面第一个代码示例的简洁风格中实现累加器生成器?正如Paul Graham推测的那样,Python将来会支持这样的事情吗?

4 个答案:

答案 0 :(得分:3)

这个例子首先是人为的。在定义累加器之后,你会在Python中使用它:

acc = foo(6)
acc(2)
acc(4)

有什么用?在Python中你会这样做:

acc = 6
acc += 2
acc += 4

我不知道在lisp中定义一个累加器是否有意义,但在Python中你不需要定义一个,因为你可以把它内置,可以这么说。

你要问的第二个问题就是钉在头上。什么阻止Python以“简洁”的方式做到这一点?因为Python的态度是它将成为一种快速开发并且可维护的语言,这意味着易于阅读。代码高尔夫和钝,简洁的代码不是Python的设计目标。

但最终,Python永远不会发展这个功能的原因在于它依赖于整数是可变的,即你可以做类似的事情:

>>> g = 6
>>> g++
>>> g
7

这不会发生在Python中,整数是不可变的。您不能增加整数的值。这简化了语言和它的使用。例如,如果整数是可变的,则它们不能用作词典中的键。

本质上,这个例子的核心是增加整数的值,这是你在Python中无法做到的。在Python中,当你添加两个整数时,你会得到第三个整数。你没有增加第一个的价值。

所以Python永远不会成为lisp,只有那些长时间使用lisp的人认为它应该,或者坚持“python几乎是lisp”的习语。并且它不能在几行中完成累加器,因为它既不需要也不想。

答案 1 :(得分:1)

他实际上在his followup post中描述了一个原因。他在那里的简短讨论涵盖了我在下面提到的两个原因,尽管他对此的看法有点不同。

正如他在帖子前面谈到的那样,他所关注的部分内容是陈述和表达之间的区别。在Python中+=是一个语句,lambdas不能包含语句,只能包含表达式。

然而,还有另一个问题。他希望他的函数以“数字”作为输入,但他区分“加”和“增量”(许多编程语言也是如此)。但是,我自己的立场是数字没有这样的区别,只有变量(或“对象”或类似的东西)。没有“递增”数字5.在这个意义上,你仍然不能编写一个Python lambda来增加包含数字类型的内置的变量,但如果它接受一个可变对象而不是原始数字。您可以编写自己的MutableNumber类,以这种方式工作,并使其与现有的数字类型完全互操作。因此,从这个意义上说,Python不支持的原因与其类型的设计有关(即数字是不可变的),而不是他在帖子中讨论的那种功能问题。

当然,这是否真的是语言的问题是另一个问题。

答案 2 :(得分:1)

它可以。诀窍是使用容器来保存原始整数,并设置和访问此数字,而不使用赋值运算符。

>>> g=lambda n: (lambda d: lambda i: (d.__setitem__('v', d['v']+i),d['v'])[1])({'v': n})
>>> x=g(3)
>>> x(1)
4
>>> x(1)
5
>>> x(10)
15
>>> 

答案 3 :(得分:0)

尝试尽可能清楚地做到这一点,将lambda分成变量:

concat = lambda arr, val: (arr.append(val), arr)[1]
f = lambda n: (lambda i: concat(n,n.pop(0)+i)[0])
accumulate = lambda n: f([n])
a=accumulate(9)
a(1) #10
a(2) #12