From: Ara.T.Howard@... Date: 2004-11-05T11:23:44+09:00 Subject: Re: Cleaner way to do this? 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 ===============================================================================