python html5 bootstrap 视频教程

德云社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 10380|回复: 0

Lua - 由 C 编写而成 小巧脚本编程语言 无法独立开发 APP 应用

[复制链接]

185

主题

204

帖子

1175

积分

工程师

Rank: 5Rank: 5

金钱
759
金币
15
威望
0
贡献
0
发表于 2016-3-11 07:47:03 | 显示全部楼层 |阅读模式
AI人工智能 语音助理 人工翻译 教程
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 Lightroom

Lightroom是 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 可很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。


示例代码
  1. print("Hello World")
复制代码

可以像 python 一样,在命令行上运行 Lua 命令后,进入 Lua 的 shell 中执行语句。

  1. chenhao-air:lua chenhao$ lua
  2. Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio
  3. > print("Hello, World")
  4. Hello, World
  5. >
复制代码

和 Go 语言一样,可以一条语句上赋多个值,如:

  1. name, age, bGay = "haoel", 37, false, "haoel@hotmail.com"
复制代码

稍复杂点的例子,展示什么是闭包:

  1. function create_a_counter()
  2.     local count = 0
  3.     return function()
  4.         count = count + 1
  5.         return count
  6.     end
  7. end
复制代码

create_a_counter() 返回一个记数器,每次调用这个记数器,都会得到一个比上次大 1 的值。


注意:调用的时候,如果你的调用是这样的:

  1. print(create_a_counter())
复制代码

那么,每次输出都一样,没有计数效果。


应这样调用:

  1. c1=create_a_counter()
  2. print(c1()) --> 1
  3. print(c1()) --> 2
复制代码

数据交换

Lua 和 C 程序通过一个栈交换数据:struct lua_State


栈的序号可从栈顶和栈底计数,从栈底计数,则栈底是 1, 向栈顶方向递增。从栈顶计数,则栈顶是 -1, 向栈底方向递减。一般都用从栈底计数的方式。


栈默认大小为 20,可以用 lua_checkstack 修改。用 lua_gettop 则可获得栈元素数目。并不是说在栈顶有一个整形元素。而是计算了一下栈顶元素在栈里的正 index,相当于元素数目。


Lua 调用 C 函数用的栈是临时的,调用结束之后就会被销毁。


如何从栈中获取 Lua 脚本参数

如果知道 Lua 脚本中某个全局变量的名字,可以用

  1. void lua_getglobal(lua_State *L, const char *name)
复制代码

这个函数会将 name 所指 Lua 变量值,放在栈顶。


如何在 C 函数中获取 Lua 调用函数参数:

首先用 lua_gettop 检查参数数量


用 lua_is 类函数检测参数类型,做好错误处理


用 lua_to 类函数将参数转换为 number 或 string (对 Lua 来说,只有这 2 种简单类型)。

  1. lua_tonumber 返回的是 double
  2. lua_tostring 返回的是 char*
复制代码

用 lua_remove 从栈中删除元素


继续获取下一元素。 因为,每次都调用 lua_remove,所以,每次调用 lua_tonumber, 使用的 index 都将固定是 -1,即栈顶。


如 lua_istable 成立,那么,说明栈顶是一个 table。注意 table 是不能直接取出来的,只能把 table 里的元素一个个取出来。


首先把元素名压入栈顶:

  1. lua_pushstring(L, "i");
复制代码

然后,就可用 lua_gettable 调用,值会被压入栈顶。同时,刚才压入的元素名会被弹出。用上面的办法,就可把这个值取出来。记得,也应 lua_remove。 如 table 的某一个元素也是 table,重复即可。当 table 所有元素都取完后,记住这个 table 本身还在栈里,要用 lua_remove 把它删除。


如要获取一个数组 (所谓数组,其实就是 key 从 1 开始的数字序列 table,且值类型相同),用 lua_next 可遍历这个数组:

首先 lua_pushnil,会压入一个空值,然后

  1. while(lua_next(L, -2)!=0)
  2. {
  3.     if(lua_isnumber(L, -1))//判断元素类型,也可能是string
  4.     {
  5.         arrf.add((float)lua_tonumber(L, -1));//获取元素的值
  6.         lua_remove(L, -1);
  7.     }
  8. }
  9. lua_remove(L,-1);//删除NIL
复制代码

如何从 C 返回数据给 Lua 脚本


