[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: ipairs in Lua 5.3.0-alpha
- From: Jan Behrens <jbe-lua-l@...>
- Date: Sun, 17 Aug 2014 23:53:45 +0200
On Sun, 17 Aug 2014 19:26:53 +0200
Jan Behrens <jbe-lua-l@public-software-group.org> wrote:
> do
>
> local function ipairsaux_raw(t, i)
> i = i + 1
> local v = rawget(t, i)
> if v then
> return i, v
> else
> return nil
> end
> end
>
> local function ipairsaux_meta(t, i)
> i = i + 1
> local v = t[i]
> if v then
> return i, v
> else
> return nil
> end
> end
>
> local function ipairsaux_metalen(t, i)
> i = i + 1
> local v = t[i]
> if v ~= nil then
> return i, v
> else
> -- Roberto's idea: evaluate len only if v == nil
> if i <= #t then
> return i, nil
> else
> return nil
> end
> end
> end
>
> local function ipairsaux_func(f, i)
> local v, v2, v3, v4, vn = f()
> -- variable arg number requires C implementation
> if v then
> return i + 1, v, v2, v3, v4, vn
> else
> return nil
> end
> end
>
> local empty = {}
>
> function supercool_ipairs(x, s, i)
> local mt = getmetatable(x) or empty
> local mt_ipairs = rawget(mt, "__ipairs")
> if mt_ipairs ~= nil then
> return mt_ipairs(x)
> elseif type(x) == "function" then
> if s == nil and i == nil then
> return ipairsaux_func, x, 0
> else
> local n = 0
> return function() -- closure not avoidable here
> n = n + 1
> local v, v2, v3, v4, vn = x(s, i)
> -- variable arg number requires C implementation
> if v == nil then
> return nil
> else
> i = v
> return n, v, v2, v3, v4, vn
> end
> end
> end
> else
> local mt_call = rawget(mt, "__call")
> if mt_call then
> return supercool_ipairs(mt_call, s, i)
> elseif rawget(mt, "__len") ~= nil then
> return ipairsaux_metalen, x, 0
> elseif rawget(mt, "__index") ~= nil then
> return ipairsaux_meta, x, 0
> else
> return ipairsaux_raw, x, 0
> end
> end
> end
>
> end
I translated this to C, to give a short proof-of-concept
(in case somebody is interested in experimenting with it):
============================================================
#include <lua.h>
#include <lauxlib.h>
#define POWERITERATOR_GLOBAL
int poweriterator_ipairsaux_raw(lua_State *L) {
lua_Integer i;
luaL_checktype(L, 1, LUA_TTABLE);
i = luaL_checkinteger(L, 2) + 1;
lua_pushinteger(L, i);
lua_rawgeti(L, 1, i); // TODO: Lua 5.3 returns type
return lua_isnil(L, -1) ? 1 : 2;
}
int poweriterator_ipairsaux_meta(lua_State *L) {
lua_Integer i;
i = luaL_checkinteger(L, 2) + 1;
lua_pushinteger(L, i);
lua_pushinteger(L, i);
lua_gettable(L, 1); // TODO: Lua 5.3 returns type
return lua_isnil(L, -1) ? 1 : 2;
}
int poweriterator_ipairsaux_metalen(lua_State *L) {
lua_Integer i;
i = luaL_checkinteger(L, 2) + 1;
lua_pushinteger(L, i);
lua_pushinteger(L, i);
lua_gettable(L, 1); // TODO: Lua 5.3 returns type
return (lua_isnil(L, -1) && i > luaL_len(L, 1)) ? 1 : 2;
}
int poweriterator_ipairsaux_func(lua_State *L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_pushinteger(L, luaL_checkinteger(L, 2) + 1);
lua_insert(L, 1);
lua_settop(L, 2);
lua_call(L, 0, LUA_MULTRET);
if (lua_isnoneornil(L, 2)) {
lua_settop(L, 0);
lua_pushnil(L);
return 1;
} else {
return lua_gettop(L);
}
}
int poweriterator_ipairsaux_funcclosure(lua_State *L) {
lua_Integer i = lua_tointeger(L, lua_upvalueindex(4)) + 1;
lua_settop(L, 0);
lua_pushinteger(L, i);
lua_replace(L, lua_upvalueindex(4));
lua_pushinteger(L, i);
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushvalue(L, lua_upvalueindex(3));
lua_call(L, 2, LUA_MULTRET);
if (lua_isnoneornil(L, 2)) {
lua_settop(L, 0);
lua_pushnil(L);
return 1;
} else {
lua_pushvalue(L, 2);
lua_replace(L, lua_upvalueindex(3));
return lua_gettop(L);
}
}
int poweriterator_ipairs(lua_State *L) {
lua_settop(L, 3);
if (luaL_getmetafield(L, 1, "__ipairs")) {
lua_pushvalue(L, 1);
lua_call(L, 1, LUA_MULTRET);
return lua_gettop(L) - 3;
}
poweriterator_ipairs_repeat:
if (lua_type(L, 1) == LUA_TFUNCTION) {
if (lua_isnil(L, 2) && lua_isnil(L, 3)) {
lua_pushcfunction(L, poweriterator_ipairsaux_func);
} else {
lua_pushinteger(L, 0);
lua_pushcclosure(L, poweriterator_ipairsaux_funcclosure, 4);
return 1;
}
} else if (luaL_getmetafield(L, 1, "__call")) {
lua_replace(L, 1);
goto poweriterator_ipairs_repeat;
} else if (luaL_getmetafield(L, 1, "__len")) {
lua_pushcfunction(L, poweriterator_ipairsaux_metalen);
} else if (luaL_getmetafield(L, 1, "__index")) {
lua_pushcfunction(L, poweriterator_ipairsaux_meta);
} else {
lua_pushcfunction(L, poweriterator_ipairsaux_raw);
}
lua_pushvalue(L, 1);
lua_pushinteger(L, 0);
return 3;
}
int poweriterator_crimp_closure(lua_State *L) {
lua_settop(L, 0);
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushvalue(L, lua_upvalueindex(3));
lua_call(L, 2, LUA_MULTRET);
if (lua_isnoneornil(L, 1)) {
lua_settop(L, 1);
return 1;
} else {
lua_pushvalue(L, 1);
lua_replace(L, lua_upvalueindex(3));
return lua_gettop(L);
}
}
int poweriterator_crimp(lua_State *L) {
if (lua_isnoneornil(L, 2) && lua_isnoneornil(L, 3)) {
lua_settop(L, 1);
return 1;
}
lua_settop(L, 3);
lua_pushcclosure(L, poweriterator_crimp_closure, 3);
return 1;
}
static const struct luaL_Reg poweriterator_module_functions[] = {
{"ipairs", poweriterator_ipairs},
{"crimp", poweriterator_crimp},
{NULL, NULL}
};
int luaopen_poweriterator(lua_State *L) {
#ifdef POWERITERATOR_GLOBAL
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
luaL_setfuncs(L, poweriterator_module_functions, 0);
#endif
luaL_newlib(L, poweriterator_module_functions);
return 1;
}
============================================================
This module registers two global functions "ipairs" and "crimp".
* "ipairs" works just like "supercool_ipairs" that I posted earlier.
* "crimp" is a helper function that can be used to compress an
iterator triplet to a single closure. If it's called with one
argument, then it simply returns its argument. Otherwise it
creates a closure for iteration.
Here are four examples of how it could work in Lua 5.3:
do
local function printentries(entries)
-- NOTE: no more varargs here,
-- entries shall be a single value iterable through ipairs
for i, v, v2 in ipairs(entries) do
if v2 == nil then
print("Entry #" .. tostring(i) .. ": " .. tostring(v))
else
print(
"Entry #" .. tostring(i) ..
": (" .. tostring(v) .. "," .. tostring(v2) .. ")"
)
end
end
end
local letter = nil
local function my_iterator() -- some example iterator
if letter == nil then
letter = "a"
elseif letter == "z" then
return nil
else
letter = string.char(string.byte(letter) + 1)
end
return letter
end
local lines = assert(io.open("testfile", "r")):lines()
local kv_pairs = crimp(pairs{fruit = "apple", mood = "good"})
-- crimp will convert the iterator triplet into a single value
-- so that the printentries function can accept it
printentries(my_iterator)
-- prints:
-- Entry #1: a
-- Entry #2: b
-- Entry #3: c
-- Entry #4: d
-- ...
-- Entry #25: y
-- Entry #26: z
printentries{"a", "b", "c"}
-- prints:
-- Entry #1: a
-- Entry #2: b
-- Entry #3: c
printentries(lines)
-- prints:
-- Entry #1: This is line #1 of my testfile.
-- Entry #2: This is line #2 of my testfile.
printentries(kv_pairs)
-- prints:
-- Entry #1: (mood,good)
-- Entry #2: (fruit,apple)
end
As you can see, the printentries function accepts any single value
that's iterable (through ipairs). Raw tables, e.g. {"a", "b", "c"},
are iterable by default, so are functions, e.g.
function() return "loop" end.
As I previously said, this requires only minimal changes in lbaselib.c
It would create a common iterator interface that could be used by all
libraries. The ipairs function could work on tables, SQL cursors,
sparse arrays, basically anything!
(Still looking forward to feedback.)
Regards
Jan Behrens
- References:
- Speed of # operator (Was: ipairs in Lua 5.3.0-alpha), Dirk Laurie
- Re: Speed of # operator (Was: ipairs in Lua 5.3.0-alpha), Roberto Ierusalimschy
- Re: ipairs in Lua 5.3.0-alpha, Jan Behrens
- Re: ipairs in Lua 5.3.0-alpha, Jan Behrens
- Re: ipairs in Lua 5.3.0-alpha, Roberto Ierusalimschy
- Re: ipairs in Lua 5.3.0-alpha, Jan Behrens
- Re: ipairs in Lua 5.3.0-alpha, Jan Behrens
- Re: ipairs in Lua 5.3.0-alpha, Roberto Ierusalimschy
- Re: ipairs in Lua 5.3.0-alpha, Jan Behrens
- Re: ipairs in Lua 5.3.0-alpha, Doug Currie
- Re: ipairs in Lua 5.3.0-alpha, Coda Highland
- Re: ipairs in Lua 5.3.0-alpha, Jan Behrens
- Re: ipairs in Lua 5.3.0-alpha, Andrew Starks
- Re: ipairs in Lua 5.3.0-alpha, Jan Behrens
- Re: ipairs in Lua 5.3.0-alpha, Jan Behrens