Lua - 由 C 编写而成 小巧脚本编程语言 无法独立开发 APP 应用
Lua - 由 C 编写而成 小巧脚本编程语言 无法独立开发 APP 应用Lua 是一个很小巧的脚本编程语言。Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由 Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo 所组成,并于1993年开发。 Lua 有一个同时进行的 JIT 项目,提供在特定平台上的即时编译功能。
Lua 其设计目的是为嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua 由标准 C 编写而成,代码简洁优美,几乎在所有操作系统和平台上都可编译、运行。
Lua 脚本是一个轻量级脚本,号称性能最高的脚本,用在很多需要性能的地方,比如:游戏脚本、nginx、wireshark 脚本,源码编译后,解释器不到 200k,这是多么地变态啊(/bin/sh 都要 1M,MacOS 平台),且能和 C 语言非常好的互动。Lua 解释器源码,可能是大家见过的最干净 C 代码。
Lua 是类 C 的,所以,大小写字符敏感。Lua 脚本语句分号是可选的,这个和 GO 语言很类似。
Lua 并没有提供强大的库,这是由 Lua 的定位所决定的。所以,Lua 是不适合作为开发独立应用程序的编程语言。
Lua 脚本很容易被 C/C++ 代码调用,也可反过来调用 C/C++ 函数,这使得 Lua 在应用程序中可被广泛应用。Lua 不仅仅可作为扩展脚本,也可作为普通配置文件,代替 XML、ini 等文件格式,并可更容易理解和维护。
一个完整的 Lua 解释器不过 200k,在目前所有脚本引擎中,Lua 的速度是最快的。这一切都决定了 Lua 是作为嵌入式脚本的最佳选择。
可通过交互模式运行 Lua,也可以用记事本编辑代码,保存为 .lua 的格式,通过 lua 编译器运行。也可通过第三方工具,将 lua 打包独立运行。
Lua 的目标是成为一个很容易嵌入其它语言中,所使用的语言。大多数程序员也认为它的确做到了这一点。
很多应用程序、游戏,使用 Lua 作为自己的嵌入式脚本语言,以此实现可配置性、可扩展性。这其中包括:魔兽世界、博德之门、愤怒的小鸟、VOCALOID3、太阳神三国杀等。
Lua 应用场景:游戏开发、独立应用脚本、Web 应用脚本、扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench、安全系统 (如:入侵检测系统)。
Minecraft 中的电脑模组(ComputerCraft):所有电脑和turtle(机器人)代码都是基于 Lua 的,你可用它们与有 (无) 线路由器、打印机、磁盘驱动器、(黄金) 显示器互动。
Adobe Photoshop LightroomLightroom是 Adobe 公司的一款摄影后期处理软件,最开始的版本由 Shadowland 代码编写,后期版本部分使用 Lua 实现,Lua 代码占到代码总量的 63%。
金庸群侠传 lua 复刻版这个游戏,游戏迷们想必都玩过了。牛人用 lua 脚本重新弄了下。
魔兽世界其插件用的是 lua。
仙剑奇侠传五解压游戏到资源目录,可看到游戏到脚本全部是使用Lua 语言编写的。
主要特性
01、轻量级Lua 语言的官方版本,只包括一个精简的核心和最基本的库。这使得 Lua 体积小、启动速度快,从而适合嵌入在别的应用程序中。
5.0.2 版的 Lua 的内核小于 120KB,而 Python 的内核大约 860KB,Perl 的内核大约 1.1MB。
02、可扩展
Lua 并不象其它许多 "大而全" 的语言,包括很多功能,譬如:网络通讯、图形界面等。
但 Lua 提供了非常易于使用的扩展接口和机制:由宿主语言 (通常是 C 或 C++) 提供这些功能,Lua 可以使用它们,就像本来就内置的功能一样。
03、其它特性
Lua 还具有其它一些特性:同时支持面向过程 (procedure-oriented) 编程和函数式编程 (functional programming);自动内存管理;只提供了一种通用类型表(table),用它可以实现数组,哈希表,集合,对象;语言内置模式匹配;闭包 (closure);函数也可看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;通过闭包和 table 可很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。
示例代码
print("Hello World")可以像 python 一样,在命令行上运行 Lua 命令后,进入 Lua 的 shell 中执行语句。chenhao-air:lua chenhao$ lua
Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio
> print("Hello, World")
Hello, World
>和 Go 语言一样,可以一条语句上赋多个值,如:name, age, bGay = "haoel", 37, false, "haoel@hotmail.com"稍复杂点的例子,展示什么是闭包:
function create_a_counter()
local count = 0
return function()
count = count + 1
return count
end
endcreate_a_counter() 返回一个记数器,每次调用这个记数器,都会得到一个比上次大 1 的值。
注意:调用的时候,如果你的调用是这样的:print(create_a_counter())那么,每次输出都一样,没有计数效果。
应这样调用:
c1=create_a_counter()
print(c1()) --> 1
print(c1()) --> 2
数据交换
Lua 和 C 程序通过一个栈交换数据:struct lua_State
栈的序号可从栈顶和栈底计数,从栈底计数,则栈底是 1, 向栈顶方向递增。从栈顶计数,则栈顶是 -1, 向栈底方向递减。一般都用从栈底计数的方式。
栈默认大小为 20,可以用 lua_checkstack 修改。用 lua_gettop 则可获得栈元素数目。并不是说在栈顶有一个整形元素。而是计算了一下栈顶元素在栈里的正 index,相当于元素数目。
Lua 调用 C 函数用的栈是临时的,调用结束之后就会被销毁。
如何从栈中获取 Lua 脚本参数如果知道 Lua 脚本中某个全局变量的名字,可以用void lua_getglobal(lua_State *L, const char *name)这个函数会将 name 所指 Lua 变量值,放在栈顶。
如何在 C 函数中获取 Lua 调用函数参数:首先用 lua_gettop 检查参数数量
用 lua_is 类函数检测参数类型,做好错误处理
用 lua_to 类函数将参数转换为 number 或 string (对 Lua 来说,只有这 2 种简单类型)。lua_tonumber 返回的是 double
lua_tostring 返回的是 char*用 lua_remove 从栈中删除元素
继续获取下一元素。 因为,每次都调用 lua_remove,所以,每次调用 lua_tonumber, 使用的 index 都将固定是 -1,即栈顶。
如 lua_istable 成立,那么,说明栈顶是一个 table。注意 table 是不能直接取出来的,只能把 table 里的元素一个个取出来。
首先把元素名压入栈顶:lua_pushstring(L, "i");然后,就可用 lua_gettable 调用,值会被压入栈顶。同时,刚才压入的元素名会被弹出。用上面的办法,就可把这个值取出来。记得,也应 lua_remove。 如 table 的某一个元素也是 table,重复即可。当 table 所有元素都取完后,记住这个 table 本身还在栈里,要用 lua_remove 把它删除。
如要获取一个数组 (所谓数组,其实就是 key 从 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));//获取元素的值
lua_remove(L, -1);
}
}
lua_remove(L,-1);//删除NIL
如何从 C 返回数据给 Lua 脚本
用 lua_push 类函数压入数据到栈中,并用 return n; 告诉 Lua 返回了几个返回值。 Lua 天生支持多返回值,譬如x,y = Test()Lua 会根据 n 从栈里取相应数据。
如要返回一个 table:
lua_newtable(L); //创建一个表格,放在栈顶
lua_pushstring(L,"mydata"); //压入key
lua_pushnumber(L,66); //压入value
lua_settable(L,-3); //弹出key,value,并设置到table里面去
lua_pushstring(L,"subdata");//压入key
lua_newtable(L); //压入value,也是一个table
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");//同上
lua_pushnumber(L,77);
lua_settable(L,-3);
return1;
//栈里现在就一个table其他都被弹掉了。如果要返回一个数组,
//用如下代码:(注意那个关于trick的注释,我在等官方的解释。
//经过验证,这个问题只在windows版本调用dll中方法的时候出现。WinCE正常)
lua_pushstring(L,"arri");
lua_newtable(L);
{
//atrick:otherwisetheluaenginewillcrash.ThiselementisinvisibleinLuascript
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 中如下遍历:
for i,v ini pairs(data.arri)
do
print(v)
end或是for i=1,table.getn(data.arri)
do
print(data.arri)
end只有数组才能这样,name,value 构成的 Record 不行,table.getn 也只对数组有效。
由于上述代码的高度相似性,所以很容易实现自动生成这些代码。比如,根据 C 的一个 struct 定义:typedef enum{BR_9600,BR_4800,}BaudRate;
typedef struct flag
{
int onoff;
int j;
long l;
double d;
char *name;
BaudRate rate;
}flag;
可自动产生如下代码:
bool DataToLua(flagdata, lua_State*L)
{
lua_newtable(L);
lua_pushstring(L, "onoff");
lua_pushnumber(L, double)data.onoff);
lua_settable(L, -3);
lua_pushstring(L, "j");
lua_pushnumber(L, (double)data.j);
lua_settable(L, -3);
lua_pushstring(L, "l");
lua_pushnumber(L, (double)data.l);
lua_settable(L, -3);
lua_pushstring(L, "d");
lua_pushnumber(L, double)data.d);
lua_settable(L, -3);
lua_pushstring(L, "name");
lua_pushstring(L, data.name.c_str());
lua_settable(L, -3);
lua_pushstring(L, "rate");
lua_pushnumber(L, (double)(int)data.rate);
lua_settable(L, -3);
return true;
}LuaToData 也是类似的。
如使用面向对象的方式,封装 flag,把 DataToLua 变成 flag 类的方法,会更方便。
版权声明:
本文为独家原创稿件,版权归 德云社区,未经许可不得转载;否则,将追究其法律责任。
页:
[1]