lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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[1]="Radius"
aryF[2]="Perimeter"
aryF[3]="Area"

-- Function name is index, contains return value and parameters
aryFD={}
aryFD["Radius"]={"RADIUS", "DIAMETER"}
aryFD["Perimeter"]={"PERIMETER", "RADIUS"}
aryFD["Area"]={"AREA", "RADIUS"}

Circle =
{
  DIAMETER=6
}

function Radius(self, d)
  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, "RADIUS")
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
>   function funcs.radius(t)
>     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)
>     return t.radius and t.radius * t.radius * math.pi
>   end
>
>   function Circle(props)
>     return Searcher(props, funcs)
>   end
> end
>
> a = Circle {radius = 27}
> b = Circle {circumference = 40}
>
>
>
>