Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?

Re: Lexical closures

by Corion (Pope)
on Oct 25, 2008 at 07:57 UTC ( #719476=note: print w/replies, xml ) Need Help??

in reply to Lexical closures

I don't think your Python example does what you think it does. In Python, to get an anonymous function, you need to use lambda, at least in the Python versions I know (up to 2.4). All you're doing is redefining the func function three times over, and in your call, you get the last declared func instance. If you do it the following way, I expect Python to behave just like Perl, except that I can't test it:

flist = [] for i in xrange(3): flist.append(lambda x,_i=i: x * _i) for f in flist: print f(2)

Note that lambda constructs also don't really close over the variables in their scope, which is why I set up the needed variables as default parameters.

Basically, I think the concepts you are searching for are Closures and Hygienic Macros, but as I haven't seen the code for the other languages, I can't tell whether you tried to use named functions as anonymous functions in the other languages as well.

Replies are listed 'Best First'.
Re^2: Lexical closures
by spurperl (Priest) on Oct 25, 2008 at 08:12 UTC
    Nope, lambda does the same. Python function definitions also create closures, BTW. And it still doesn't explain why the same happens in Common Lisp and Javascript, both of which support closures.

      Well, it depends on what the closures really close over, respectively when a new instance of the loop variable/value gets created. It seems that in Perl, at least when you use a lexical loop variable, you get a new copy each iteration, while in the other languages, you don't. To test this theory, create a new lexical variable within the loop body in each language and see if that's different, that is, use the local equivalent of the following Perl code to create the closures:

      for (0..2) { my $i = $_; push @funcs, sub { $i * $_[0] }; };

      You want to force allocation of a new instance of the lexical variable so your subroutine references get a new instance on each way around.

      Of course, most of the languages have map, so using it would be more apt.

      The real question is, what do your Scheme and Common Lisp code samples look like?

      The Perl behaviour is precisely what I would expect. It generates three closures, each capturing a value of $i. So your first foreach block reduces to:

      my @flist = ( sub {0 * $_[0]}, sub {1 * $_[0]}, sub {2 * $_[0]});
      It's obvious, then, that 0 2 4 is the correct output of
      foreach my $f (@flist) { print $f->(2), "\n"; }

        OK, I took some time to mock out some Scheme: here are both behaviours you've seen:

        (define cclosures (lambda (values) (cond ((null? values) '()) (else (cons (lambda (x) (* (car values) x)) (cclosures (cdr values))))))) (define cclosures2 (let ((val -1)) (lambda (values) (cond ((null? values) '()) (else (begin (set! val (car values)) (cons (lambda (x) (* val x)) (cclosures2 (cdr values))))))))) (define clprint (lambda (closures) (map (lambda (fn) (fn 2)) closures))) > (clprint (cclosures '(0 1 2))) (0 2 4) > (clprint (cclosures2 '(0 1 2))) (4 4 4)

        cclosures uses the equivalent ofdeclaring my $i inside the foreach loop: it defines a new variable called val for every iteration.

        cclosures2 uses the equivalent of declaring my $i outside the foreach loop: val gets reassigned with every iteration.

        Notice the closure closes over the variable, not the value. So when the variable is reassigned, the value inside the closure changes too.


Re^2: Lexical closures
by Anonymous Monk on Oct 25, 2008 at 10:53 UTC
    C:\>C:\Python25\python.exe 0 2 4

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://719476]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (9)
As of 2020-08-05 10:15 GMT
Find Nodes?
    Voting Booth?
    Which rocket would you take to Mars?

    Results (35 votes). Check out past polls.