用 lua_push 类函数压入数据到栈中,并用 return n; 告诉 Lua 返回了几个返回值。 Lua 天生支持多返回值,譬如

  1. x,y = Test()
复制代码

Lua 会根据 n 从栈里取相应数据。


如要返回一个 table:

  1. lua_newtable(L); //创建一个表格,放在栈顶
  2. lua_pushstring(L,"mydata"); //压入key
  3. lua_pushnumber(L,66); //压入value
  4. lua_settable(L,-3); //弹出key,value,并设置到table里面去
  5. lua_pushstring(L,"subdata");//压入key
  6. lua_newtable(L); //压入value,也是一个table
  7. lua_pushstring(L,"mydata"); //压入subtable的key
  8. lua_pushnumber(L,53);
  9. valuelua_settable(L,-3); //弹出key,value,并设置到subtable
  10. lua_settable(L,-3); //这时候父table的位置还是-3,弹出key,value(subtable),
  11. //并设置到table里去
  12. lua_pushstring(L,"mydata2");//同上
  13. lua_pushnumber(L,77);
  14. lua_settable(L,-3);
  15. return1;

  16. //栈里现在就一个table其他都被弹掉了。如果要返回一个数组,
  17. //用如下代码:(注意那个关于trick的注释,我在等官方的解释。
  18. //经过验证,这个问题只在windows版本调用dll中方法的时候出现。WinCE正常)
  19. lua_pushstring(L,"arri");
  20. lua_newtable(L);
  21. {
  22.     //atrick:otherwisetheluaenginewillcrash.ThiselementisinvisibleinLuascript
  23.     lua_pushnumber(L,-1);
  24.     lua_rawseti(L,-2,0);
  25.     for(int i=0; i<arri.size(); i++)
  26.     {
  27.         lua_pushnumber(L, arri);
  28.         lua_rawseti(L, -2, i+1);
  29.     }
  30. }
  31. lua_settable(L,-3);
复制代码

这样产生的数组,可在 Lua 中如下遍历:

  1. for i,v ini pairs(data.arri)
  2. do
  3.     print(v)
  4. end
复制代码

或是

  1. for i=1,table.getn(data.arri)
  2. do
  3.  print(data.arri)
  4. end
复制代码

只有数组才能这样,name,value 构成的 Record 不行,table.getn 也只对数组有效。


由于上述代码的高度相似性,所以很容易实现自动生成这些代码。比如,根据 C 的一个 struct 定义:

  1. typedef enum{BR_9600,BR_4800,}BaudRate;
  2. typedef struct flag
  3. {
  4.     int onoff;
  5.     int j;
  6.     long l;
  7.     double d;
  8.     char *name;
  9.     BaudRate rate;
  10. }flag;
复制代码

可自动产生如下代码:

  1. bool DataToLua(flagdata, lua_State*L)
  2. {
  3.     lua_newtable(L);
  4.     lua_pushstring(L, "onoff");
  5.     lua_pushnumber(L, double)data.onoff);
  6.     lua_settable(L, -3);
  7.     lua_pushstring(L, "j");
  8.     lua_pushnumber(L, (double)data.j);
  9.     lua_settable(L, -3);
  10.     lua_pushstring(L, "l");
  11.     lua_pushnumber(L, (double)data.l);
  12.     lua_settable(L, -3);
  13.     lua_pushstring(L, "d");
  14.     lua_pushnumber(L, double)data.d);
  15.     lua_settable(L, -3);
  16.     lua_pushstring(L, "name");
  17.     lua_pushstring(L, data.name.c_str());
  18.     lua_settable(L, -3);
  19.     lua_pushstring(L, "rate");
  20.     lua_pushnumber(L, (double)(int)data.rate);
  21.     lua_settable(L, -3);
  22.     return true;
  23. }
复制代码

LuaToData 也是类似的。


如使用面向对象的方式,封装 flag,把 DataToLua 变成 flag 类的方法,会更方便。

版权声明:
本文为独家原创稿件,版权归 德云社区,未经许可不得转载;否则,将追究其法律责任。


AI人工智能 语音助理 人工翻译 教程
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|Sitemap|小黑屋|德云社区 |网站地图  

GMT+8, 2024-11-23 12:06 , Processed in 0.031880 second(s), 28 queries .

工业和信息化部: 粤ICP备14079481号-2

技术支持 乐数软件     版权所有 © 2014-2021 德云社区    

快速回复 返回顶部 返回列表