lua-users home
lua-l archive

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


Wim escribió:

> On the other hand, I think that Python tuples can be emulated by Lua 
tables
(arrays) if you use a proxy.  Simple approach:

<snip>

Here's an alternative which doesn't make as_table freely available:

function tuple(...)
  return setmetatable({}, {
    __index = function(_, index) return arg[index] end,
    __newindex = function() error "tuples are immutable!" end
  })
end

Unfortunately, this creates three tables (one empty) and a closure
for each tuple, while Wim's original only creates two tables for
each closure.

For efficiency, we can use a standard weak-table registry solution:

do
  local tuples = setmetatable({}, {__mode = "k"})

  local meta = {
    __index = function(self, index) return tuples[self][index] end,
    __newindex = function() error "tuples are immutable!" end
  }

  function tuple(...)
    local self = setmetatable({}, meta)
    tuples[self] = arg
    return self
  end
end

Now, the only problem we have is that if tuple A is a member of itself 
(possibly through a chain of other tuples or objects), then the garbage 
collector will never clean the tuple out of the weak table. This certainly 
cannot happen if the tuples are always simple objects, like strings or 
numbers.

Neither of these solutions solves the tuple-equality problem I mentioned 
earlier, though.

For a specific tuple implementation, like a vector, you can also use a 
functional approach (this is more or less adapted from SICP, I think) 
which also leaves the tuple-equality problem hanging:

function vector(_x, _y, _z)
  return function(fn) return fn(_x, _y, _z) end
end

function x(_x, _y, _z) return _x end
function y(_x, _y, _z) return _y end
function z(_x, _y, _z) return _z end
function set_x(_x)
  return function(_, _y, _z) return vector(_x, _y, _z) end
end
function set_y(_y)
  return function(_x, _, _z) return vector(_x, _y, _z) end
end
function set_z(_z)
  return function(_x, _y, _) return vector(_x, _y, _z) end
end

> p1 = vector(1, 2, 3)
> =p1(x)
1
> =p1(z)
3
> =p1(print) -- :)
1       2       3
>
> =p1(set_x(42))(print)
42      2       3

That this has considerable expressive power can be seen from:

function vlength(_x, _y, _z) return math.sqrt(_x * _x + _y * _y + _z * _z) 
end

function vadd(v2)
  return function(_x, _y, _z)
    return vector(_x + v2(x), _y + v2(y), _z + v2(z))
  end
end

-- p1 was not changed by the set_x above
> =p1(print)
1       2       3

> =p1(vlength)
3.7416573867739

> =p1(vadd(p1))(print)
2       4       6

Obviously, the accessor and mutator functions above could be easily 
generated automatically from a list of member names. The syntax could be 
improved by using metamethods; this could also be used to put accessors 
and mutators in an appropriate namespace. Unfortunately, function closures 
cannot have metamethods, but you can achieve a very similar effect with a 
table with a __call metamethod.

(This solution does put a bit of a load on the memory management system. 
:) )

R.


R.