From: Robert Klemme Date: 2007-11-21T00:27:30+09:00 Subject: Re: using lambda, binding happens immediately? 2007/11/20, Sam Larbi : > Thanks Robert, > > I have some more questions / responses below. > > > On Nov 20, 2007 6:45 AM, Robert Klemme wrote: > > > 2007/11/19, Sam Larbi : > > > I have this code: > > > > > > > > > require 'test/unit' > > > require 'leapyear' > > > class LeapYearTest < Test::Unit::TestCase > > > def setup > > > @ly = LeapYear.new > > > end > > > def LeapYearTest.generate_tests > > > filename = "testdata.dat" > > > file = File.new(filename, "r") > > > file.each_line do |line| > > > year, is_leap = line.split; > > > code = lambda { assert_equal(is_leap.downcase=="true", @ly.isleap > > ?( > > > year.to_i)) } > > > define_method("test_isleap_" + year, code) > > > end > > > file.close > > > end > > > end > > > > > > > > > What I want to know is why when I use define_method("name") {block;} > > instead > > > of define_method("name", lambda{block}) the binding seems to happen > > later > > > (after the each_line loop)? > > > > > > Is there a reason behind that? > > > > I'm not sure I understand you but why don't you just do > > > > Well, the problem is that attaching the block uses only the last "year" to > be processed in the loop, never the appropriate one. Then you are using "year" outside the block as well and the code you have shown is not the code you actually run. $ ruby /cygdrive/c/sc.rb.txt foo bar 16:23:16 $ cat /cygdrive/c/sc.rb.txt class Foo def self.create(words) words.each do |w| define_method("test_#{w}") { puts w } end end end Foo.create %w{foo bar} Foo.new.test_foo Foo.new.test_bar 16:23:28 $ ruby /cygdrive/c/sc.rb.txt bar bar 16:24:18 $ cat /cygdrive/c/sc.rb.txt class Foo def self.create(words) w = nil words.each do |w| define_method("test_#{w}") { puts w } end end end Foo.create %w{foo bar} Foo.new.test_foo Foo.new.test_bar 16:24:19 > > define_method("test_isleap_" + year) do > > assert_equal(is_leap.downcase=="true", @ly.isleap?(year.to_i)) > > end > > > > My understanding is that this would do the same as define_method(...) { > ...}, so I would expect to have the same problem. > > Basically, lets say the file testdata.dat has the following lines: > > 2000 true > 2004 true > 2001 false > > Then running the code above (with leapyear class defined as well) would use > 2001 as the date for each of the tests if I used the block attachment as > opposed to passing through the arguments after using lambda (although the > tests would be named correctly). > > So basically, It would generate the following: > > test_isleap2000 -> but the date inside is 2001 > test_isleap2004 -> but the date inside is 2001 > test_isleap2001 -> the test is correct > > Then, if I use lambda, the tests are all correct. > > Does that make any more sense? I am afraid, no (see above). Kind regards robert -- use.inject do |as, often| as.you_can - without end