lovingxiaobing 发表于 2018-9-8 20:25

[Lua&C」lua闭包(迭代器)简单应用|仿一个python中的range函数

小冰在网上搜了一下Lua c api,貌似对于用c实现lua闭包的的文章几乎没有<没有科学依据的说>。
萌新小冰就来教一下大家如何用C写一个lua的闭包函数吧~

等会会用用到的LUA API:

[*]lua_gettop
[*]luaL_checkinteger
[*]lua_tointeger
[*]lua_setupvalue
[*]lua_upvalueindex
[*]lua_pushinteger
[*]lua_pushcclosure
[*]luaL_newlib


嘻嘻,相信大家的自学能力还是蛮ok哒
栈的元素特点就是先进后出嘛,栈顶是-1,栈低是1

大家也看过科幻片吧,这个平常用到栈就比作时现实空间<科幻>,等会会用到异元次空间<科幻>:伪索引。就像现实和虚拟,不能不承认,他们都是存在。

光说不行呀<实践是理论的唯一标准>

先来一段lua闭包


```lua
local range

range = function (n)
    local start = 0
    local target = n
    local step = 1
    local now = start
   
    local function range_element()
      if now >= target then
            return nil
      end
      
      now = now + step
      return now--闭包的返回值
    end
   
    return range_element    --把闭包函数返回给 for
end

print("value:")

for i in range(10) do
    print(i)
end

```

结果:
value:
1
2
3
4
5
6
7
8
9
10


上面的range函数中的局部变量叫做range_element的上值,emmm,这是chinglish吧,英文名叫做:upvalue
上值的作用就是给闭包函数用的,作为闭包函数每次的返回值,作为迭代器结束循环的条件,作为,,,,好像没啦~

前面扯淡了那么多,主题来了!!!

问题来了,在C中咋整????

其实原理跟lua代码差不多,上代码吧

```c
//file: myiter.c

#define LUA_LIB
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

static int range_element(lua_State *L)
{
    lua_Integer now, start, target, step;
   
    //把值从异次元空间取出来
    start = lua_tointeger(L, lua_upvalueindex(1));
    target = lua_tointeger(L, lua_upvalueindex(2));
    step = lua_tointeger(L, lua_upvalueindex(3));
    now = lua_tointeger(L, lua_upvalueindex(4));
   
    if (now >= target) return 0;    //返回nil结束lua中的for
   
    lua_pushinteger(L, now);    //要返回的值
    now += step;
    lua_pushinteger(L, now);    //记录本次的值
    lua_replace(L, lua_upvalueindex(4));    //替换旧值
   
    return 1;
}

static int l_range(lua_State *L)
{
    lua_Integer now, start = 0, target, step = 1;
    now = start;
   
    luaL_checkinteger(L, 1);
   
    lua_pushcclosure(L, range_element, 4);
   
    //把上值传入异元次空间
    //最小值
    lua_pushinteger(L, start);
    lua_setupvalue(L, -2, 1);   //闭包函数位于栈的-2位置,而这个1上值的伪索引
   
    //再来几个上值
    lua_pushinteger(L, target);
    lua_setupvalue(L, -2, 2);
    //------------------
    lua_pushinteger(L, step);
    lua_setupvalue(L, -2, 3);
   
    //这个记录当前量
    lua_pushinteger(L, now);
    lua_setupvalue(L, -2, 4);
   
    return 1;
}

static const struct luaL_Reg myiter[] =\
{
    {"range", l_range},
    {NULL, NULL}
};

LUALIB_API int luaopen_myiter(lua_State *L)
{
    luaL_newlib(L, myiter);
    return 1;
}

```

```lua
--file: myiter.lua
myiter = require "myiter"

local range = myiter.range

print("value:")
for i in range(10) do
    print(i)
end

```

ok!编译并执行!

gcc myiter.c -o myiter.so -shared -fPIC -llua
lua myiter,lua
value:
0
1
2
3
4
5
6
7
8
9


哈哈哈看来已经接近range函数啦。

再来完善一下吧~~

直接上代码:

```c
/*
* author: 小冰哟
*/

/*
* range( target[, step])<不支持浮点数>类似于python中的range(...)
*/

#define LUA_LIB
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

static int l_closurefunc(lua_State *L)
{
    lua_Integer now, start, target, step;
   
    start = lua_tointeger(L, lua_upvalueindex(1));
    target = lua_tointeger(L, lua_upvalueindex(2));
    step = lua_tointeger(L, lua_upvalueindex(3));
    now = lua_tointeger(L, lua_upvalueindex(4));
   
    if (((now >= target) && (step > 0)) \
      || ((now <= target) && (step < 0)))
            return 0;
   
    lua_pushinteger(L, (now != start) ? now : start);
   
    now += step;
    lua_pushinteger(L, now);
    lua_replace(L, lua_upvalueindex(4));    //当前变量
   
    return 1;
}

static int l_range(lua_State *L)
{
    lua_Integer n = {0, 0, 1}; //:start|:target|:step
    unsigned char i;
   
    switch (lua_gettop(L)) {
      case 1:
            n = luaL_checkinteger(L, 1);
            
            if (n == 0) return 0;
            n = (n > 0) ? n : -n;
            break;
      case 2:
            n = luaL_checkinteger(L, 1);
            n = luaL_checkinteger(L, 2);
            break;
      case 3:
            n = luaL_checkinteger(L, 1);
            n = luaL_checkinteger(L, 2);
            n = luaL_checkinteger(L, 3);
            break;
      default:
            return 0;
    }
   
    lua_pushcclosure(L, l_closurefunc, 4);
   
    for (i = 0; i < 3; i++) {
      lua_pushinteger(L, n);
      lua_setupvalue(L, -2, (i + 1));
    }
   
    //当前变量
    lua_pushinteger(L, n);
    lua_setupvalue(L, -2, 4);
   
    return 1;
}

static const struct luaL_Reg l_miter[] =\
{
    {"range", l_range},
    {NULL, NULL}
};

LUALIB_API int luaopen_miter(lua_State *L)
{
    luaL_newlib(L, l_miter);
   
    lua_pushstring(L, "author");
    lua_pushstring(L, "小冰哟|lovingxiaobing");
    lua_rawset(L, 3);
   
    return 1;
}

```

好啦,小冰就跟大家唠嗑到这啦~

给大家一个查询lua c api 的网,中文的哟。
https://www.runoob.com/manual/lua53doc/contents.html

喜欢小冰的文章,支持一下哟,mua~

小甲鱼:programming change the world!

Right_Hai 发表于 2018-9-8 22:22

学习学习
页: [1]
查看完整版本: [Lua&C」lua闭包(迭代器)简单应用|仿一个python中的range函数