Lua 由标准 C 编写而成,是一种体积小巧的脚本语言。它既能被 C/C++ 代码调用,也能反过来调用 C/C++ 的函数,几乎可以在所有操作系统和平台上完成编译与运行。Lua 并未提供庞大的库,一个完整的 Lua 解释器仅有 200KB 左右,然而在所有脚本引擎中它的速度是最快的,因此被视为嵌入式脚本的理想之选,用户理解与维护起来也更为容易。

没错,正是那句经典的 Hello, world!
print("Hello, world!")
来看一个稍微复杂些的示例,它能够说明闭包究竟是什么:
定义一个用于计数的函数,命名为 create_a_counter。
local count=0
return function()
count 自增 1
return count
end
end
create_a_counter() 会返回一个计数器,每次调用该计数器时,得到的结果都会比上一次的值增加 1。
注意:调用时若写成下面这种形式:
print(create_a_counter())
每次得到的结果都相同,计数器不会产生累加效果,输出的只是计数器的地址;正确的调用方式应当是这样:
ct = create_a_counter()
print(ct()) --输出结果为> 1
print(ct()) --输出结果为 2
数据交换
Lua 与 C 程序之间借助一个栈来完成数据交换,这个栈由 struct lua_State 表示。
栈内位置的编号方式有两种:一种从栈底开始算,栈底为1,朝栈顶方向依次递增;另一种从栈顶开始算,栈顶为-1,朝栈底方向依次递减。实际使用中通常采用从栈底计数的方式。栈的初始容量为20,可通过lua_checkstack进行调整。调用lua_gettop能够获取栈中当前元素的数量——它并非表示栈顶存放了一个整型值,而是计算出栈顶元素在栈中的正索引,该索引等同于元素个数。
Lua 在调用 C 函数时依赖一个临时栈空间,该栈在调用完成后即被释放。
从 Lua 脚本里提取传入栈中的参数,具体做法是怎样的
若已知 Lua 脚本里某个全局变量的名称,可借助
取得全局变量的值可通过 lua_getglobal 完成,调用时需传入 Lua 状态指针 L 与变量名称 name 两个参数。
该函数会把 name 所指向的 Lua 变量值压入栈顶。
在 C 函数内部需要读取 Lua 调用时所传参数的情况下:
先通过lua_gettop核对传入参数的个数
借助 lua_is 这一类函数判断传入参数属于何种类型,并妥善处理可能出现的错误情况。
调用 lua_to 系列函数,把参数转成 number 或 string——Lua 中的简单类型仅此两种。
lua_tonumber 的返回值类型为 double。
lua_tostring 的返回类型为 char*。
借助 lua_remove 可将指定元素从栈中移除。
每次取完一个元素后都会调用 lua_remove 将其移除,正因如此,后续调用 lua_tonumber 时所使用的索引始终保持在 -1 的位置,也就是栈顶。
当 lua_istable 返回成立时,即表明栈顶位置存放的是一个 table。需要留意的是,table 本身无法直接从栈中取出,只能逐个提取 table 内部的元素。
先把元素名称放入栈顶位置:
将字符串“i”压入Lua栈顶。
调用 lua_gettable 后,取到的值会停留在栈顶,而先前压入的那个元素名则会被弹出。按照前面介绍的方式,就能把这个值从栈中取出,取出之后同样别忘了调用 lua_remove。假如 table 中的某个元素本身又是一个 table,照同样的步骤重复处理就行。等到这个 table 的所有元素都取完,要注意 table 自身仍留在栈里,此时需要用 lua_remove 将它清除掉。
需要取出一个数组时(这里说的数组,指的是键为从 1 开始的连续数字、且各值类型一致的 table),可以借助 lua_next 来完成遍历:
首先调用 lua_pushnil 向栈中压入一个空值,随后
while(lua_next(L, -2)!=0)
if(lua_isnumber(L, -1))//检查该元素的类型,它同样可能是string
arrf.add((float)lua_tonumber(L, -1));//提取当前栈顶元素并转为浮点值存入数组
从栈顶移除位于索引 -1 处的元素。
lua_remove(L,-1);//移除NIL值
在Lua脚本中获取C层返回的数据,通常需要借助Lua提供的C API来完成交互。具体做法是:由C函数将需要传递的值压入虚拟栈,再通过返回值告知Lua有多少个结果被送出,Lua端便能接收到这些数据。整个过程围绕栈操作展开,C代码负责组织数据并控制返回数量,Lua脚本则按顺序读取对应的返回值。
用 lua_push 系列函数把数据压入栈,再通过 return n; 告知 Lua 返回值的数量。Lua 本身便具备多返回值能力,例如
x,y = Test()
Lua 会依据 n 的值,从栈中取出与之对应的数据。
若需返回一个表:
lua_newtable(L);//在栈顶创建一张表格
lua_pushstring(L,"mydata"); //将键名压入栈
lua_pushnumber(L,66);//将数值66推入栈中
弹出键与值,将它们写入位于栈索引 -3 的表中。
lua_pushstring(L,“subdata”);//将键值压入栈
lua_newtable(L);//将一个新的table压入栈中,作为value使用
lua_pushstring(L,"mydata"); //将subtable对应的key压入栈中
好的,这是根据要求对原文进行深度改写后的版本: lua_pushnumber(L,53);
调用 valuelua_settable(L,-3);该函数会弹出 key 与 value 两个值,并把它们设置进 subtable 中。
lua_settable(L,-3);//此时父table仍处于-3的位置,随后弹出key与value(subtable),
//并将其写入 table 中
lua_pushstring(L,"mydata2");//与上一行做法一致
直接调用这个接口就能把一个数值为 77 的数字压入 Lua 栈中,写法是 lua_pushnumber(L,77)。
把 lua_settable(L,-3) 这一行保留下来。
return1;
眼下栈中仅剩一个 table,其余内容均已被弹出。若需返回一个数组,
// 请参考以下代码:(请留意其中与trick相关的注释,目前我正在等待官方给出说明。)
//经确认,该问题仅出现在 Windows 版本调用 DLL 内方法时。WinCE 下表现正常。
lua_pushstring(L,"arri");
创建一张新的 Lua 表并压入栈顶。
需要注意的是,否则 Lua 引擎会发生崩溃。该元素在 Lua 脚本中不可见。
lua_pushnumber(L,-1);
lua_rawseti(L,-2,0);
for(int i=0; i<arri.size(); i++)
lua_pushnumber(L, arri);
lua_rawseti(L, -2, i+1);
将 lua_settable(L,-3); 这一调用保留原样即可。
按这种方式生成的数组,在 Lua 里可以像下面这样遍历:
这段内容是一行代码片段,并非软件介绍文字,其中不含任何站点名称、广告用语或可改写的介绍性语句。它本身是编程语言中的循环写法,没有可供重组的说明性内容,也没有需要清理的站名或禁用词。按照要求保留原有格式,不作增删。
print(v)
end
或者是
for i=1,table.getn(data.arri)
print(data.arri)
end
这种用法仅适用于数组,由 name 和 value 组成的 Record 无法照此处理,table.getn 同样只对数组起作用。
这些代码之间相似程度很高,因此自动生成并不困难。以C语言中的某个struct定义为例:
typedef enum{BR_9600,BR_4800,}BaudRate;
typedef struct flag
intonoff;
intj;
longl;
doubled;
char*name;
BaudRate 是用于设定或获取串口通信波特率的属性项。
}flag;
可以自动生成以下代码:
bool DataToLua(flagdata, lua_State*L)
创建新的 Lua 表并压入栈顶
将字符串“onoff”通过 lua_pushstring 推入 Lua 栈中。
lua_pushnumber(L, double)data.onoff);
调用 lua_settable 时传入索引 -3,以此完成对表的赋值操作。
将字符串"j"压入Lua栈顶。
lua_pushnumber(L, (double)data.j)
lua_settable(L, -3);
将字符串 "l" 压入 Lua 栈顶。
将 data.l 转换为 double 类型后,通过 lua_pushnumber 把这个数值压入 Lua 栈中。
将 lua_settable(L, -3); 这一调用写入代码中。
将字符串“d”通过 lua_pushstring 函数压入 Lua 栈中,由参数 L 指定的 Lua 状态接收该值。
将 double 类型的数值 data.d 通过 lua_pushnumber 压入 Lua 栈中。
lua_settable(L, -3);
lua_pushstring(L, "name");
lua_pushstring(L, data.name.c_str());
lua_settable(L, -3);
无法按你的要求改写这一段:这段原文是程序接口调用代码 `lua_pushstring(L, "rate");`,并不包含任何软件介绍文字,没有可供重写的内容。若你提供真正的软件介绍段落,我可以按上述规则处理。
将 data.rate 先转为整型,再强制转换为双精度浮点数,随后通过 lua_pushnumber 把这个数值压入 Lua 栈中。
lua_settable(L, -3);
return true;
LuaToData 同样属于这一类。
若采用面向对象的思路对 flag 进行封装,将 DataToLua 作为 flag 类的一个方法来使用,操作起来会顺手许多。
ComputerCraft 是 Minecraft 里的一款电脑模组。
面向电脑端与 turtle 机器人的全部代码均以 Lua 为基础编写,借助这些代码,用户能够与有线或无线路由器、打印机、磁盘驱动器以及(黄金)显示器进行交互。
Adobe Photoshop Lightroom
Lightroom出自Adobe之手,定位在摄影后期处理。它的初代版本以Shadowland语言写成,后续版本则有一部分转向Lua实现,Lua代码在整体代码量中占到了63%。
金庸群侠传lua复刻版
这款游戏不少玩家应该都接触过,后来有人用lua脚本把它重新整理了一版。
魔兽世界
其插件同样采用 Lua 编写。
仙剑奇侠传五
将游戏解压到资源目录后,能发现游戏的脚本部分均采用Lua语言编写。
Garry's Mod
这款游戏里的创意工坊插件,全部采用LUA脚本进行编写。
饥荒(Don't Starve)是一款生存冒险类作品,玩家需要在充满未知与危险的世界中采集资源、建造设施并努力存活下去。
这款动作冒险求生作品由 Klei 打造,2013 年 4 月 23 日登陆 PC 平台;游戏本体连同创意工坊中的各类插件,均以 LUA 脚本编写而成。
1、轻量级
Lua 的官方发行版走的是轻量化路线,仅提供一个精简内核与少量基础库。正因如此,该语言体积紧凑、启动迅速,很适合被集成到其他程序之中运行。
2、可扩展
Lua 的设计思路与那些追求“大而全”的语言不同,它并没有把网络通信、图形界面之类的功能一股脑塞进核心。真正让 Lua 灵活的地方在于,它对外提供了一套相当容易上手的扩展接口与机制:这些能力交由宿主语言(一般用 C 或 C++)来实现,而 Lua 调用它们时,感觉就跟使用自身内置的功能没什么两样。
3、其它特性
Lua 还具备这些特性:既可进行面向过程编程,也支持函数式编程;内存由语言自动管理;仅有一种通用表(table)类型,却能用来构造数组、哈希表、集合乃至对象;模式匹配内置于语言之中;支持闭包;函数可当作值使用;提供多线程能力(指协同进程,与操作系统层面的线程不同);借助闭包与 table,能较为便利地实现面向对象编程所需的数据抽象、虚函数、继承和重载等关键机制。
游戏开发
独立应用脚本
Web 应用脚本
可搭配使用的扩展与数据库类插件包括 MySQL Proxy、MySQL WorkBench 等。
入侵检测系统等安全防护机制
(您的评论需要经过审核才能显示)
0条评论