Closures and loop scope
Nick Sabalausky
SeeWebsiteToContactMe at semitwist.com
Tue Jun 4 14:03:00 PDT 2013
On Tue, 04 Jun 2013 21:19:56 +0200
"Idan Arye" <GenericNPC at gmail.com> wrote:
> Consider the following code. What will it print?
>
> auto arr=new ulong delegate()[5];
>
> foreach(i;0..arr.length){
> arr[i]=()=>i;
> }
>
> writeln(arr.map!`a()`());
>
> It is natural to expect that it will print [0, 1, 2, 3, 4], but
> it actually prints [5, 5, 5, 5, 5]. The reason is obvious - all
> the delegates refer to the same `i` in their closures, and at the
> end of the loop that `i` is set to `5`.
>
I think the problem is simply a misunderstanding of closures.
Consider this:
int a = 2;
auto dg = () => a;
a = 3;
// This prints 3
writeln(dg());
// Now this prints 5
a = 5;
writeln(dg());
The thing to keep in mind is that closures are *not* evaluated at the
point of creation (otherwise they may as well not be written as a
delegate at all). They're evaluated at the point of invocation. And
that's the whole point: to say "Ok program, I want you to *hold on* to
this set of instructions for now...ignore them right now, but I'll tell
you when I want you to 'open the envelope' and look at it".
In other words, the scope captured by a delegate is *by reference*, not
by value. It's the *same* scope, not a snapshot copy of the scope.
So when you put the delegate creation in a loop:
foreach(a; iota(0..6))
dg = () => a;
It *is* expected that you're *not* sticking 0, 1, 2, 3, etc inside the
delegate. That's because you're not evaluating "a" *at all* here,
you're just crerates a delegate that *refers* to "a" itself. You're
just creating the exact same delegate five times. In other words:
You're just saying:
Store the following set of instructions into 'dg': "Read the value of
'a' and then return it."
You're *not* saying:
Read the value of 'a' and *then* create a delegate that returns that
value.
More information about the Digitalmars-d
mailing list