From: ara.t.howard@... Date: 2006-09-08T22:59:20+09:00 Subject: Re: Puzzling bug with yielded array On Fri, 8 Sep 2006, A. S. Bradbury wrote: > I have a Node class, children are stored in a hash with the node's name as the > key and the child node object as the value. I have the method Node#each_level > that yields each level of the node tree as an array: > > def each_level(include_self=false) > if include_self > node_queue=[self] > else > node_queue=self.children.values > end > yield node_queue > while node_queue.any? {|node| node.children.empty? == false} do > node_queue.collect! {|node| node.children.values} > node_queue.flatten! > yield node_queue > end > end the problem is here. this illustrates the issue: harp:~ > cat a.rb def yields_shared_then_munged_list list = %w[ forty-two ] yield list list.collect!{|elem| elem.upcase} yield list end yields_shared_then_munged_list{|list| p list} harp:~ > ruby a.rb ["forty-two"] ["FORTY-TWO"] your method first returns a list to the caller but, later, munges it in place without the caller's consent. this is as evil as returning pointer to member in c--. > Here is the code that demonstrates the problem: > > require 'ariel' > > root=Ariel::Node.new :root > child1=Ariel::Node.new :child1 > child2=Ariel::Node.new :child2 > child1_1=Ariel::Node.new :child1_1 > > root.add_child child1 > root.add_child child2 > child1.add_child child1_1 > > results=[] > > puts "Results when making an array then flattening it" > root.each_level do |level| > puts "Yielded #{level.inspect}" > puts > raise StandardError unless level.kind_of? Array > results << [level].flatten # works because you are making a copy. so the traversal code does fubar the elements of results in place. > end > > puts "results=#{results.inspect}" > puts > > results=[] > > puts "Results when just adding the array" > root.each_level do |level| > puts "Yielded #{level.inspect}" > puts > raise StandardError unless level.kind_of? Array > raise StandardError if level.any? {|val| val.kind_of? Array} > results << level # doesn't work because results contains a reference to the yielded array (not a copy) so the subsequent calls to 'collect!', etc. scramble it in place. kind regards. -a -- what science finds to be nonexistent, we must accept as nonexistent; but what science merely does not find is a completely different matter... it is quite clear that there are many, many mysterious things. - h.h. the 14th dalai lama