lua-users home
lua-l archive

Re: ipairs in Lua 5.3.0-alpha

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


On 2014年8月19日 18:01:00 +0200
Jan Behrens <jbe-lua-l@public-software-group.org> wrote:
> On 2014年8月19日 15:38:58 +0200
> Jan Behrens <jbe-lua-l@public-software-group.org> wrote:
> 
> > extending ipairs in such way to accept functions (and possibly creating
> > an easy to use API interface for ordinal iteration on functions AND
> > tables) would encourage library programmers to create interfaces that
> > do not distinguish between (function) iterators and (table) sequences.
> 
> With an API interface, I mean something like this:
> 
> #define luaL_geti(L, idx, i) ( \
> (*(i))++, \
> lua_type((L), (idx)) == LUA_TFUNCTION \
> ? (lua_pushvalue((L), (idx)), lua_call((L), 0, 1)) \
> : (lua_rawgeti((L), (idx), *(i))), \
> lua_isnil((L), -1) \
> ? (lua_pop((L), 1), LUA_TNIL) \
> : lua_type((L), -1) \
> )
> 
> (This is just a draft. A real implementation might want to check
> types for safety and/or respect the __call metamethod.)
And here the "real" implementation (which is doing type-checks,
respecting __call, and which does not evaluate lua_type for every
iteration step):
==================================================
#define LUA_ITERTYPE_FUNC 1
#define LUA_ITERTYPE_META 2
#define LUA_ITERTYPE_RAW 3
typedef struct {
 lua_State *L;
 int itertype;
 int i;
} luaL_Iterator;
static void luaL_iterinit(lua_State *L, luaL_Iterator *iter, int idx) {
 iter->L = L;
 if (luaL_getmetafield(L, idx, "__call")) {
 iter->itertype = LUA_ITERTYPE_FUNC;
 } else if (lua_type(L, idx) == LUA_TFUNCTION) {
 lua_pushvalue(L, idx);
 iter->itertype = LUA_ITERTYPE_FUNC;
 } else {
 if (luaL_getmetafield(L, idx, "__index")) {
 lua_pop(L, 1);
 iter->itertype = LUA_ITERTYPE_META;
 } else {
 luaL_checktype(L, idx, LUA_TTABLE);
 iter->itertype = LUA_ITERTYPE_RAW;
 }
 lua_pushvalue(L, idx);
 }
 iter->i = 0;
}
static int luaL_iternext(luaL_Iterator *iter) {
 lua_State *L = iter->L;
 int i = ++iter->i;
 switch (iter->itertype) {
 case LUA_ITERTYPE_FUNC:
 lua_pushvalue(L, -1);
 lua_call(L, 0, 1);
 break;
 case LUA_ITERTYPE_META:
 lua_pushinteger(L, i);
 lua_gettable(L, -2);
 break;
 case LUA_ITERTYPE_RAW:
 lua_rawgeti(L, -1, i);
 break;
 default:
 abort(); // should not happen
 }
 if (lua_isnil(L, -1)) {
 lua_pop(L, 2);
 return 0;
 }
 return i;
}
==================================================
The invocation is a bit more clumsy (but avoids unnecessarily
evaluating lua_type and luaL_getmetafield for every iteration step):
static int printcsv(lua_State *L) {
 luaL_Iterator iter;
 int i;
 luaL_checkany(L, 1);
 for (luaL_iterinit(L, &iter, 1); (i = luaL_iternext(&iter)); lua_pop(L, 1)) {
 if (i > 1) fputs(",", stdout);
 fputs(lua_tostring(L, -1), stdout);
 }
 fputs("\n", stdout);
 return 0;
}
If that's too clumsy, one could also do something like this:
#define luaL_loop(L, iter, idx, i) \
 for ( \
 luaL_iterinit((L), &(iter), (idx)); \
 (i = luaL_iternext(&(iter))); \
 lua_pop((L), 1) \
 )
And then write:
static int printcsv(lua_State *L) {
 luaL_Iterator iter;
 int i;
 luaL_checkany(L, 1);
 luaL_loop(L, &iter, &i, 1) {
 if (i > 1) fputs(",", stdout);
 fputs(lua_tostring(L, -1), stdout);
 }
 fputs("\n", stdout);
 return 0;
}
It still works fine:
% lua52 -l printcsv
Lua 5.2.3 Copyright (C) 1994-2013 Lua.org, PUC-Rio
> printcsv{"a", "b", "c"}
a,b,c
> printcsv(assert(io.open("testfile", "r")):lines())
line 1 in testfile,line 2 in testfile
Regards
Jan

AltStyle によって変換されたページ (->オリジナル) /