On Fri, 5 Nov 2004, Harry Ohlsen wrote:

> Hi,
>
> One of my colleagues asked me last night how he could do some processing
> on every object of an array, with different processing for the first
> item ... but without generating an intermediate variable.
>
> An example would be doing the equivalent of
>
>   puts a.join(", ")
>
> without having a temporary string created.  The temporary string isn't
> an issue normally, but imagine if the array is huge.
>
> Trying to make it reasonably flexible, I came up with the following, but
> I figure there's probably some cleaner way to do it.
>
> Maybe there's a method I don't know about that allows for this kind of
> thing. Any suggestions?
>
> Pardon the method name ... it's only a model :-)
>
> class Array
>   def first_and_rest(first_time)
>      first_time.call self[0]
>
>      (1 .. self.length - 1).each do |i|
>         yield self[i]
>      end
>   end
> end
>
> a = [1, 2, 3, 4, 5]
>
> File.open("test.txt", "w") do |out|
>   a.first_and_rest(proc {|x| out.print x}) do |x|
>      out.print ", #{x}"
>   end
>
>   out.puts
> end

hard to profile with IO involved but:

   harp:~ > ruby a.rb
   1048576
   -------------------------------------------------------------------------------
   first_and_rest
   -------------------------------------------------------------------------------
   7.89007091522217
   549755289600
   -------------------------------------------------------------------------------
   ruby objects are copy on write references - no copy is made here!
   -------------------------------------------------------------------------------
   6.67814898490906
   549755289600
   -------------------------------------------------------------------------------
   is testing __really__ that slow?
   -------------------------------------------------------------------------------
   13.0074229240417
   549755289600

   harp:~ > cat a.rb
   def time label
     puts('-' * 79)
     puts label
     puts('-' * 79)
     a = Time::now
     yield
     b = Time::now
     puts(b.to_f - a.to_f)
   end

   class Array
     def first_and_rest(first_time)
       first_time.call self[0]
       (1 .. self.length - 1).each do |i|
       yield self[i]
       end
     end
   end

   huge = (0...(2 ** 20)).to_a
   p huge.size


   sum = 0
   time('first_and_rest') do
     huge.first_and_rest(proc {|x| sum += x}) do |x|
       sum += x
     end
   end
   p sum

   sum = 0
   time('ruby objects are copy on write references - no copy is made here!') do
     first = huge.first
     sum += first
     huge.last(huge.size - 1).each{|x| sum += x}
   end
   p sum

   sum = 0
   time('is testing __really__ that slow?') do
     huge.each_with_index do |x, i|
       sum += (i == 0 ? (x * 2) : x)
     end
   end
   p sum


so testing is slow, however i couldn't really tell the difference until a
million entries or so.  in any case i think your friend isn't clear on the
meaning of 'created' - unless you modify the first element no copy is made -
you just get a reference which costs basically nothing.  to prove it:

   harp:~ > ruby b.rb
   1048576
   -------------------------------------------------------------------------------
   it's plenty fast to take references
   -------------------------------------------------------------------------------
   3.60012054443359e-05
   -------------------------------------------------------------------------------
   but slow(er) if you actually copy on write
   -------------------------------------------------------------------------------
   36.049026966095


   harp:~ > cat b.rb
   def time label
     puts('-' * 79)
     puts label
     puts('-' * 79)
     a = Time::now
     yield
     b = Time::now
     puts(b.to_f - a.to_f)
   end

   huge = '_' * (2 ** 20)
   p huge.size


   time("it's plenty fast to take references"){ 42.times{ s = huge } }
   time("but slow(er) if you actually copy on write"){ 42.times{ (s = huge).gsub(%r/./,'!') } }


IMHO the copy on write semantics of ruby assignment is one of it's nicest
features - it's a lot easier to shoot your self in the foot with memory
allocation in perl.


kind regards.

-a
--
===============================================================================
| EMAIL   :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE   :: 303.497.6469
| When you do something, you should burn yourself completely, like a good
| bonfire, leaving no trace of yourself.  --Shunryu Suzuki
===============================================================================