Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Python, why can't an accumulator generators be written with lambdas? [closed]

Paul Graham describes the following problem:

We want to write a function that generates accumulators-- a function that takes a number n, and returns a function that takes another number i and returns n incremented by i.

He says that whereas such a function could be implemented in Lisp/Ruby/Perl as simply as something like

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

, in Python it would be written

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

So my question is, What exactly about Python (apart from the lack of support for multi-line lambdas) prevents you from implementing an accumulator generator in the terse style of the first code sample above? Would Python ever support such a thing in the future, as Paul Graham speculates?

like image 972
Jian Avatar asked Apr 13 '13 04:04

Jian


People also ask

Why generators are better in Python?

Generators allow you to create iterators in a very pythonic manner. Iterators allow lazy evaluation, only generating the next element of an iterable object when requested. This is useful for very large data sets. Iterators and generators can only be iterated over once.

How do generators work Python?

A Python generator is a function that produces a sequence of results. It works by maintaining its local state, so that the function can resume again exactly where it left off when called subsequent times. Thus, you can think of a generator as something like a powerful iterator.

How do you create a generator in Python?

Create Generators in Python It is fairly simple to create a generator in Python. It is as easy as defining a normal function, but with a yield statement instead of a return statement. If a function contains at least one yield statement (it may contain other yield or return statements), it becomes a generator function.

What is generator expression in Python?

A generator expression is an expression that returns a generator object. Basically, a generator function is a function that contains a yield statement and returns a generator object. For example, the following defines a generator function: def squares(length): for n in range(length): yield n ** 2.


3 Answers

The example if first of all contrived. After defining the accumulator, you would in Python use it like this:

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

For what use? In Python you would do this:

acc = 6
acc += 2
acc += 4

I don't know if defining an accumulator in lisp makes sense, but in Python you don't need to define one, because you would have it built in, so to speak.

Second of all the question you ask hits the nail on the head. What prevents Python from doing this in a "terse" style? Because the attitude of Python is that it will going to be a language that is quick to develop in and that is maintainable, and that means easy to read. Code golf and obtuse, terse code is not a part of what Python is designed for.

But ultimately, the reason Python will never evolve this functionality is that is relies on integers being mutable, ie that you can do something like:

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

This will not happen in Python, where integers are immutable. You can't increase the value of an integer. This simplifies both the language and it's use a lot. For example, if integers were mutable, they could not be used as keys in dictionaries.

Essentially the example centers around increasing the value of integers, something you can't do in Python. In Python, when you add two integers you get a third integer back. You do not increase the value of the first one.

So Python will never become lisp, and only people who have used lisp for too long thinks it should, or persist in the "python is almost lisp" idiom. And it can't do an accumulator in just a few lines, because it neither needs to nor wants to.

like image 194
Lennart Regebro Avatar answered Oct 13 '22 19:10

Lennart Regebro


It can. The trick is to use a container to hold the original integer and to set and access this number without using the assignment operator.

>>> 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
>>> 
like image 34
MrD Avatar answered Oct 13 '22 19:10

MrD


He actually describes one reason in his followup post. His brief discussion there covers both of the reasons I mention below, although his take on it is a bit different.

As he talks about earlier in the post, part of what he's concerned with is the difference between statements and expressions. In Python += is a statement, and lambdas cannot contain statements, only expressions.

However, there's another issue. He wants his function to take "a number" as input, but he makes a distinction between "plus" and "increment" (as do many programming languages). However, my own position would be that there is no such distinction for numbers, only for variables (or "objects" or similar things). There is no such thing as "incrementing" the number 5. In this sense, you still can't write a Python lambda that increments a variable containing a builtin in numeric type, but you can do it if it accepts a mutable object instead of a raw number. And you could write your own MutableNumber class that works this way and make it totally interoperable with existing numeric types. So in this sense the reason Python doesn't support has to do with the design of its types (i.e., numbers are immutable) rather than the sort of functional issues he discusses in the post.

Whether any of this is actually a problem for the language is, of course, another question.

like image 39
BrenBarn Avatar answered Oct 13 '22 20:10

BrenBarn