PEP 342: simple example, closure alternative
I was trying to translate a pattern that uses closures in a language like Scheme (where closed values can be written to) to generators using PEP 342, but I'm not clear exactly how it works; the examples in the PEP have different motivations. Since I can't actually run these examples, perhaps someone could confirm or debug these: A closure based accumulator (using Scheme): (define (accum n) (lambda (incr) (set! n (+ n incr)) n)) (define s (accum 0)) (s 1) ; -> 1 == 0+1 (s 5) ; -> 6 == 1+5 So I thought the generator version might look like: def accum(n): while 1: incr = (yield n) or 0 n += incr
s = accum(0) s.next() s.send(1) 0 s.send(5) 1 s.send(1) 6
Is the order of the output correct? Is there a better way to write accum, that makes it feel more like the closure-based version? Is this for loop correct?
s = accum(0) for i in s: ... if i >= 10: break ... print i, ... assert s.send(2) == i 0 2 4 6 8
Hmm... maybe this would make it feel more closure-like: def closure_like(func): def replacement(*args, **kw): return ClosureLike(func(*args, **kw)) return replacement class ClosureLike(object): def __init__(self, iterator): self.iterator = iterator # I think this initial .next() is required, but I'm # very confused on this point: assert self.iterator.next() is None def __call__(self, input): assert self.iterator.send(input) is None return self.iterator.next() @closure_like def accum(n): while 1: # yields should always be in pairs, the first yield is input # and the second yield is output. incr = (yield) # this line is equivalent to (lambda (incr)... n += incr # equivalent to (set! ...) yield n # equivalent to n; this yield always returns None
s = accum(0) s(1) 1 s(5) 6
Everything before the first (yield) is equivalent to the closed values between "(define (accum n)" and "(lambda" (for this example there's nothing there; I guess a more interesting example would have closed variables that were written to that were not function parameters). -- Ian Bicking / ianb@colorstudy.com / http://blog.ianbicking.org
At 02:10 PM 8/25/2005 -0500, Ian Bicking wrote:
I was trying to translate a pattern that uses closures in a language like Scheme (where closed values can be written to) to generators using PEP 342, but I'm not clear exactly how it works; the examples in the PEP have different motivations. Since I can't actually run these examples, perhaps someone could confirm or debug these:
A closure based accumulator (using Scheme):
(define (accum n) (lambda (incr) (set! n (+ n incr)) n)) (define s (accum 0)) (s 1) ; -> 1 == 0+1 (s 5) ; -> 6 == 1+5
So I thought the generator version might look like:
def accum(n): while 1: incr = (yield n) or 0 n += incr
s = accum(0) s.next()
The initial next() will yield 0, not None.
s.send(1) 0
1
s.send(5) 1
6
s.send(1) 6
7
Is the order of the output correct? Is there a better way to write accum, that makes it feel more like the closure-based version?
Is this for loop correct?
s = accum(0) for i in s: ... if i >= 10: break ... print i, ... assert s.send(2) == i 0 2 4 6 8
The assert will fail on the first pass. s.send(2) will == i+2, e.g.:
s = accum(0) for i in s: ... if i>=10: break ... print i, ... assert s.send(2) == i+2 ... 0 2 4 6 8
Phillip J. Eby wrote:
At 02:10 PM 8/25/2005 -0500, Ian Bicking wrote:
I was trying to translate a pattern that uses closures in a language like Scheme (where closed values can be written to) to generators using PEP 342, but I'm not clear exactly how it works; the examples in the PEP have different motivations. Since I can't actually run these examples, perhaps someone could confirm or debug these:
A closure based accumulator (using Scheme):
(define (accum n) (lambda (incr) (set! n (+ n incr)) n)) (define s (accum 0)) (s 1) ; -> 1 == 0+1 (s 5) ; -> 6 == 1+5
So I thought the generator version might look like:
def accum(n): while 1: incr = (yield n) or 0 n += incr
Bah, I don't know why this had me so confused. Well, I kind of know why. So maybe this example would be better written: def accum(n): incr = yield # wait to get the first incr to be sent in while 1: n += incr incr = yield n # return the new value, wait for next incr This way it is more explicit all around -- the first call to .next() is just setup, kind of like __init__ in an object, except it has to be explicitly invoked. -- Ian Bicking / ianb@colorstudy.com / http://blog.ianbicking.org
A closure based accumulator (using Scheme):
(define (accum n) (lambda (incr) (set! n (+ n incr)) n)) (define s (accum 0)) (s 1) ; -> 1 == 0+1 (s 5) ; -> 6 == 1+5
So I thought the generator version might look like:
def accum(n): while 1: incr = (yield n) or 0 n += incr
Maybe I'm missing something but this example seems needlessly tricky to me. How about doing it this way? def accum(n): acc = [n] def f(incr): acc[0] += incr return acc[0] return f Here, the [0] turns "read-only" access into write access to a list element. The list itself isn't written; only its element is.
Andrew Koenig wrote:
A closure based accumulator (using Scheme):
(define (accum n) (lambda (incr) (set! n (+ n incr)) n)) (define s (accum 0)) (s 1) ; -> 1 == 0+1 (s 5) ; -> 6 == 1+5
So I thought the generator version might look like:
def accum(n): while 1: incr = (yield n) or 0 n += incr
Maybe I'm missing something but this example seems needlessly tricky to me. How about doing it this way?
def accum(n): acc = [n] def f(incr): acc[0] += incr return acc[0] return f
Here, the [0] turns "read-only" access into write access to a list element. The list itself isn't written; only its element is.
I was just exploring how it could be done with coroutines. But also because using lists as pointers isn't that elegant, and isn't something I'd encourage people do to coming from other languages (where closures are used more heavily). More generally, I've been doing some language comparisons, and I don't like literal but non-idiomatic translations of programming patterns. So I'm considering better ways to translate some of the same use cases. -- Ian Bicking / ianb@colorstudy.com / http://blog.ianbicking.org
On 8/25/05, Ian Bicking
More generally, I've been doing some language comparisons, and I don't like literal but non-idiomatic translations of programming patterns.
True. (But that doesn't mean I think using generators for this example is great either.)
So I'm considering better ways to translate some of the same use cases.
Remember that this particuar example was invented to show the superiority of Lisp; it has no practical value when taken literally. If you substitute a method call for the "acc += incr" operation, the Python translation using nested functions is very natural. For larger examples, I'd recommend defining a class as always. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Le Vendredi 26 Août 2005 16:57, Guido van Rossum a écrit :
On 8/25/05, Ian Bicking
wrote: More generally, I've been doing some language comparisons, and I don't like literal but non-idiomatic translations of programming patterns.
True. (But that doesn't mean I think using generators for this example is great either.)
So I'm considering better ways to translate some of the same use cases.
Remember that this particuar example was invented to show the superiority of Lisp; it has no practical value when taken literally. If you substitute a method call for the "acc += incr" operation, the Python translation using nested functions is very natural. For larger examples, I'd recommend defining a class as always.
For example, I often use this class to help me in functional programming : _marker = () class var: def __init__(self, v=None): self.v = v def __call__(self, v=_marker): if v is not _marker: self.v = v return self.v and so the nested functions become very functional : def accum(n): acc = var(n) return lambda incr: acc(acc()+incr)
On Fri, Aug 26, 2005 at 06:21:58PM +0200, Alain Poirier wrote:
For example, I often use this class to help me in functional programming :
_marker = () [...]
You should not use an immutable object here (e.g. the empty tuple is shared). My preferred idiom is: _marker = object() Cheers, Neil
participants (6)
-
Alain Poirier
-
Andrew Koenig
-
Guido van Rossum
-
Ian Bicking
-
Neil Schemenauer
-
Phillip J. Eby