Many languages have adopted some form of the “foreach” keyword, for traversing elements of a collection. The advantages are obvious: fencepost errors are impossible, and programs are easier to read. Foreach loops are not places where I expect to find bugs. But about a month ago, I found one, in a piece of code similar to the code below. The expected behavior of the program is to print out the numbers zero and one, on separate lines. Do not read past the end of the program if you want to find the bug yourself, because I explain it below.
nums=[0,1]numclosures=[]fornuminnums:numclosures.append(lambda:num)fornumclosureinnumclosures:printnumclosure()# output from Python 2.5.2# 1# 1
The solution has to do with the implementation of the for loop in python. (I ran the program in cpython; it may be interesting to see what other implementations of python do.) Rather than creating a new binding for the num variable on every iteration of the loop, the num variable is mutated (probably for efficiency or just simplicity of implementation). Thus, even though numclosures is filled with distinct anonymous functions, they both refer to the same instance of num.
I tried writing similar routines in other languages. Ruby and C# do the same thing as Python:
usingSystem;usingSystem.Collections.Generic;publicstaticclassForeach{publicdelegateintNumClosure();// Func<int> does not exist in the latest Mono???publicstaticvoidMain(){int[]nums=newint[]{0,1};List<NumClosure>numclosures=newList<NumClosure>();foreach(intnuminnums){numclosures.Add(delegate(){returnnum;});}foreach(NumClosurenumclosureinnumclosures){Console.WriteLine(numclosure());}}}// output from Mono 1.2.6// 1// 1
Please excuse the use of the NumClosure delegate. For some reason I could not get Mono to compile with Func.
Fortunately, all of these languages provide some kind of work-around. Ruby has Array#each, and C# has List<>.ForEach. Python has the map built-in.
usingSystem;usingSystem.Collections.Generic;publicstaticclassForeach{publicdelegateintNumClosure();// Func<int> does not exist in the latest Mono???publicstaticvoidMain(){int[]nums=newint[]{0,1};List<NumClosure>numclosures=newList<NumClosure>();newList<int>(nums).ForEach(delegate(intnum){numclosures.Add(delegate(){returnnum;});});foreach(NumClosurenumclosureinnumclosures){Console.WriteLine(numclosure());}}}// output from Mono 1.2.6// 0// 1
Not everybody mutates their enumerators, however. Lisp, the language which normally requires every programmer to be an expert in variable scoping, handles iteration very cleanly: