From: Gavin Kistner Date: 2006-10-05T08:18:24+09:00 Subject: Associating data with a function Because I just had to solve this problem in both JavaScript and Lua, and because I love Ruby more than either, I thought I'd ask people here how they'd solve it in Ruby. The general question is: how would do you associate a few object instances with methods on a per-method basis? 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 The Solution We'll Ignore -------------------------- We *could* create a distinct instance variable for each needed variable. I personally don't like the weird coupling this creates, though. So I'm ignoring it. class Foo def initialize @tmp1 = ExpensiveObject.new @tmp2 = ExpensiveObject.new @tmp3 = ExpensiveObject.new @tmp4 = ExpensiveObject.new end def c1 tmp1, tmp2 = @tmp1, @tmp2 #... end def c2 tmp1, tmp2 = @tmp3, @tmp4 #... end end The JavaScript Solution ----------------------- The solution I used for JavaScript was that functions/methods are first-class objects that can have arbitrary data assigned, and can get a reference to themselves during execution. So: Foo.prototype.c1 = function( ) { var tmp1 = arguments.callee.eo1; var tmp2 = arguments.callee.eo2; //... } Foo.prototype.c1.eo1 = new ExpensiveObject; Foo.prototype.c1.eo2 = new ExpensiveObject; There were a few potential solutions for Lua. I'll list the two best, just to help give you ideas: Lua Solution #1: Closures (would work for JS, too) -------------------------------------------------- Foo.c1 = ( function( ) local tmp1 = ExpensiveObject:new( ) local tmp2 = ExpensiveObject:new( ) return function( self ) -- stuff using tmp1 and tmp2 end end)( ) Lua Solution #2: Changing the global environment for a function --------------------------------------------------------------- function associateFunctionData( inF, inData ) setfenv(inF, setmetatable(inData,{__index=getfenv(inF)})) end function Foo:c1( ) -- stuff using tmp1 and tmp2 -- as though they were globals end associateFunctionData( Foo.c1, { tmp1 = ExpensiveObject:new( ), tmp2 = ExpensiveObject:new( ) } )