5

Paul Graham描述了以下问题:

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

他说,虽然这样的功能可以在 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

4 回答 4

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”的成语。它不能只用几行就做一个累加器,因为它既不需要也不想做。

于 2013-04-13T06:02:59.680 回答
2

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

正如他在帖子前面所说的那样,他关心的部分内容是语句和表达式之间的区别。在 Python+=中是语句,而 lambda 不能包含语句,只能包含表达式。

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

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

于 2013-04-13T04:18:38.537 回答
2

它可以。诀窍是使用一个容器来保存原始整数,并在不使用赋值运算符的情况下设置和访问这个数字。

>>> 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
>>> 
于 2013-04-13T06:37:44.733 回答
0

尝试尽可能清楚地做到这一点,将 lambdas 分隔为变量:

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
于 2013-04-13T12:19:23.470 回答