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