On Wed, 04 Jan 2006 00:46:16 -0000, Daniel Schüle <uval / rz.uni-karlsruhe.de> wrote: > hello all, > > consider follow code > > irb(main):528:0* def abc > irb(main):529:1> ppp=111 > irb(main):530:1> puts ppp > irb(main):531:1> yield > irb(main):532:1> puts ppp > irb(main):533:1> ppp > irb(main):534:1> end > => nil > irb(main):536:0> abc {} > 111 > 111 > => 111 > irb(main):537:0> abc { puts ppp } > 111 > NameError: undefined local variable or method `ppp' for main:Object > from (irb):537 > from (irb):537:in `abc' > from (irb):537 > from :0 > irb(main):538:0> > irb(main):539:0* > irb(main):540:0* ppp = "OOO" > => "OOO" > irb(main):541:0> abc { puts ppp } > 111 > OOO > 111 > => 111 > irb(main):542:0> > > I am right assuming that all "local" variables in the method abc > are invisible in the block? > Yes. The block captures the scope it is defined in, not the scope it's called from, i.e. it's a closure. > I tried a little more > > irb(main):544:0* def cba > irb(main):545:1> ppp = 222 > irb(main):546:1> puts ppp > irb(main):547:1> yield ppp > irb(main):548:1> puts ppp > irb(main):549:1> ppp > irb(main):550:1> end > => nil > irb(main):551:0> cba {} > 222 > 222 > => 222 > irb(main):552:0> cba {|p|} > 222 > 222 > => 222 > irb(main):553:0> cba {|p| p = 333} > 222 > 222 > => 222 > irb(main):554:0> cba {|p| puts p; p = 333} > 222 > 222 > 222 > => 222 > irb(main):555:0> > > if explicitely passed to block ppp is now visible there. > but seems to be imposible to change the "method local" variable > ppp in this case. > Yes. Ignoring implementation details, the block parameter 'p' is given a reference to the same object. When you then assign to 'p' that reference is replaced by a new reference to the object you assigned. Note that this is only for 'p' - 'ppp' still has the same reference it always had (to the original object). The reality is apparently slightly more complex but I believe that for most practical purposes (including this)you can ignore that. > the following *does* work > > irb(main):557:0* def xxx > irb(main):558:1> ppp = [1] > irb(main):559:1> p ppp > irb(main):560:1> yield ppp > irb(main):561:1> p ppp > irb(main):562:1> ppp > irb(main):563:1> end > => nil > irb(main):564:0> xxx {} > [1] > [1] > => [1] > irb(main):565:0> xxx {|x| } > [1] > [1] > => [1] > irb(main):566:0> xxx {|x| x[0]=2 } > [1] > [2] > => [2] > irb(main):567:0> > > and I kind of understand why it works > > if I change line 566 to > irb(main):566:0> xxx {|x| x=[2] } > than it would not work > Correct. The original code doesn't actually assign anything to 'x', but instead calls the []= method on it, which modifies the array's content. Since both 'ppp' and 'x' reference the same Array instance, your change makes it out of the block. In the second case, you _do_ assign to 'x', supplying a new array. 'ppp' retains it's original value, so the new array is (almost) lost. Almost, because of course it's not _quite_ lost at that point: irb(main):001:0> def xxx irb(main):002:1> ppp = [1] irb(main):003:1> p ppp irb(main):004:1> ppp = yield ppp irb(main):005:1> p ppp irb(main):006:1> end => nil irb(main):007:0> xxx { |x| x = [2] } [1] [2] => nil > the reason I came accross this is following > I was reading > > " > Parameters to a block may be existing local variables; if so, the new > value of the variable will be retained after the block completes. This > may lead to unexpected behavior, but there is also a performance gain to > be had by using variables that already exist > " > and in my understanding all variables defined in a method > are "local" (C++ background) > > If not local what are they considered to be then? > They are local. I think that test is referring to this: irb(main):017:0> def test(arg) irb(main):018:1> p arg irb(main):019:1> [1,2,3].select { |arg| arg % 2 == 0 } irb(main):020:1> p arg irb(main):021:1> end => nil irb(main):022:0> test("ten") "ten" 3 => nil An interesting aside to this (IMHO) is this: irb(main):018:0> class Demo irb(main):019:1> def last=(arg) irb(main):020:2> (@last ||= []) << arg irb(main):021:2> end irb(main):022:1> def all irb(main):023:2> @last irb(main):024:2> end irb(main):025:1> end => nil irb(main):026:0> d = Demo.new => #<Demo:0xb7f49a6c> irb(main):027:0> ['one','two','three'].each { |d.last| } => ["one", "two", "three"] irb(main):028:0> d.last => ["one", "two", "three"] Which I guess illustrates that block arguments are handled by assignment to the named variable (or method in this case), and should make more sense of the preceeding example... Cheers, -- Ross Bamford - rosco / roscopeco.remove.co.uk