From: "Aleksi Niemelä" Date: 2000-11-22T16:48:42+09:00 Subject: [ruby-talk:6514] RE: Question about the behavior of write attribu tes in blocks I changed the code a little, to make it more illustrative. The revised code is in the end of the file. Please, forgive me changing the strings a little. > root.each { |a| a = "try " } # ``data'' is not changed > root.each { |a| a.replace "succ " } # ``data'' is changed The difference between these two snippets is just what you commented. When the associated block gets called > def each > yield data what exactly block variable 'a' starts to refer at. Well, in the first call to the block your datastructure is like this: #, @data="fail 1">, @data="root data"> 'a' starts to refer the same string as Bt:0x40178894's @data, that is the bottommost "root data ". Then, in the block we assign a = "try " happens the same thing as: b = "gets" c = b c = "fooled" puts b, c outputting gets fooled So in this example c starts to point to same string as b, that is object of class String containing a string "gets". When c is assigned to refer object "fooled" b continues to refer to "gets". At any point c is not "pointing" to variable b itself, and can't alter where b refers to. Then the second case. When this block is called for the same data > root.each { |a| a.replace "succ " } # ``data'' is changed block variable 'a' starts to refer the same string "root data". But instead of reassign 'a' to point some other string, we invoke the object's method replace and pass the string "succ " to it. So the very same object that @data was pointing replaces it's contents with different string, so there's effectively a change in the data structure. Hopefully this cleared things a bit. Attached is a slightly modified code, with convenient dumper method. BTW. I'm sure you noticed there's a big difference between these two constructs: Bt.new ( "fail root", Bt.new "fail 1", Bt.new "fail 2") Bt.new ( "fail root", Bt.new("fail 1"), Bt.new("fail 2")) In the resulting data structures the first one doesn't have right branch at all #, @data="fail 1">, @data="root data"> like root / \ fail 1 nil / \ fail2 nil / \ nil nil and the second one is well balanced case :). #, @left=#, @data="root data"> like root / \ fail 1 fail 2 / \ / \ nil nil nil nil - Aleksi #*********************** # A binary tree class class Bt def initialize( cont, l = nil, r = nil ) @data=cont @left=l @right=r end def each yield data left.each { |l| yield l } right.each { |r| yield r } end attr_accessor( :left, :right, :data) end module Each def each end end nil.extend(Each) def dumper(obj) indent = 0 str = obj.inspect while str.length > 0 if m = /(#<.+? )|(, (?=@))|(>, )/.match(str) if m[1] indent += 1 print $` + $& + "\n" + " " * indent str = $' elsif m[2] print $` + ",\n" + " " * indent str = $' elsif m[3] indent -= 1 print $` + $& + "\n" + " " * indent str = $' end else indent -= 1 puts " " * indent + str str = "" end end end [Bt.new ( "fail root", Bt.new "fail 1", Bt.new "fail 2"), Bt.new ( "fail root", Bt.new("fail 1"), Bt.new("fail 2")) ].each do |root| # ``data'' is changed root.data="root data" # ``data'' is not changed root.each { |a| dumper(root); a = "try"; dumper(root); puts; } root.each { |a| print a, " " } puts dumper(root) puts "Then the real change" # ``data'' is changed root.each { |a| dumper(root); a.replace "succ"; dumper(root); puts; } root.each { |a| print a, " " } puts dumper(root) puts "\n" * 3 end