Mark:
Only problem would be if you use a similar but not equal module and
you stomp around each other in string.
> Set.lua is the module I'm concerned with. I'm not too bothered about efficiency, but my questions here are:
> - Is the module in good lua style?
I think you are messing around too much with the metatables. In
Set:new you asume you are being called with colon notation and
(idempotently, if correctly called ) rebuild the metatable each time.
Normally it is better to build it just once and IMO, not use "method
call" syntax for new. And make the metatable private.
If you do
-- Confine the methods to a table:
local set_methods = {}
function set_methods:add(...)......
-- Now lets go for the metatable:
local set_meta = {
__index = set_methods, -- This will send method calls to the correct point..
}
function set_meta:__len().,... -- This will make # work.
-- Although I would prefer to have a method too, not a fan of
excessive operator overriding, that can be done like this:
function set_methods:len() -- Build the method.
set_meta.len = set_methods.len --
-- Rest can be deducted, now the basic function to build a Set would be:
local function build_Set(...)
-- Done in steps:
local self = { items_ = {}, size_ = 0 } -- We are not in a method
but this simplifies reading.
setmetatable(self, set_meta)
self:add(...)
return self
end
-- For this kind of things I normally make the add method chainable (
return self ), which has among other things the benefit of
function build_Set_with_chainable_add(...)
return setmetatable( { items_ = {}, size_ = 0 } , set_meta):add(...)
end
Anyway, the thing is how to make this work as a class. One easy way is
to end your module with:
return build_set;
-- This will allow you to do this in users code:
local Set = require "Lib/Set"
local s = Set(1,2,3)
-- Or, if you add more classes you can do:
return {
Set = build_set,
Bag = build_bag,
}
and something like DS=require"data_structs"; my_set = DS.Set()
But this comes with practice, the big thing is to 1.- build your
methods and metatables first, not only to avoid rebuilds, but to keep
it tidier, and 2.- remember "constructors" in a basic object dessign
are just functions, so no : normally. Of course you can go in a way
that makes classes objects and constructors methods on them, but this
is just a kind of factory pattern and has its own chicken and egg
problem.
> - I want insersect to be a function and the & operator; similarly for union. I have this working, but am I doing it the best way? (See Set:new)
Note your
set.new nearlyu assumess Set==self ( my build_set show a way
to skip that ).
For the intersection trick, do the same I showed for len()/__len(),
nearly equal to what you did.
In fact given intersection returns a new Set I normally prefer:
local function set_intersect(set1,set2)..... return new_set end
set_methods.intersect = set_intersect
set_meta.band = set_intersect ( or even set_methods.intersect for more
complex cases, for future proofing ).
> - Is there a better (faster) way to get a random element?
Difficult to do with the way you store it, but if you use it a lot you
can dessign around.
> - Similarly for the iter() iterator?
Sorted is not your problem there. I see among other things you seem to
be relying on only having strings but not checking that. That may be a
harder problem.
You may be able to build the array a bit faster by directly calling
next, something like:
local sitems = self.items
local items = {}
local item = nil
for i=1,math.maxinteger do
item = next(sitems, item)
if item == nil then break end
items[i] = item
end
Or even something like:
local sitems = self.items
local items = {}
local item = next(sitem)
local i=1
while (item ~= nil) do
items[i] = item
i=i+1
item = next(sitems, item)
end
But as always, measure twice cut once.
I'll look at your check questions after lunch :)
Francisco Olarte.