Well, they are supposed to be and sure enough this code won't compile:
var words = new List{ "monkey", "nut", "mice" };
foreach (string word in words)
{
word = "sugar";
}
The C# compiler reports: Cannot assign to 'word' because it is a 'foreach iteration variable'.
But now consider the following code:
var words = new List{ "monkey", "nut", "mice" };
Listprinters = new List ();
foreach (var word in words)
{
printers.Add(
new Action(() => Console.WriteLine(word)));
}
printers.ForEach(printer => printer());
To my surprise this generates:
mice
mice
mice
This may be conform the language specifications, but to me it is highly unlogical, as it violates the illusion that word is immutable with scope local to a single iteration.
Obviously there are always work-arounds to get 3 different words, like this one (to rub it in):
var words = new List{ "monkey", "nut", "mice" };
Listprinters = new List ();
foreach (var word in words)
{
string local=word;
printers.Add(
new Action(() => Console.WriteLine(local)));
}
printers.ForEach(printer => printer());
Or this one (more usable):
var words = new List{ "monkey", "nut", "mice" };
var printers = new List(
words.Select(word =>
new Action(() => Console.WriteLine(word))));
printers.ForEach(printer => printer());
I have been checking out F# lately. It's really neat, I'll have more to say about it subsequent posts. But of course I wanted to be sure the situation in F# is more logical:
let printers = new ResizeArray<(unit->unit)>()
for word in [ "monkey"; "nut"; "mice" ] do
(fun ()->printfn "%s" word) |> printers.Add
Seq.iter (fun action->action()) printers
No fooling around it gets us to the expected:
monkey
nut
mice
Maybe there is some thruth after all in the claim that functional programming makes your code more reliable :-)
2 comments:
"This may be conform the language specifications, but to me it is highly unlogical, as it violates the illusion that word is immutable with scope local to a single iteration."
This isn't just about conforming to the language specification. This is just how lexical closures are supposed to work. A closure binds to the variable -- not the value. So, when you print the variable later, it prints the very last value of that variable.
Check out the lively discussion at the end of http://blogs.msdn.com/abhinaba/archive/2005/10/18/482180.aspx.
I agree completely on the closures. My point is about the scope of the iteration variable.
foreach (var word in words)
{
...
}
translates to
{
var e = words.GetEnumerator();
string word;
while (e.MoveNext()) {
word = e.Current;
...
}
}
The immutability of the iteration variable would make the following translation much more logical IMHO:
{
var e = words.GetEnumerator();
while (e.MoveNext()) {
var word = e.Current;
...
}
}
Post a Comment