Lisp-likeness
Bengt Richter
bokr at oz.net
Tue Mar 15 21:00:01 EST 2005
On Wed, 16 Mar 2005 00:36:40 +0100, Marcin 'Qrczak' Kowalczyk <qrczak at knm.org.pl> wrote:
>tar at sevak.isi.edu (Thomas A. Russ) writes:
>
>>> >(defun addn (n)
>>> > #'(lambda (x)
>>> > (+ x n)))
>>>
>>> The same as
>>> def addn(n):
>>> def fn(x):
>>> return n + x
>>> return fn
>>
>> Is this really equivalent?
>>
>> What happens if you call addn more than once with different
>> parameters. Will you get different functions that you can
>> use simultaneously?
>
>Yes.
>
>It also behaves correctly when a variable it refers to is later
>mutated.
>
>
>BTW, the fact that a closure refers to a variable itself rather to its
>current value can be used to check the true attitude of languages with
>respect to functional programming, by observing how they understand
>their basic loops :-)
>
>Python loses:
>
>>>> closures = []
>>>> for i in range(10):
>... def add(x):
>... return x + i
>... closures.append(add)
>...
>>>> closures[5](1000)
>1009
Fire the coach -- the team can do it ;-)
One way is with the help of a byte-code-hacking decorator:
>>> from ut.presets import presets
>>> closures = []
>>> for i in range(10):
... @presets(i=i)
... def add(x):
... return x + i
... closures.append(add)
...
>>> closures[5](1000)
1005
>>> import dis
>>> dis.dis(closures[5])
2 0 LOAD_CONST 1 (5)
3 STORE_FAST 1 (i)
4 6 LOAD_FAST 0 (x)
9 LOAD_FAST 1 (i)
12 BINARY_ADD
13 RETURN_VALUE
>>> dis.dis(closures[3])
2 0 LOAD_CONST 1 (3)
3 STORE_FAST 1 (i)
4 6 LOAD_FAST 0 (x)
9 LOAD_FAST 1 (i)
12 BINARY_ADD
13 RETURN_VALUE
Of course, if you want to do it without byte code hacking, you can:
>>> closures2 = list((lambda i: lambda x: x + i)(i) for i in xrange(10))
>>> closures2[5](1000)
1005
>>> dis.dis(closures2[5])
1 0 LOAD_FAST 0 (x)
3 LOAD_DEREF 0 (i)
6 BINARY_ADD
7 RETURN_VALUE
>>> closures2[3](1000)
1003
Or same thing without lambda:
>>> def mkadd(i):
... def add(x): return x + i
... return add
...
>>> closures3 = [mkadd(i) for i in xrange(10)]
>>> closures3[5](1000)
1005
>>> closures3[3](1000)
1003
>>> dis.dis(closures3[5])
2 0 LOAD_FAST 0 (x)
3 LOAD_DEREF 0 (i)
6 BINARY_ADD
7 RETURN_VALUE
>
>as does Ruby:
>
>$ ruby -e '
> closures = []
> for i in 0..9 do
> closures.push(proc {|x| x + i})
> end
> puts closures[5][1000]'
>1009
>
>but Lisp loses too:
>
>> (let ((closures (make-array 10)))
> (do ((i 0 (+ i 1)))
> ((= i 10))
> (setf (svref closures i) #'(lambda (x) (+ x i))))
> (funcall (svref closures 5) 1000))
>1010
>
>
>Scheme wins:
>
>> (let ((closures (make-vector 10)))
> (do ((i 0 (+ i 1)))
> ((= i 10))
> (vector-set! closures i (lambda (x) (+ x i))))
> ((vector-ref closures 5) 1000))
>1005
>
>and what is perhaps surprising, Perl wins:
>
>$ perl -e '
> foreach my $i (0..9) {
> push @closures, sub {$_[0] + $i}
> }
> print $closures[5](1000), "\n"'
>1005
>
>
>If you think it's unlikely that one would want to keep a closure
>referring to a loop control variable longer than the loop iteration
>which has created it, think about the loop body spawning a thread.
>
Just do what you need to do. Python usually provides a way ;-)
Or do you just want what you want using your idea of the right spelling? ;-)
Regards,
Bengt Richter
More information about the Python-list
mailing list