• Subject: Re: Determining which functions to call
• From: Florian Berger <fberger@...>
• Date: Wed, 22 Jan 2003 18:01:38 +0200 (EET)

```Hi.

Thanks for answers. I'm using Lua 4 so metatables were quite difficult to
understand for Lua newbie like me. I compiled Lua 5 beta and tried
metatables solution and it worked. I just have to figure out what excatly
is happening. Below is my current solution. I don't know if it's any good
but opinions are welcome.

Florian

function New(old, init)
local new = init or {}

for k,v in old do
if not new[k] then
new[k] = v
end
end

return new
end

----------------------------

-- Available functions
aryF={}
aryF[2]="Perimeter"
aryF[3]="Area"

-- Function name is index, contains return value and parameters
aryFD={}

Circle =
{
DIAMETER=6
}

return d/2
end

function Perimeter(self, r)
return 2*PI*r
end

function Area(self, r)
return PI*(r*r)
end

-----------------------------

function Find(val)
for i=1, getn(aryF) do
if aryFD[aryF[i]][1]==val then
return aryF[i]
end
end

return nil
end

-- Which function is needed to solve given variable?
function SolveFunction(self, VarName)
local FuncName=Find(VarName)
local Parameter=0
local ParameterValues={}
local str, str2

for i=2, getn(aryFD[FuncName]) do
Parameter=aryFD[FuncName][i]

if self[Parameter]==nil then
ParameterValues[i-1]=SolveVariable(self, Parameter)
else
ParameterValues[i-1]=(self[Parameter]);
end
end

str=format("temp=%s(a", FuncName)

for i=2, getn(aryFD[FuncName]) do
str=str..", "
str2=format("%d", ParameterValues[i-1])
str=str..str2
end

str=str..")"

dostring(str)

return temp
end

-- Do we know the value of given variable?
-- If not solve variable using function
function SolveVariable(self, VarName)
if self[VarName] then
return self[VarName]
else
return SolveFunction(self, VarName)
end
end

-- Solve given variable if possible
function Solve(self, str)
if type(self)=="table" then
if type(str)=="string" then
if self[str]==nil then
print(SolveVariable(self, str))
else
print(self[str]);
end
else
print("Second parameter must be a string")
end
else
print("First parameter must be a table");
end
end

---------------

a=New(Circle)
Solve(a, "DIAMETER")
Solve(a, "AREA")
Solve(a, "PERIMETER")

-- Output
-- > 3
-- > 6
-- > 28.27
-- > 18.85

>
> Here is another approach which uses Lua's amazing
> tables-are-functions-are-tables worldview. Here I assume that it is ok to
> memoise the result of function calls (which I think works in the case you
> used as an example.)
>
> The basic idea is that the object has a number of property fields which may
> or may not be present. Each one has an associated function defined in terms
> of other field(s). We assume that no function will return "false"; some
> other marker value could be used but it would be uglier. The marker is
> necessary to avoid endless recursion and to identify whether computations
> have the necessary information to proceed or not.
>
> Then we can define everything in terms of an __index metamethod:
>
> function try_to_compute(table, key)
>   local func = getmetatable(table)[key]
>   if func then
>     table.key = false       -- mark goal
>     local val = func(table) -- try to get the value
>     if val
>       then table.key = val       -- got it, so record it
>            return val            -- and return it
>       else table.key = nil       -- nope, reset for next try
>            return nil
>     end
>   end
> end
>
> Note that we use the metatable also for the functions. To set up an
> object, we need to attach the metatable, and we need to include the
> above function as the __index method. So here we go:
>
> function Searcher(props, funcs)
>   funcs.__index = try_to_compute
>   return setmetatable(props, funcs)
> end
>
> If I were doing this in real life, I would put the definition of
> try_to_compute in Searcher and use a closure instead of getmetatable.
>
> I haven't tried this, but the basic idea would be (my math is a bit rusty
> and I'm a bit sleep-deprived, so apologies in advance for typos and stupid
> errors.)
>
> do
>   local funcs = {}
>   -- example of a property with two possible formula
>     return (t.diameter and t.diameter / 2)
>            or (t.circumference and t.circumference / 2 * math.pi
>   end
>   function funcs.diameter(t)
>     return t.radius and t.radius * 2
>   end
>   function funcs.circumference(t)
>     return t.diameter and t.diameter * math.pi
>   end
>   function funcs.area(t)
>   end
>
>   function Circle(props)
>     return Searcher(props, funcs)
>   end
> end
>
> a = Circle {radius = 27}
> b = Circle {circumference = 40}
>
>
>
>

```

• References: