From: "Fitzgerald,Greg" Date: 2004-08-04T05:38:38+09:00 Subject: Re: Closure question > >I'm trying to make a function act as a counter, but I can't > seem to get > >it to work. Here's what I've tried: > > > >def count > > counter =3D 1 > > def count > > proc {counter +=3D 1}.call > > end > > counter > >end > > > > > >I was hoping for this: > >count =3D> 1 > >count =3D> 2 > >count =3D> 3 > > > >Instead, I got this: > >count =3D> 1 > >count =3D> NoMethodError: undefined method '+' for nil:NilClass > > > >Isn't the idea of a closure that everything in the block is > executed in > >the same scope it was defined? It seems that the name 'counter' is > >referenced under the name 'count', and so when I redefine > 'count', the > >'counter' variable gets lost. Anyone know what's really > happening here? > > > > The problem is that you've defined a new scope with the > definition of the inner 'count' method, so the 'counter' > variable you're referring to inside of the proc is not the > same as the one defined in the outer count method. > Also, everytime you call 'count', doesn't the 'counter' > variable get reinitialized to '1'? And, it's not clear to me > how the inner 'count' > method would ever get called. I believe you're seeing the > error because both the inner and the outer 'count' methods > are both called 'count'. > > I modified your code somewhat to remove the name collision: > > irb(main):064:0> def foo > irb(main):065:1> counter = 1 > irb(main):066:1> def bar > irb(main):067:2> proc {counter += 1}.call > irb(main):068:2> end > irb(main):069:1> bar #-> note this added call to bar > irb(main):070:1> counter > irb(main):071:1> end > => nil > irb(main):072:0> foo > NoMethodError: undefined method `+' for nil:NilClass > from (irb):67:in `bar' > from (irb):67:in `call' > from (irb):67:in `bar' > from (irb):69:in `foo' > from (irb):72 > from :0 > > .... so the inner method, bar, knows nothing about the > 'counter' variable defined in the outer scope. > > > > Here's how I'd do a counter, but it doesn't involve closures, > so maybe it > doesn't address your question: > > class Counter > def initialize > @counter = 1 > end > > def count > @counter += 1 > end > end > > cc = Counter.new > cc.count #=> 2 > cc.count #=> 3 > > Phil > > Jim and Pit hit the nail on the head with: def count counter = 1 self.class.send(:define_method, :count) { counter += 1 } counter end The idea is to redefine the 'count' function so that after it is called the first time, the line 'counter = 1' is never executed again. Instead, when 'count' is called, only 'counter += 1' is executed. The new 'count' function will now reference the original 'counter' variable that existed at the time the closure was created. Good fun. -Greg CONFIDENTIALITY NOTICE This message and any included attachments are from Cerner Corporation and are intended only for the addressee. The information contained in this message is confidential and may constitute inside or non-public information under international, federal, or state securities laws. Unauthorized forwarding, printing, copying, distribution, or use of such information is strictly prohibited and may be unlawful. If you are not the addressee, please promptly delete this message and notify the sender of the delivery error by e-mail or you may call Cerner's corporate offices in Kansas City, Missouri, U.S.A at (+1) (816)221-1024. ---------------------------------------- --