From: Robert Klemme Date: 2007-10-02T23:30:14+09:00 Subject: Re: Stupid error on blocks 2007/10/2, Federico Brubacher : > > > That's one way to do it. Actually only a smaller change is needed > > because the root cause in this case is the scoping rule of the > > iteration variable in a "for" loop which is different than when > > iterating with a block as this example demonstrates: > > > > CORRECT : > > > names.each do |name| > > define_method("m1_#{name}") do > > puts "1_#{name}" > > end > > end > > INCORRECT ! > > > > for name in names do > > define_method("m2_#{name}") do > > puts "2_#{name}" > > end > > end > > > Wow thanks Robert that was of help ... > > So if i get this straight : what happens is that in the execution time > as the for iterated over all the array until the end the variable that > would be the one that gets printed out. If we use each to iterate over a > block the scope of the name variable is saved and stays the same (it > closes on itself) thus we get the correct name variable everytime. > > Am i right !? Yep. With #each you get multiple bindings that contain the same name because the block opens a new scope. With for you just get one binding that contains the variable and so all created methods share that same value because they fetch it from the binding. The concept is known as closure. That's the reason why this works: $ ruby/counter.rb c1:1 c1:2 c1:3 c2:101 c2:102 c2:103 c2:104 c2:105 RKlemme@padrklemme1 ~ $ cat ruby/counter.rb #!ruby def create_counter(initial = 0) lambda { initial += 1 } end c1 = create_counter c2 = create_counter 100 3.times { print "c1:", c1.call, "\n" } 5.times { print "c2:", c2.call, "\n" } The lambdas carry around the binding to the scope of the method create_counter when it was invoked. As you can see, there were two invocations and so you get two independent counters. The example is similar to the #each version. As you can see in this example, both closures share the same environment and thus share the counter value: RKlemme@padrklemme1 ~ $ ruby/counter-2.rb c1:1 c1:2 c1:3 c2:5 c2:7 c2:9 c2:11 c2:13 c1:14 c1:15 c1:16 RKlemme@padrklemme1 ~ $ cat ruby/counter-2.rb #!ruby def create_counter(initial = 0) return lambda { initial += 1 }, lambda { initial += 2 } end c1, c2 = create_counter 3.times { print "c1:", c1.call, "\n" } 5.times { print "c2:", c2.call, "\n" } 3.times { print "c1:", c1.call, "\n" } Kind regards robert