lua-users home
lua-l archive

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


That is a cleaver solution - thanks for sharing!

It is essentially the same as the one discussed earlier by LHdeF, except
that you generalise it using the double-indexing trick. Presumably you have
further mechanisms to maintain the integrity of the double-index which could
get quite complex (for example, if you have a dictionary-like collection and
allow selective deletion in the Generic For, you'd need a way of getting to
the string key from the numeric key).

Also you have to compact the numeric index for random deletions to avoid
holes developing? This could get expensive!

-----Original Message-----
From: lua-bounces@bazar2.conectiva.com.br
[mailto:lua-bounces@bazar2.conectiva.com.br] On Behalf Of Ronald Lamprecht
Sent: 16 December 2009 23:09
To: Lua list
Subject: Re: '__iter', yet again!

Hi,

John Hind wrote:
> I am trying to write an object oriented library in which you can have 
> objects for which the following is valid:
> 
> for v in obj do ... end
> 
> Using the __call metamethod you can easily get to:
> 
> for v in obj() do ... end
> 
> But the redundant parenthesis rankles.

In most cases you can simply eliminate the need of the parenthesis by 
double indexing the container object. I noticed this usefull concept 
when reengineering the Lua API for our project two years ago. I 
implemented a set like container called "group" supporting joins, 
intersections, etc. The for loop is as simple as:

for obj in group do obj:toggle() end

The implemention is very simple and does not require any Lua change at all.

In OO you can rely on objects having a unique id. You just need to store 
the objects twice in your container - once at their regular index and 
once at their id used as index. The "group" is in fact Lua table based 
and stores the objects at standard numerical indices, which makes the 
concepts implementation very straight forward.

The __call has no need to store the iteration state, as the last object 
with its unique id fulfills this purpose:

static int iteratorGroup(lua_State *L) {
     // generic for loop iterator function
     // var_1 = _f(_s, _var) with _var == nil on first access, var_1 == 
nil on end
     // on stack: group, _s, _var
     if (!(is_group(L, 1)))
         throwLuaError(L, "Group: iterator first arg not a group");
     lua_getmetatable(L, 1);
     int size = lua_objlen(L, -1);
     if (lua_isnil(L, 3)) {   // first iterator loop access
         if (size == 0) {     // an empty group
             lua_pushnil(L);
             return 1;
         } else {
             lua_rawgeti(L, -1, 1);  // get the first object
             return 1;
         }
     } else {
         lua_pushvalue(L, 3);   // copy last object as key
         lua_rawget(L, -2);     // get last index
         int i = lua_tointeger(L, -1);
         lua_rawgeti(L, -2, ++i);  // get next object
         return 1;
     }
}

Be aware that the implementation depends on the containers index usage 
and the object id's nature. But the concept can easily been modified for 
other index and id types.

This implementation is up and running and is meanwhile being used by 
multiple ten thousand lines of code.

>> you are
>> wrongfully introducing object oriented concepts in a language that is
>> not OO.
> 
> I was about to write this, but you were faster. Thanks,

I agree that in most cases it is preferable not to reveal underlying OO 
technology in the Lua part to its full extend. A simple object based API 
   that hides complex concepts like inheritance can be more suitable.

Greets,

Ronald