Hi,
At Thu, 5 Oct 2006 08:18:24 +0900,
Gavin Kistner wrote in [ruby-talk:218056]:
> The Situation/Requirements
> --------------------------
> A class has 2 different methods.
> Each method needs a couple 'scratch' objects to perform its
> calculations.
> The methods call each other; they must not use the same scratch objects.
> It is expensive to instantiate a scratch object.
> It's not expensive to initialize an existing scratch object with data.
> We like OOP coding, and want to call these as methods of a receiver.
>
> Wasteful Example:
> class Foo
>   def c1
>     tmp1 = ExpensiveObject.new
>     tmp2 = ExpensiveObject.new
>     # stuff involving tmp1/tmp2
>     result = tmp1 + tmp2 + c2
>   end
>   def c2
>     tmp1 = ExpensiveObject.new
>     tmp2 = ExpensiveObject.new
>     # stuff involving tmp1/tmp2
>     result = tmp1 + tmp2
>   end
> end

Like this?

$ ruby eo.rb 5
#<Foo::ExpensiveObject: 1>
#<Foo::ExpensiveObject: 2>
#<Foo::ExpensiveObject: 3>
#<Foo::ExpensiveObject: 4>
#<Foo::ExpensiveObject: 5>
[:c1,
 #<Foo::ExpensiveObject: 1>,
 #<Foo::ExpensiveObject: 2>,
 [:c2,
  #<Foo::ExpensiveObject: 3>,
  #<Foo::ExpensiveObject: 4>,
  #<Foo::ExpensiveObject: 5>,
  [:c1,
   #<Foo::ExpensiveObject: 1>,
   #<Foo::ExpensiveObject: 2>,
   [:c2,
    #<Foo::ExpensiveObject: 3>,
    #<Foo::ExpensiveObject: 4>,
    #<Foo::ExpensiveObject: 5>,
    [:c1, #<Foo::ExpensiveObject: 1>, #<Foo::ExpensiveObject: 2>, []]]]]]

$ cat eo.rb
#!/usr/bin/ruby

module ScratchArgument
  def scratch_method(name)
    meth = instance_method(name)
    define_method(name) do |*args0|
      *eo = *yield
      imeth = meth.bind(self)
      (class << self; self; end).class_eval do
        define_method(name) do |*args|
          args.unshift(*eo)
          imeth.call(*args)
        end
      end
      args0.unshift(*eo)
      imeth.call(*args0)
    end
  end
end

class Foo
  extend ScratchArgument

  class ExpensiveObject
    @@count = 0
    def initialize
      @id = @@count += 1
      p self
    end
    def inspect
      "#<#{self.class.name}: #{@id}>"
    end
  end

  def c1(tmp1, tmp2, i)
    if i <= 0
      []
    else
      [:c1, tmp1, tmp2, c2(i - 1)]
    end
  end
  scratch_method(:c1) do
    [ExpensiveObject.new, ExpensiveObject.new]
  end

  def c2(tmp1, tmp2, tmp3, i)
    if i <= 0
      []
    else
      [:c2, tmp1, tmp2, tmp3, c1(i - 1)]
    end
  end
  scratch_method(:c2) do
    [ExpensiveObject.new, ExpensiveObject.new, ExpensiveObject.new]
  end
end

result = Foo.new.c1(ARGV.empty? ? 3 : ARGV.first.to_i)
require 'pp'
pp result

-- 
Nobu Nakada