请问lua 中怎么比对两组内容,获取富文本内容 lua其中相同的内容

博客访问: 57369
博文数量: 37
博客积分: 10
博客等级: 民兵
技术积分: 352
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
对于新手来说,在lua中在table中如何去除重复的项是个小纠结的问题,捯饬了好久终于找到方法了,问题现状和解决思路如下:
存在重复项的数组table={"ne","two","three","test","test","test","test","test","test","test","test","test","test","test0","test1","test2","sohu","iqyi","baidu","one","two","three","four"}
解决思路:
(1)、将上述table的value作为key,true为值,构成新的table。在lua中key不允许有重复,因此形成的新数组,就能够实现去重复元素了。
&&local&aa={}
&&for&key,val&in&pairs(zz)&do
&&&&&aa[val]=true
&&local&fan=cjson.encode(aa)
&&ngx.say(fan)
结果:形成一个新的key,value的table:(已经实现了去重)
{"ne":true,"two":true,"three":true,"iqyi":true,"test":true,"sohu":true,"test1":true,"baidu":true,"test2":true,"test0":true}
(2)、将上述形成table的key作为最终结果,组成table输出:
&&local&bb={}
&&for&key,val&in&pairs(aa)&do
&&&&&table.insert(bb,key) & & & & & & & &# 将key插入到新的table,构成最终的结果
&&table.sort(bb) &
&&local&fan=cjson.encode(bb)
&&ngx.say(fan)
得到最终去重后的结果:
{"baidu","four","iqyi","one","sohu","test","test0","test1","test2","three","two"}
达到最终的目的,收工!!!
阅读(5518) | 评论(0) | 转发(0) |
下一篇:没有了
相关热门文章
给主人留下些什么吧!~~
请登录后评论。!!LUA语言参考【转】-提供留学,移民,理财,培训,美容,整形,高考,外汇,印刷,健康,建材等信息_突袭网
当前位置&:&&&&!!LUA语言参考【转】
热门标签:&
!!LUA语言参考【转】
来源: 由用户
编辑:李利平
学好LUA和PYTHON语言需要什么样的基础,最好是给出参考书...lua的资料比较少,买本《ProgramminginLua》的第二版中文版看看就行了。测试的话看你程序怎么写,反正lua也有自己的解释器。学好LUA和PYTHON语言需要什么样的基础,最好是给出参考书...不需要什么基础,可能英文要有点基础,有点逻辑基础。外加有一台安装了这两个解释器的电脑。书目的话,python入门的书很多,就不列举了。比如diveintopython。lua的话,书很...如何在Java中使用Lua脚本语言在测试中,发现Luajava提供的文档中,对于Lua脚本怎么调用Java对象/方法很详细,但是在Java中怎么调用Lua函数/取得返回值就没有.参考了http://www.lua.org/manual/5.1/man...!!LUA语言参考【转】(图2)!!LUA语言参考【转】(图4)!!LUA语言参考【转】(图6)!!LUA语言参考【转】(图8)!!LUA语言参考【转】(图11)!!LUA语言参考【转】(图13)
Lua 的语法比较简单,学习起来也比较省力,但功能却并不弱。请问在Lua语言中,如何点击一个随机坐标用Lua的获取随机数函数就可以实现:math.random([m,n])该函数有三种用法:1.random...:返回1到n之间的伪随机整数3.random(m防抓取,突袭网提供内容,请查看原文。  在Lua中,一切都是变量,除了关键字。请记住这句话。有会lua语言的吗,简单问题你是想判断变量是否由数字组成还是变量和某一个固定值比较?如果后者可以参考上面的回答。前者的话可以这么判断:io.write(&Inputanumber:&quot防抓取,突袭网提供内容,请查看原文。I. 首先是注释如何用宿主语言,如C,将LUA中的数组导出到数据库,如EXCEL方法很多,可以考虑VC的库函数。
第二步,如何用宿主语言调用LUA?这方面的资料也不少,我感觉,用C把数据存到数据库工作防抓取,突袭网提供内容,请查看原文。  写一个程序,总是少不了注释的。求帮忙啊C语言如何调用指定目录下的LUA文件的全局变量(变...以下为我的模拟代码,仅供参考lua_State*L=luaL_newstate();luaL_openlibs(L);luaL_dost防抓取,突袭网提供内容,请查看原文。  在Lua中,你可以使用单行注释和多行注释。openresty中使用lua-nginx创建socket实例&这篇文章主要介绍了openresty中使用lua-nginx创建socket实例,本文直接给出代码实例和运行效果,需要的朋友可防抓取,突袭网提供内容,请查看原文。  单行注释中,连续两个减号"--"表示注释的开始,一直延续到行末为止。相当于C++语言中的"//"。急!luaforwindows安装失败,win764位。问:操作系统是win764位,请求高手赐教答:这个版本是要联网的你把网络连上再安装如果网络是正常连接的话你看看是不是有防火墙之类的软件或者把36防抓取,突袭网提供内容,请查看原文。  多行注释中,由"--[["表示注释开始,并且一直延续到"]]"为止。这种注释相当于C语言中的"/…/"。在注释当中,"[["和"]]"是可以嵌套的。lua文件要什么软件可以打开呢?急!!!!答:Lua是一个扩展式程序设计语言,它被设计成支持通用的过程式编程,并有相关数据描述的设施。Lua也能对面向对象编程,函数式编程,数据驱动式编程提供很好的支持防抓取,突袭网提供内容,请查看原文。II. Lua编程一个C++和lua调用的简单问题。。。高分求高手!!!问:这是个lua代码index=1;story1={"C","C++"};functiongetStory...答:#include#includ防抓取,突袭网提供内容,请查看原文。  经典的"Hello world"的程序总是被用来开始介绍一种语言。在Lua中,写一个这样的程序很简单:luamath.modf函数问题!!问:functionmain()localnum1=0localnum2=0fori=1,1000dol...答:当2.73.74.7....时,d的值确实一直都是防抓取,突袭网提供内容,请查看原文。  print("Hello world")Lua的奇葩问题!为什么赋值后true成了false?问:看图吧,我断点了,self.isReverse=true是正确的,当他赋值给localir...答:有可能是元表的问题我这里实现一下这个方法l防抓取,突袭网提供内容,请查看原文。  在Lua中,语句之间可以用分号";"隔开,也可以用空白隔开。一般来说,如果多个语句写在同一行的话,建议总是用分号隔开。请教谁会lua的反编译,或者指点一下luadec5.1的用...答:记得在luaer网站上看到一个反编译软件大概名字是luadisasm能将字节码反编译成汇编防抓取,突袭网提供内容,请查看原文。  Lua 有好几种程序控制语句,如:lua脚本求完成下面简单的题目编程!!!!!!!!!!问:用随机函数产生十个从1~100之间范围的值放入到一个表ranTable中,输出表...答:functiontest()math.randoms防抓取,突袭网提供内容,请查看原文。  条件控制:if 条件 then … elseif 条件 then … else … end关于魔兽世界RayUI动作条字体问题,来个看得懂lua...问:LZ用RayUI很久了,之前RayUI的动作条字体一直是像素字,12月插件作者更...答:WorldofWarcraft\Interfa防抓取,突袭网提供内容,请查看原文。  While循环:while 条件 do … endlua编程问题!本人是个小菜鸟,这个错误不知道怎么...问:ss=io.read()functionGetWeekDay(ss)tw={"刺刀","石头","火枪","...答:ss=io.read(防抓取,突袭网提供内容,请查看原文。  Repeat循环:repeat … until 条件高悬赏,请各位认真回答!水的,答得少的就别来了...问:高悬赏,请各位认真回答!水的,答得少的就别来了!我要学脚本编写!...答:首先,LUA和java/vb之类的根本不是一种类型的。LUA的设计思想防抓取,突袭网提供内容,请查看原文。  For循环:for 变量 = 初值,终点值,步进 do … end防抓取,突袭网提供内容,请查看原文。  For循环:for 变量1,变量2,… ,变量N in表或枚举函数 do … end蛇泡酒,就是常说的药酒,药酒在我国有悠久的历史。按其所浸药材的不同,一般可分为两大类:一类是以治疗为主的药酒,另一类是以补虚强壮为主要功效的补酒。蛇药酒的主要功效为祛风、通络等,对中医所说的风湿等疾病防抓取,突袭网提供内容,请查看原文。  注意一下,for的循环变量总是只作用于for的局部变量,你也可以省略步进值,这时候,for循环会使用1作为步进值。从2007年进入联盟到如今的10年里,杜兰特的性格发生了很大的变化,刚进入联盟时他是个不折不扣的好好先生,低调、安静,在比赛中很少动怒,场外和鲜有绯闻,但现在的杜兰特,不仅在场上杀器十足,在场下也显得防抓取,突袭网提供内容,请查看原文。  你可以用break来中止一个循环。这个问题我可以回答你原因,因为我曾经是某品牌4S店汽车销售,你想如果你把这一切都办了,销售顾问吃啥,他们都有一堆KPI考核。你这样他不卖以下几点是主要原因,参考谈判,可以让你买到物美价廉的车。你相当于防抓取,突袭网提供内容,请查看原文。  如果你有程序设计的基础,比如你学过Basic,C之类的,你会觉得Lua也不难。但Lua有几个地方是明显不同于这些程序设计语言的,所以请特别注意。在以前可能很少有人想到一枚小小的硬币可以拍到上万甚至十几万的高价,但伴随邮币卡行业的持续发展,现在的天价硬币已经越来越多,像1979年发行的5分硬币就曾卖到数万元左右的高价,这已经超过了相同重量黄金的防抓取,突袭网提供内容,请查看原文。  .语句块很多购房者就会犯头痛:1-30层,一层一价,但是相差的钱也不多,高层买第几层好?小编今天就给购房者说说如何选楼层的知识。    低层:1-3F的建筑为低层建筑  多层:7F以下的建筑为多层建筑(包括6防抓取,突袭网提供内容,请查看原文。    语句块在C++中是用"{"和"}"括起来的,在Lua中,它是用do 和 end 括起来的。比如:&只出了一次险,第二年保费就涨了1000多,跟以前要出险2次甚至3次才涨保费相比,现在的车损险还有购买的意义吗?  赔的还不如保费涨的多  卢先生名下有一辆20余万元的汽车,车龄4年,由于每天开车上下班防抓取,突袭网提供内容,请查看原文。    do print("Hello") end不知道别人怎么做,反正我每次保养完都会把机油桶带走,即使不带走,也会用螺丝刀捅个窟窿后还给修理工。具体原因请看下面转载我头条号上发的一篇热文。某种程度上,假机油对汽车发动机的伤害可以说是致命的,但由于防抓取,突袭网提供内容,请查看原文。    你可以在 函数 中和 语句块 中定局部变量。初到拉萨,上了一辆出租车,大哥是藏族。到玛吉阿米(藏餐厅)师傅上车就问:“来旅游的嘛?”“恩,是”“去玛吉阿米吃饭可以,名气比较大。街上的石头嘛,不要买”“绿松石吗”“对!真的的东西,你们买不到”这个防抓取,突袭网提供内容,请查看原文。  .赋值语句二手车里程表的问题比较复杂了,今天给大家说说普及下。中国与欧美日本不同,国外汽车流通发达国家汽车从生产到报废基本所有环节都会有正规记录并且可以商业查询,在中国出了4S店以后的维修就很难有正规渠道查询了防抓取,突袭网提供内容,请查看原文。    赋值语句在Lua被强化了。它可以同时给多个变量赋值。这几年,女明星间兴起了一股机场街拍风。无论是范冰冰这样的一线明星,还是18线的演员,上网一搜,满眼都是机场街拍照。好像没有几张像样的机场街拍,都不好意思混娱乐圈。尤其是杨幂、宋茜这样的人气小花,更被网防抓取,突袭网提供内容,请查看原文。    例如:小时候,经常三三两两爬树摘桑泡(桑葚)吃。踩坏了地里庄稼,扳断了桑树。农民伯伯看见了就大喊:那桑树打了农药的,吃死你娃儿。然后作势就要来打我们。吓得我们赶紧折几条长满桑泡的桑树枝就跑,跑回家慢慢吃。桑防抓取,突袭网提供内容,请查看原文。    a,b,c,d=1,2,3,4在宠物店待过一段时间,我来告诉你狗狗为什么老实!1.手法,工具辅助。做美容的时候,很多狗狗都是会乱动的,特别是第一次的狗。美容师得剪刀很锋利的,一动就可能受伤!所以很多美容师的美容桌上都会有固定绳,然防抓取,突袭网提供内容,请查看原文。    甚至是:这话怎么说呢。以前的条件没有现在好,很多孩子都是用布,可以反复洗晒的,甚至很多孩子穿开裆裤,人有三急容易解决,而且大人也方便。还有些老人觉得你们这一代人都是我们这么带大的,还不是好好的吗?现在的怎么这防抓取,突袭网提供内容,请查看原文。    a,b=b,a -- 多么方便的交换变量功能啊。一在游戏中培养幼儿活泼开朗的性格喜欢游戏是幼儿的共同特点。游戏为每个幼儿提供的机会是均等的,在游戏中他们是主人,能够根据自己的想象和意愿来创造和指导游戏,随着扮演不同的角色,想胜则胜,想败则败,想来说防抓取,突袭网提供内容,请查看原文。    在默认情况下,变量总是认为是全局的。假如你要定义局部变量,则在第一次赋值的时候,需要用local说明。比如:这很正常,孩子走晚些不怕,关键是必须多爬,这是在训练他的感觉统一性。我有位朋友,孩子走得早,那时可骄傲了,见人就夸,可稍大点发现孩子身体协调不行,又花钱进行感统训练,每天在家也爬,但效果还是不太好。所防抓取,突袭网提供内容,请查看原文。    local a,b,c = 1,2,3 -- a,b,c都是局部变量流水的春晚,铁打的春晚笑脸哥!每年都会出现在观众席的他又来了~为了防止大家换台,今年春晚连细节都走心,董卿口红色号太美!对于爱美女孩而言,一只口红怎么够?这10支让你美炸整个春节的口红,你都get了吗防抓取,突袭网提供内容,请查看原文。  .数值运算谢友邀答。如何让婆婆按照你的要求心心甘情愿为你带孩子,并不难。一、把婆婆当妈对待。婆婆一生不容易,辛辛苦苦把孩子养大,再娶上儿媳妇,一生精力都献给了儿女。儿媳应该把婆婆当妈看待,因为她为你培养了一个好防抓取,突袭网提供内容,请查看原文。    和C语言一样,支持 +, -, , /。但Lua还多了一个"^"。这表示指数乘方运算。比如2^3 结果为8, 2^4结果为16。谢谢邀请。螳螂属于昆虫类,它捕捉害虫故为益虫。雌性螳螂食欲,食量和捕捉能力大于雄性,雌性有时还吃掉雄性,雌性的产卵形式很特别,既不产在地下,也不产在植物茎中,而是产卵在树枝表面。螳螂住在适应栖息环境而防抓取,突袭网提供内容,请查看原文。    连接两个字符串,可以用".."运处符。如:首先推荐吴闲云《煮酒论西游》。其次,原著叫书评,不能叫影评。关于西游原著,我主观的说几点我的看法:1、悟空的佛学造诣远远高于唐僧。2、悟空从前是有性生活的-----在撞天婚时候亲口承认:“俺老孙早就不防抓取,突袭网提供内容,请查看原文。    "This a " .. "string." -- 等于 "this a string"↑↑↑↑点击上方“美丽修行”,关注获取更多科学护肤知识!先上个对比图看看以前的我爆豆的多么厉害第二章是现在了。。。第二张马赛克掉自己的微博名。。。长痘了怎么办,我还是那句话“去医院”你唯一要做的就是看防抓取,突袭网提供内容,请查看原文。  .比较运算每个女人不管多大年纪,心里都住着一个公主,都幻想着能够穿着仙女裙,穿着水晶鞋,但是事实是我们不是谁的公主,我们要工作要照顾家庭,照顾孩子,但是我们的心里还是有一个公主梦~pink粉真的非常美的一个颜色防抓取,突袭网提供内容,请查看原文。    & & &= &= == ~=有“性感小野马”之称的金泫雅最近正值回归,贡献了一波性感又高级的回归舞台,简直性感撩人的没话说,为什么人和人差距就这么大……心痛2017。舞台上的小野马艳光四射性感撩人,但是私下的她偏爱美式休闲和小性防抓取,突袭网提供内容,请查看原文。    分别表示 小于,大于,不大于,不小于,相等,不相等先说说原25集成功的原因吧。那是一个电视观众还很少能看到神话电视剧的时代,人们只是平时听说孙悟空猪八戒,但只是在动画片上见过,在真实镜头的电视上还没有看到过。所以当真人扮演的神话人物上天入地,妖魔鬼鬼防抓取,突袭网提供内容,请查看原文。    所有这些操作符总是返回true或false。狗的嗅觉器官很灵敏,其嗅粘膜面积是人类的4倍,粘膜内嗅细胞大约两亿多个,为人类的40倍。狗对气味的敏感程度高,辨别能力强。即使是未通过训练的普通家养狗在一般的情况下也能嗅到百米地方散发出的轻微臭气。人防抓取,突袭网提供内容,请查看原文。    对于Table,Function和Userdata类型的数据,只有 == 和 ~=可以用。相等表示两个变量引用的是同一个数据。比如:千万车主都在【不懂车互助养车】平台上轻松养车,本月产品上线,快来关注吧!随着社会的发展,现在越来越多的人都会选择小汽车作为自己的代步工具,因为现在汽车的价格是越来越便宜了,但是配置也做的相当不同,尤其防抓取,突袭网提供内容,请查看原文。    a={1,2}狗狗一定要认真的管教。因为狗狗在这个年纪会有叛逆期,而且他在这么小的时候他是犯各种错误,会试探主人的极限在哪里,作为主人的这个时候,该管教就要管教,一定不能手软,严重的话要揍护士。护食行为也是要好好的防抓取,突袭网提供内容,请查看原文。    b=a男人三十,怎么着也得给自己准备一套像样的西服了,别老是大裤衩T恤衫的,一看就是还没长大的大男孩。衣服要挑有品位一点的,不要买那种几百块钱的西装,不合身穿着很难看的,有条件的话定制一套,在比较正式的场合防抓取,突袭网提供内容,请查看原文。    print(a==b, a~=b) -- true, false今天有一位妈妈在后台询问我说她很纳闷医院为什么一出生就取了小宝宝的右脚印,这个有什么用吗?看到这位妈妈的问题我想有很多宝妈都有这种疑惑吧,那么今天就给大家讲一下医院为什么这么做的原因!一:当做病历因为防抓取,突袭网提供内容,请查看原文。    a={1,2}狗狗一定要认真的管教。因为狗狗在这个年纪会有叛逆期,而且他在这么小的时候他是犯各种错误,会试探主人的极限在哪里,作为主人的这个时候,该管教就要管教,一定不能手软,严重的话要揍护士。护食行为也是要好好的防抓取,突袭网提供内容,请查看原文。    b={1,2}    print(a==b, a~=b) -- false, true  .逻辑运算    and, or, not    其中,and 和 or 与C语言区别特别大。    在这里,请先记住,在Lua中,只有false和nil才计算为false,其它任何数据都计算为true,0也是true!    and 和 or的运算结果不是true和false,而是和它的两个操作数相关。    a and b:如果a为false,则返回a;否则返回b    a or b:如果 a 为true,则返回a;否则返回b    举几个例子:     print(4 and 5) --& 5     print(nil and 13) --& nil     print(false and 13) --& false     print(4 or 5) --& 4     print(false or 5) --& 5    在Lua中这是很有用的特性,也是比较令人混洧的特性。    我们可以模拟C语言中的语句:x = a? b : c,在Lua中,可以写成:x = a and b or c。    最有用的语句是: x = x or v,它相当于:if not x then x = v end 。  .运算符优先级,从高到低顺序如下:    ^    not - (一元运算)     /     + -     ..(字符串连接)     & & &= &= ~= ==     and     orIII. 关键字  关键字是不能做为变量的。Lua的关键字不多,就以下几个:  and break do else elseif  end false for function if  in local nil not or  repeat return then true until whileIV. 变量类型  怎么确定一个变量是什么类型的呢?大家可以用type()函数来检查。Lua支持的类型有以下几种:  Nil 空值,所有没有使用过的变量,都是nil。nil既是值,又是类型。  Boolean 布尔值  Number 数值,在Lua里,数值相当于C语言的double  String 字符串,如果你愿意的话,字符串是可以包含'/0'字符的  Table 关系表类型,这个类型功能比较强大,我们在后面慢慢说。  Function 函数类型,不要怀疑,函数也是一种类型,也就是说,所有的函数,它本身就是一个变量。  Userdata 嗯,这个类型专门用来和Lua的宿主打交道的。宿主通常是用C和C++来编写的,在这种情况下,Userdata可以是宿主的任意数据类型,常用的有Struct和指针。  Thread   线程类型,在Lua中没有真正的线程。Lua中可以将一个函数分成几部份运行。如果感兴趣的话,可以去看看Lua的文档。V. 变量的定义  所有的语言,都要用到变量。在Lua中,不管你在什么地方使用变量,都不需要声明,并且所有的这些变量总是全局变量,除非,你在前面加上"local"。  这一点要特别注意,因为你可能想在函数里使用局部变量,却忘了用local来说明。  至于变量名字,它是大小写相关的。也就是说,A和a是两个不同的变量。  定义一个变量的方法就是赋值。"="操作就是用来赋值的  我们一起来定义几种常用类型的变量吧。  A. Nil    正如前面所说的,没有使用过的变量的值,都是Nil。有时候我们也需要将一个变量清除,这时候,我们可以直接给变量赋以nil值。如:    var1=nil -- 请注意 nil 一定要小写  B. Boolean    布尔值通常是用在进行条件判断的时候。布尔值有两种:true 和 false。在Lua中,只有false和nil才被计算为false,而所有任何其它类型的值,都是true。比如0,空串等等,都是true。不要被C语言的习惯所误导,0在Lua中的的确确是true。你也可以直接给一个变量赋以Boolean类型的值,如:    varboolean = true  C. Number    在Lua中,是没有整数类型的,也不需要。一般情况下,只要数值不是很大(比如不超过100,000,000,000,000),是不会产生舍入误差的。在很多CPU上,实数的运算并不比整数慢。    实数的表示方法,同C语言类似,如:    4 0.4 4.57e-3 0.3e12 5e+20  D. String    字符串,总是一种非常常用的高级类型。在Lua中,你可以非常方便的定义很长很长的字符串。    字符串在Lua中有几种方法来表示,最通用的方法,是用双引号或单引号来括起一个字符串的,如:    "This is a string."    和C语言相同的,它支持一些转义字符,列表如下:    /a bell    /b back space    /f form feed    /n newline    /r carriage return    /t horizontal tab    /v vertical tab    // backslash    /" double quote    /' single quote    /[ left square bracket    /] right square bracket    由于这种字符串只能写在一行中,因此,不可避免的要用到转义字符。加入了转义字符的串,看起来实在是不敢恭维,比如:    "one line/nnext line/n/"in quotes/", 'in quotes'"    一大堆的"/"符号让人看起来很倒胃口。如果你与我有同感,那么,我们在Lua中,可以用另一种表示方法:用"[["和"]]"将多行的字符串括起来,如:    page = [[    &HTML&      &HEAD&        &TITLE&An HTML Page&/TITLE&      &/HEAD&      &BODY&        &A HREF="[&http://www.lua.org&]"&Lua&/A&        [[a text between double brackets]]      &/BODY&    &/HTML&    ]]    值得注意的是,在这种字符串中,如果含有单独使用的"[["或"]]"就仍然得用"/["或"/]"来避免歧义。当然,这种情况是极少会发生的。  E. Table    关系表类型,这是一个很强大的类型。我们可以把这个类型看作是一个数组。只是C语言的数组,只能用正整数来作索引;在Lua中,你可以用任意类型来作数组的索引,除了nil。同样,在C语言中,数组的内容只允许一种类型;在Lua中,你也可以用任意类型的值来作数组的内容,除了nil。    Table的定义很简单,它的主要特征是用"{"和"}"来括起一系列数据元素的。比如:    T1 = {} -- 定义一个空表    T1[1]=10 -- 然后我们就可以象C语言一样来使用它了。    T1["John"]={Age=27, Gender="Male"}    这一句相当于:    T1["John"]={} -- 必须先定义成一个表,还记得未定义的变量是nil类型吗    T1["John"]["Age"]=27    T1["John"]["Gender"]="Male"    当表的索引是字符串的时候,我们可以简写成:    T1.John={}    T1.John.Age=27    T1.John.Gender="Male"    或    T1.John{Age=27, Gender="Male"}    这是一个很强的特性。    在定义表的时候,我们可以把所有的数据内容一起写在"{"和"}"之间,这样子是非常方便,而且很好看。比如,前面的T1的定义,我们可以这么写:    T1=    {      10, -- 相当于 [1] = 10      [100] = 40,      John= -- 如果你原意,你还可以写成:["John"] =      {        Age=27, & -- 如果你原意,你还可以写成:["Age"] =27        Gender=Male & -- 如果你原意,你还可以写成:["Gender"] =Male      },      20 -- 相当于 [2] = 20    }    看起来很漂亮,不是吗?我们在写的时候,需要注意三点:    第一,所有元素之间,总是用逗号","隔开;    第二,所有索引值都需要用"["和"]"括起来;如果是字符串,还可以去掉引号和中括号;    第三,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编;    表类型的构造是如此的方便,以致于常常被人用来代替配置文件。是的,不用怀疑,它比ini文件要漂亮,并且强大的多。  F. Function    函数,在Lua中,函数的定义也很简单。典型的定义如下:    function add(a,b) -- add 是函数名字,a和b是参数名字     return a+b -- return 用来返回函数的运行结果    end    请注意,return语言一定要写在end之前。假如你非要在中间放上一句return,那么请写成:do return end。    还记得前面说过,函数也是变量类型吗?上面的函数定义,其实相当于:    add = function (a,b) return a+b end    当你重新给add赋值时,它就不再表示这个函数了。你甚至可以赋给add任意数据,包括nil (这样,你就清除了add变量)。Function是不是很象C语言的函数指针呢?    和C语言一样,Lua的函数可以接受可变参数个数,它同样是用"…"来定义的,比如:    function sum (a,b,…)    如果想取得…所代表的参数,可以在函数中访问arg局部变量(表类型)得到。    如 sum(1,2,3,4)    则,在函数中,a = 1, b = 2, arg = {3, 4}    更可贵的是,它可以同时返回多个结果,比如:    function s()      return 1,2,3,4    end    a,b,c,d = s() -- 此时,a = 1, b = 2, c = 3, d = 4    前面说过,表类型可以拥有任意类型的值,包括函数!因此,有一个很强大的特性是,拥有函数的表,哦,我想更恰当的应该说是对象吧。Lua可以使用面向对象编程了。不信?那我举例如下:    t =    {     Age = 27     add = function(self, n) self.Age = self.Age+n end    }    print(t.Age) -- 27    t.add(t, 10)    print(t.Age) -- 37    不过,t.add(t,10) 这一句实在是有点土对吧?没关系,在Lua中,你可以简写成:    t:add(10) & -- 相当于 t.add(t,10)  G. Userdata 和 Thread    这两个类型的话题,超出了本文的内容,就不打算细说了。&
1 - 绪论Lua是一种为支持有数据描述机制的一般过程式编程语言而设计的扩展编程语言。它同样可以对面向对象语言、函数式程序设计(Functional Programming,如Lisp)以及数据驱动编程(data-driven programming)提供很好的支持。它的目标是被用作一种强大的、轻型的配置语言。Lua目前已经被实现为一个扩展库,是用clean C (ANSI C/C++的一个通用子集)编写的。&作为一个扩展语言,Lua没有"Main"函数的概念:它仅仅是嵌入一个宿主程序进行工作,可以称之为 嵌入式编程 或者简单的说是 宿主编程。这个宿主程序可以调用函数来执行Lua的代码片断,可以设置和读取Lua的变量,可以注册C函数让Lua代码调用。Lua的能力可以扩展到更大范围,在不同的领域内,这样就在同样的语法框架下创建了你自定义的编程语言。&Lua的发行版包括一个独立的嵌入式程序,lua,他使用Lua的扩展库来提供一个完全的Lua解释器。&Lua是自由软件,通常不提供任何担保,如它的版权说明中叙述的那样。 手册中描述的实现在Lua的官方网站可以找到,[&http://www.lua.org&]。&如果需要知道Lua设计背后的一些决定和讨论,可以参考以下论文,它们都可以在Lua的网站上找到。R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. Lua---an extensible extension language. Software: Practice & Experience 26 #6 (2.&L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. The design and implementation of a language for extending applications. Proceedings of XXI Brazilian Seminar on Software and Hardware (3.&L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. Lua: an extensible embedded language. Dr. Dobb's Journal 21 #12 (Dec .&R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. The evolution of an extension language: a history of Lua, Proceedings of V Brazilian Symposium on Programming Languages (2001) B-14-B-28.&Lua在葡萄牙语中的意思是“月亮”,发音是 LOO-ah。&2 - 语言这一章将描述Lua的词法、语法和语义结构。换句话说,这一章会讲什么标记是合法的,他们是如何组合的,以及他们的组合是什么含义。&语言结构会使用常用的扩展BNF范式来解释,如{a} 表示0或多个a, [a] 表示a是可选的(0个或1个)。非终端字体(不能显示的)用 斜体表示,关键字是粗体,其他终端符号用typewriter(等宽)字体,并用单引号引出。&2.1 - 词法约定Lua中的标识符(Identifiers)可以是任意的数字、字符和下划线“_”,但不能以数字开头。这条规则符合大多数编程语言中的标识符的定义。(字符的具体定义要根据系统的地区设置:任何区域设置可以认同的字母表中的字母都可以用在标识符中。)&下面的关键字(keywords)为保留关键字不可以作为标识符出现:& & and & & break & do & & else & & elseif& & end & & false & for & & function if& & in & & local & nil & & not & & or& & repeat & return & then & & true & & until & whileLua对大小写敏感:and是一个保留字,但是 And 和 AND 是两个不一样的、但都合法的标识符。习惯上来说,以下划线开始且后面跟着大写字母的标识符 (例如 _VERSION) 是为Lua内部变量所保留的。&下面的字符(串)是其他的一些标记:& & + & - & & & / & ^ & =& & ~= & &= & &= & & & & & ==& & ( & ) & { & } & [ & ]& & ; & : & , & . & .. & ...字符串(Literal strings) 以单引号或者双引号定界,同时可以包含以下C语言风格的转义字符:&/a --- 铃声(bell)&/b --- 回退(backspace)&/f --- form feed&/n --- 新行(newline)&/r --- 回车(carriage return)&/t --- 水平制表符(horizontal tab)&/v --- 垂直制表符(vertical tab)&// --- 反斜杠(backslash)&/" --- 双引号(quotation mark)&/' --- 单引号(apostrophe)&/[ --- 左方括号(left square bracket)&/] --- 右方括号(right square bracket)&另外,一个 `/newline′ (一个反斜杠加上一个真正的换行符)会导致字符串内的分行。字符串中的字符也可以使用转义字符`/ddd′通过数字值来指定。ddd 是最多为3个十进制数字的序列。Lua中的字符串也可以包含8进制数字,包括嵌入零,它可以表示为 `/0′。&字符串也可以用双方括号来定界[[ ? ? ? ]]。这种括号方式的语法,字符串可以跨越多行,也可以包含嵌套的,同时不会转义任何序列。方便起见,当开始的 `[[′ 后面紧跟着一个换行符的话,这个换行符不会包括在字符串内。举个例子:在一个使用ASCII编码(其中`a′ 的编码是 97,换行符是 10,字符`1′ 是 49)的系统中,以下四种格式得到的都是同一个字符串:& & (1) & "alo/n123/""& & (2) & '/97lo/10/04923"'& & (3) & [[alo& & & & 123"]]& & (4) & [[& & & & alo& & & & 123"]]数值常量(Numerical constants) 可以有一个可选的底数部分和一个可选的指数部分。以下是有效的数值常量:&& & 3 & 3.0 & 3.e-2 & 0.31416E1注释(Comments) 可以在任何地方出现,必须在最前面加上双减号 (--)。如果紧接着 -- 的文本不是 [[,那么会认为是一个 短注释(short comment), 这一行往后到行尾都是注释。否则,会认为是一个 常注释(long comment),注释直到相应的 ]]结束。长注释可以跨越多行,同时可以包含嵌套的 [[ ? ? ? ]] 括号对。&为了方便起见,文件的第一行如果是以#开始,这个机制允许Lua在Unix系统中用做一个脚本解释器(见 6)。2.2 - 值和类型Lua是一种 动态类型语言(dynamically typed language)。这意味着变量是没有类型的;只有值才有。语言中没有类型定义。所有的值都包含他自身的类型。Lua中有八种基本类型:nil, boolean, number, string, function, userdata, thread 和 table。 Nil 空类型只对应 nil值,他的属性和其他任何值都有区别;通常它代表没有有效的值。 Boolean 布尔类型有两种不同的值 false and true。在Lua中, nil and false 代表成假条件;其他任何值都代表成真条件。 Number 数字类型表示实数(双精度浮点数)。(构建Lua解释器时也可以很容易地用其他内部的表示方式表示数字,如单精度浮点数或者长整型)。 String 字符串类型表示一个字符的序列。Lua 字符串可以包含8位字符,包括嵌入的 ('/0') (见 2.1)。&函数是Lua中的 第一类值(first-class values)。也就是说函数可以保存在变量中,当作参数传递给其他函数,或者被当作结果返回。Lua可以调用(和处理)Lua写的函数和C写的函数 (见 2.5.7)。&用户数据类型(userdata) 提供了让任意C数据储存在Lua变量中的功能。这种类型直接对应着一块内存,Lua中也没有任何预先定义的操作,除了赋值和一致性比较。然而,通过使用 元表(metatables),程序员可以定义处理userdata的操作。(见 2.8)。 Userdata 值不能在Lua中建立或者修改,只能通过 C API。这保证了宿主程序的数据完整性。&线程(thread) 类型代表了相互独立的执行线程,用来实现同步程序。&表(table) 类型实现了联合数组,也就是说,数组不仅可以使用数字,还能使用其他的值(除了 nil)。 而且,tables 可以是 互异的(heterogeneous),他们可以保存任何类型的值(除了 nil)。 Tables 是Lua中唯一的数据结构机制;他们可以用来表示一般数组,特征表,集合,记录,图,树等等。如果要表示记录,Lua使用字段名作为索引。语言支持 a.name 这种比较优美的表示方式,还有 a["name"]。在Lua中有几种建立表的简便方法 (见 2.5.6)。&就像索引一样,表字段的值也可以是任何类型(除了 nil)。特别需要注意地是,由于函数是第一型的值,表字段也可以包含函数。这样表也可以支持 方法(methods) (见 2.5.8)。&表,函数,和用户数据类型的值都是 对象(objects):变量不会包含他们的实际值,只是一个他们的引用(references)。 赋值,参数传递和函数返回只是操作这些值的引用,这些操作不会暗含任何拷贝。&库函数 type 返回一个字符串描述给出值所表示的类型 (见 5.1)。&2.2.1 - 类型转换Lua提供运行时的数字和字符串值得自动转换。任何对字符串的算术操作都会现尝试把字符串转换成数字,使用一般规则转换。反过来,当一个数值用在需要字符串的地方时,数字会自动转换成字符串,遵循一种合理的格式。如果要指定数值如何转换成字符串,请使用字符串库中的 format 函数(见 5.3)。2.3 - 变量变量是储存值的地方。Lua中有三种不同的变量:全局变量,局部变量和表字段。&一个名称可以表示全局变量或局部变量(或者一个函数的正式参数,一种局部变量的特殊形式):&& & &var ::= NameLua假设变量是全局变量,除非明确地用local进行声明 (见 2.4.7)。局部变量有 词义范围(lexically scoped):局部变量可以被在它们范围内的函数自由访问 (见 2.6)。&在变量第一次赋值之前,它的值是 nil。&方括号用于对表进行检索:&& & &var ::= prefixexp `[′ exp `]′第一个表达式 (prefixexp)结果必须是表;第二个表达式 (exp) 识别表中一个特定条目。给出表的表达式有一个限制语法;详细见 2.5。&var.NAME 语法是 var["NAME"] 的较好形式:&& & &var ::= prefixexp `.′ Name访问全局变量和表字段的实质可以通过元表进行改变。对索引变量 t[i] 的访问等同于调用 gettable_event(t,i)。(关于 gettable_event 的完整描述见 2.8。这个函数并没有在Lua中定义,也无法调用。我们在这里仅仅用来解释原理)。&所有的全局变量存在一个普通的Lua表中,称之为 环境变量表(environment tables) 或简称 环境(environments)。由C写的并导入到Lua中的函数 (C 函数) 全部共享一个通用 全局环境(global environment)。Lua写的每个函数 (a Lua 函数) 都有一个它自己的环境的引用,这样这个函数中的所有的全局变量都会指向这个环境变量表。当新创建一个函数时,它会继承创建它的函数的环境。要改变或者获得Lua函数的环境表,可以调用 setfenv or getfenv (见 5.1)。&访问全局变量 x 等同于 _env.x,又等同于&& & gettable_event(_env, "x")_env 是运行的函数的环境。(_env 变量并没有在Lua中定义。我们这里仅仅用来解释原理)&2.4 - 语句Lua支持一种很通俗的语句集,和Pascal或者C中的很相似。他包括赋值,控制结构,过程调用,表构造和变量声明。&2.4.1 - 语句段Lua执行的最小单元称之为一个 段(chunk)。一段语句就是简单的语句的序列,以顺序执行。每一个语句后面都可以加上一个分号(可选):&& & &chunk ::= {stat [`;′]}Lua将语句段作为一个匿名函数 (见 2.5.8) 的本体进行处理。这样,语句段可以定义局部变量或者返回值。&一段语句可以储存在文件内或者宿主程序的一个字符串中。当语句段被执行时,他首先被预编译成虚拟机使用的字节码,然后虚拟机用一个解释器执行被编译的代码。&语句段也可以被预编译为二进制代码;详情参看 luac 程序。源代码和编译形态可以互相转换;Lua自动监测文件类型然后作相应操作。&2.4.2 - 语句块一个语句块是一系列语句;从语句构成上来看,语句块等同于语句段:& & &block ::= chunk一个语句块可以明确定界来替换单个语句:& & &stat ::= do block end显式语句块可以很好地控制变量的声明范围。显示语句块有时也常会在另一个语句块的中间添加 return 或 break 语句 (见 2.4.4)。&2.4.3 - 赋值Lua允许多重赋值。因此,赋值的语法定义为:等号左边是一个变量表,右边是一个表达式表。两边的表中的元素都用逗号分隔开来:& & &stat ::= varlist1 `=′ explist1& & &varlist1 ::= var {`,′ var}& & &explist1 ::= exp {`,′ exp}我们将在 2.5 讨论表达式。&在赋值之前,值的表长度会被 调整 为和变量的表一样。如果值比需要的多,多出的值就会被扔掉。如果值的数量不够,就会用足够多的 nil 来填充表直到满足数量要求。如果表达式表以一个函数调用结束,那么在赋值之前,函数返回的所有的值都会添加到值的表中(除非把函数调用放在括号里面;见 2.5)。&赋值语句首先计算出所有的表达式,然后才会执行赋值,所以代码:&& & i = 3& & i, a[i] = i+1, 20设置 a[3] 为 20,但不影响 a[4]。因为在 a[i] 中的 i 在赋值为4之前是等于3。同样的,下面这行:& & x, y = y, x可以交换 x 和 y 的值。&对全局变量和表字段的赋值可以看作是通过元表进行的。对一个索引变量的赋值 t[i] = val 等同于 settable_event(t,i,val)。 (settable_event详细介绍参看 2.8 ,Lua中并未定义该函数,他也无法直接调用。我们这里只是用它来进行解释。)&对全局变量的赋值 x = val 等同于赋值语句 _env.x = val,像前面也等同于:& & settable_event(_env, "x", val)_env 是运行函数的环境。(_env 变量并未在Lua中定义。我们这里只是用来进行解释。)&2.4.4 - 控制结构控制结构 if, while 和 repeat 具有通用的含义和类似的语法:&& & &stat ::= while exp do block end& & &stat ::= repeat block until exp& & &stat ::= if exp then block {elseif exp then block} [else block] endLua也有 for 语句,有两种格式 (见 2.4.5)。&控制结构的条件表达式 exp 可以返回任意值。false 和 nil 都表示假。所有其他的值都认为是真(特别要说明的:数字0和空字符串也表示真)。&语句 return 用来从函数或者是语句段中返回一个值。函数和语句段都可以返回多个值,所以 return 语句的语法为:& & &stat ::= return [explist1]break 语句可以用来终止while, repeat 或者 for 循环的执行,直接跳到循环后面的语句。&& & &stat ::= breakbreak 结束最里面的一个循环。&由于语法的原因, return 和 break 语句只能作为语句块的 最后一个 语句。如果确实需要在语句块的中间使用 return 或者 break,需要使用一个显示语句块: `do return end′ 和 `do break end′,这样现在 return 和 break 就成为他们(内部)语句块中的最后一个语句了。实际上,这两种用法一般只用在调试中。&2.4.5 - For 语句for 语句有两种形式:数值形式和一般形式。&数值形式的 for 循环根据一个控制变量用算术过程重复一语句块。语法如下:&& & &stat ::= for Name `=′ exp `,′ exp [`,′ exp] do block endblock 语句块根据 name 以第一个 exp 的值开始,直到他以第三个 exp 为步长达到了第二个 exp。一个这样的 for 语句:&& & for var = e1, e2, e3 do block end等价于一下代码:&& & do& & & local var, _limit, _step = tonumber(e1), tonumber(e2), tonumber(e3)& & & if not (var and _limit and _step) then error() end& & & while (_step&0 and var&=_limit) or (_step&=0 and var&=_limit) do& & & block& & & var = var + _step& & & end& & end注意:&三种控制表达式只会被计算一次,在循环开始之前。他们的结果必须是数值。&_limit 和 _step 是不可见的变量。这里只是为了进行解释。&如果你在程序块内给 var 赋值,结果行为将会不确定。&如果没有给出第三个表达式(步长),那么默认为1。&你可以使用 break 来退出 for 循环。&循环变量 var 是局部变量;你不可以在 for 循环结束之后继续使用。如果你需要使用这个值,请在退出循环之前把它们传给其他变量。&for 的语句的一般形式是操作于函数之上的,称之为迭代器(iterators)。每一个迭代过程,它调用迭代函数来产生新的值,直到新的值是 nil 。一般形式 for 循环有如下语法:&& & &stat ::= for Name {`,′ Name} in explist1 do block end一个这样的 for 语句&& & for var_1, ..., var_n in explist do block end等同于以下代码:&& & do& & & local _f, _s, var_1 = explist& & & local var_2, ... , var_n& & & while true do& & & var_1, ..., var_n = _f(_s, var_1)& & & if var_1 == nil then break end& & & block& & & end& & end注意:explist 只会计算一次。他的结果是一个 迭代 函数,一个 状态,和给第一个 迭代变量的一个初始值。&_f 和 _s 是不可见的变量。这里只是用来进行解释说明。&如果你在语句块中给 var_1 赋值,那么行为就会变得不确定。&你可以使用 break 来退出 for 循环。&循环变量 var_i 是局部变量;你不可以在 for 循环结束之后继续使用。如果你需要使用这个值,请在退出循环之前把它们传给其他变量。&2.4.6 - 语句式函数调用如果要忽略可能的影响,函数调用可以按照语句执行:&& & &stat ::= functioncallI在这里,所有的返回值都会被忽略。函数调用将在 2.5.7 详细解释。&2.4.7 - 局部变量声明局部变量可以在语句块中任何地方声明。声明时也可以添加一个初始赋值:&& & &stat ::= local namelist [`=′ explist1]& & &namelist ::= Name {`,′ Name}如果出现初始赋值,他的语法和多重赋值语句一样(见 2.4.3)。否则,所有的变量都会初始化为 nil。&一个语句段也是一个语句块(见 2.4.1),所以语句段之内的任何显式语句块之外也可以声明局部变量。这种局部变量在语句段结束就会销毁。&局部变量的可见规则会在 2.6解释。&2.5 - 表达式Lua中有以下几种基本表达式:&& & &exp ::= prefixexp& & &exp ::= nil | false | true& & &exp ::= Number& & &exp ::= Literal& & &exp ::= function& & &exp ::= tableconstructor& & &prefixexp ::= var | functioncall | `(′ exp `)′数字和字符串已经在 2.1 中解释;变量在 2.3 中解释;函数定义在 2.5.8;函数调用在 2.5.7;表构造器在 2.5.6。&一个用括号括起的表达式只会返回一个值。这样,(f(x,y,z)) 将只会返回单一的一个值,即使 f 可以返回多个值,((f(x,y,z)) 的值将是 f 返回的第一个值或者如果 f 没有返回任何值就是 nil )。&表达式也可以使用各种算术运算符,关系运算符和逻辑运算符,下面几节就会讲到。&2.5.1 - 算术运算符Lua支持常见的几种运算符:二元 + (加), - (减), (乘), / (除), 以及 ^ (指数运算); 一元 - (负号)。如果操作数是数字,或者是可以转换成数字的字符串(见 2.2.1),那么所有的操作都和算术意义上的运算一致(除了指数)。指数运算其实是调用一个全局函数 __pow,否则一个合适的元方法将会被调用(见 2.8)。标准数学库定义了函数 __pow,给出了指数运算的定义(见 5.5)。&2.5.2 - 关系运算符Lua中的关系运算符有&& & == & ~= & & & & & &= & &=这些运算只会产生 false 或 true值。&等于 (==) 先比较操作数的类型。如果类型不一样,结果便是 false。否则,再比较操作数的值。对象(表,用户数据,线程,和函数)是按照引用进行比较:只有两个对象是同一个对象的时候,才认为是相等。每次你创建一个新的对象(表,用户数据,或者是函数)。这个新的对象将不同于前面存在的任何对象。&你可以用"eq"元方法改变Lua比较表的方式(见 2.8)。&2.2.1 的转换规则 不适用 于相等比较。这样," "0"==0 结果是 false ,同样 t[0] 和 t["0"] 给出的是表中不同的字段。&而操作符 ~= 是等于 (==) 的相反的操作。&T操作符的执行顺序如下。如果两个参数都是数字,那么它们就直接进行比较。如果,两个参数都是字符串,那么它们的值会根据当前的区域设置进行比较。否则,Lua尝试调用"lt"或者 "le" 元方法(见 2.8)。&
2.5.3 - 逻辑运算符Lua中的逻辑运算符是:&& & and & or & not和控制结构一样(见 2.4.4),所有的逻辑操作符认为 false 和 nil 都是假,其他的值都是真。&not 操作符总是返回 false 或 true。&合取运算 and 如果第一个参数是 false 或者 nil 则返回第一个参数;否则 and 返回第二个参数。析取运算 or 如果第一个参数不是 nil 或 false 则返回第一个参数,否则 or 返回第二个参数。 and 和 or 都使用截取计算,也就是,只有有必要的情况下才计算第二个参数。例如:&& & 10 or error() & & -& 10& & nil or "a" & & & -& "a"& & nil and 10 & & & -& nil& & false and error() & -& false& & false and nil & & -& false& & false or nil & & -& nil& & 10 and 20 & & & -& 202.5.4 - 串联接在Lua中字符串连接操作符是两个点 (`..′)。如果两边的操作数都是字符或者数字,他们就都会按照 2.2.1的规则被转换成字符串。否则,将调用 "concat" 元方法(见 2.8)。&2.5.5 - 优先级Lua中的操作符的优先级如下表所示,从低到高优先级:&& & or& & and& & & & & & &= & &= & ~= & ==& & ..& & + & -& & & & /& & not & - (unary)& & ^表达式中,你可以使用括号来改变优先顺序。串联接符 (`..′) 和指数符 (`^′) 都是右结合的。其他二元操作都是左结合的。&2.5.6 - 表构造器表构造器是创建表的表达式。当计算构造器的时候,就会创建一个新的表。构造器可以用来创建空的表,或者创建表并初始化一些字段。一般的语法如下:&& & tableconstructor ::= `{′ [fieldlist] `}′& & fieldlist ::= field {fieldsep field} [fieldsep]& & field ::= `[′ exp `]′ `=′ exp | Name `=′ exp | exp& & fieldsep ::= `,′ | `;′[exp1] = exp2 形式的每一个添加到新表中的字段条目以 exp1 为键并以 exp2 为值。name = exp 形式的字段,等同于 ["name"] = exp。最后,exp 形式的字段等同于 [i] = exp 其中 i 是连续的整数,从1开始。其它格式的字段不会影响它的计数。例如:&& & a = {[f(1)] = "x", "y"; x = 1, f(x), [30] = 23; 45}等同于:&& & do& & & local temp = {}& & & temp[f(1)] = g& & & temp[1] = "x" & & & -- 1st exp& & & temp[2] = "y" & & & -- 2nd exp& & & temp.x = 1 & & & & -- temp["x"] = 1& & & temp[3] = f(x) & & -- 3rd exp& & & temp[30] = 23& & & temp[4] = 45 & & & -- 4th exp& & & a = temp& & end如果列表中最后一个字段的形式是 exp 同时表达式又是一个函数调用,那么调用返回的所有值会依次进入列表(见 2.5.7)。如果要避免这种情况,在函数调用两边加上括号(见 2.5)。&字段列表可以有一个结尾的分隔符,这个对由机器生成的列表十分方便。&2.5.7 - 函数调用Lua中的一个函数调用有如下语法:&& & functioncall ::= prefixexp args在函数调用中,首先会计算 prefixexp 和 args 。如果 prefixexp 的值是 function 类型,那么那个函数就会被调用,同时使用给出的参数。否则,他的 "call" 元方法就会被调用,第一个参数是 prefixexp 的值,接下来是原来的调用参数(见 2.8)。&形式&& & functioncall ::= prefixexp `:′ Name args可以用来调用“方法”("methods")。调用 v:name(...) 语法上比 v.name(v,...),要好一些,除非表达式 v 只计算一次。&参数可以有以下几种语法:&& & args ::= `(′ [explist1] `)′& & args ::= tableconstructor& & args ::= Literal所有的参数表达式都会在实际调用之前进行计算。f{...} 的调用形式在语法上较 f({...}) 要好,是因为,参数列表示一个单独的新表。 f'...' (或者 f"..." 或者 f[[...]]) 较 f('...') 要好,是因为参数列表是一个单独的字符串。&因为函数可以返回任意个结果(见 2.4.4),结果的数量必须在使用它们前进行调整。如果函数按照语句进行调用(见 2.4.6),那么它的返回列表就会被调整为零个元素,这样就舍弃了所有的返回值。如果调用函数时,他是一个表达式列表的最后一个元素,那么不会做调整(除非调用时加了括号)。&以下是一些例子:&& & f() & & & & & -- 调整为0个结果& & g(f(), x) & & & -- f() 被调整成1个结果& & g(x, f()) & & & -- g 获得 x 加上f()返回的所有值& & a,b,c = f(), x & -- f() 被调整成1个结果(此时c获得nil值)& & a,b,c = x, f() & -- f() 被调整为两个结果& & a,b,c = f() & & -- f() 被调整为3个结果& & return f() & & & -- 返回所有 f() 返回的值& & return x,y,f() & -- 建立一个表包含所有 f() 返回的值& & {f()} & & & & -- creates a list with all values returned by f()& & {f(), nil} & & & -- f() 被调整为一个结果如果你用括号括起调用的函数,那么它就会被调整为返回一个值。&& & return x,y,(f()) & -- returns x, y, and the first value from f()& & {(f())} & & & & -- creates a table with exactly one element作为Lua语法自由格式的一个例外,你不能在函数调用的 `(′ 前面加入一个换行。这个限制可以避免语言中的一些二义性。如果你写:&& & a = f& & (g).x(a)Lua会读作 a = f(g).x(a)。这样,如果你想执行为两条语句,你必须在中间加分号。如果你实际上想调用 f,你就必须删除 (g) 前面的换行。&return functioncall 的调用格式称之为 尾部调用(tail call)。Lua实现了proper tail calls;在一个尾部调用中,被调用的函数将会重新使用调用程序的栈。因此,程序执行对嵌套尾部调用的次数没有任何限制。然而,尾部调用会清楚调用函数的调试信息。注意尾部调用只有在特殊的语法中才能出现,也就是 return 只有一个函数调用作为参数,这种语法保证了调用函数确切返回被调用函数的返回值。所以,下面的例子都不是尾部调用:&return (f(x)) & & -- results adjusted to 1return 2 f(x)return x, f(x) & & -- additional resultsf(x); return & & & -- results discardedreturn x or f(x) & -- results adjusted to 12.5.8 - 函数定义函数定义的语法是:&& & function ::= function funcbody& & funcbody ::= `(′ [parlist1] `)′ block end下面较好的语法简化了函数定义:&& & stat ::= function funcname funcbody& & stat ::= local function Name funcbody& & funcname ::= Name {`.′ Name} [`:′ Name]语句&& & function f () ... end会被翻译为&& & f = function () ... end语句&& & function t.a.b.c.f () ... end会被翻译为&& & t.a.b.c.f = function () ... end语句&& & local function f () ... end会被翻译为&& & f = function () ... end一个函数定义是一个可执行的表达式,他的类型为 函数(function) 。当Lua预编译语句段的时候,他的函数体也会被预编译。这样,当Lua执行函数定义的时候,函数被 实例化 (封装 closed)。这个函数实例(或闭包 closure)是表达式的最终结果。同一个函数的不同的实例可以引用不同的外部局部变量也可以有不同的环境表。&形式参数(代表参数的变量,简称形参)就像用实际参数值(简称实参)初始化的局部变量一样。&& & parlist1 ::= namelist [`,′ `...′]& & parlist1 ::= `...′当调用一个函数时,实参表会调整为和形参一样的长度,除非函数是 variadic 或者 变长参数函数(vararg function)。变长参数函数在其参数列表最后有三个点 (`...′)。 变长参数函数不会对参数列表进行调整;而是,它把所有的额外实参放到一个隐含的形参 arg中。 arg 的值是一个表,包含一个字段 `n′ 表示额外参数的个数,位置 1, 2, ..., n是额外的参数。&请思考以下函数定义的例子:&& & function f(a, b) end& & function g(a, b, ...) end& & function r() return 1,2,3 end然后,我们有以下实参到形参的对应关系:&& & CALL & & & & PARAMETERS& & f(3) & & & & a=3, b=nil& & f(3, 4) & & & a=3, b=4& & f(3, 4, 5) & & a=3, b=4& & f(r(), 10) & & a=1, b=10& & f(r()) & & & a=1, b=2& & g(3) & & & & a=3, b=nil, arg={n=0}& & g(3, 4) & & & a=3, b=4, & arg={n=0}& & g(3, 4, 5, 8) & a=3, b=4, & arg={5, 8; n=2}& & g(5, r()) & & a=5, b=1, & arg={2, 3; n=2}结果使用 return 语句返回(见 2.4.4)。如果控制到达了函数尾部而没有遇到 return 语句,那么函数没有返回值。&冒号(:) 语法是用来定义 methods 的,也就是,函数有一个隐含的额外参数 self. 。这样,语句:&& & function t.a.b.c:f (...) ... end相对以下是较好的形式:&& & t.a.b.c.f = function (self, ...) ... end2.6 - 可见性规则Lua是一个有词法范围的语言。变量的范围从声明语句后的第一个语句开始到包含声明的最内部的语句块为止。例如:&x = 10 & & & & & -- global variabledo & & & & & & -- new block& local x = x & & & -- new `x', with value 10& print(x) & & & & --& 10& x = x+1& do & & & & & & -- another block& & local x = x+1 & -- another `x'& & print(x) & & & --& 12& end& print(x) & & & & --& 11endprint(x) & & & & --& 10 (the global one)注意:在类似 local x = x,正在声明的新的 x 尚未进入范围,所以第二个 x 指代的是外面的变量。&由于词法范围的规则,在局部变量的范围内定义的函数可以任意访问这些变量。例如:&local counter = 0function inc (x)& counter = counter + x& return counterend内部函数使用的局部变量在函数内部称之为 上值(upvalue),或者 外局部变量(external local variable)。&注意每个 local 语句执行时会定义一个新的局部变量。看以下例子:&a = {}local x = 20for i=1,10 do& local y = 0& a[i] = function () y=y+1; return x+y endend循环产生了十个闭包(也就是,十个匿名函数的实例)。每个闭包使用不同的 y 变量,但他们共享同一个 x 变量。&2.7 - 错误处理因为Lua是一个扩展语言,所有的Lua动作都是从宿主程序中调用Lua库中函数的C代码开始的(见 3.15)。无论错误发生在Lua编译过程时或执行时,控制返回C,然后可以做相应的处理(比如打印一个错误)。&Lua代码可以通过调用error函数来产生一个错误(见 5.1)。如果你要在Lua中捕获错误,你可以使用 pcall 函数(见 5.1)。&2.8 - 元表 (Metatables)Lua中的每一个表和用户数据都可以拥有一个 元表(metatable)。这个 元表 是一个普通的Lua表,定义了在特定操作下原始表和用户数据的行为。你可以通过设置一个对象的元表中的特定字段来更改它某些方面的行为。例如,当一个对象是一个加法的操作数时,Lua检查它的元表中的 "__add" 字段是不是一个函数。如果是,Lua调用它来执行加法。&我们称元表中的键(字段名,key)为 事件(events) ,值为 元方法(metamethods)。在上一个例子中, "add" 是事件,执行加法的函数是元方法。&你可以通过 set/getmetatable 函数来查询和更改一个对象的元表(见 5.1)。&元表可以控制对象在算术操作、比较、串连接、索引取值中如何运行。元表也可以定义一个函数当收集内存垃圾时调用。每一个操作这里Lua都用一个特定的键关联,称之为事件。当Lua对一个表或是一个用户数据执行上面中的一个操作时,它先检查元表控制的操作已经罗列在下面。每个操作有一个相应的名称,代表了他的含义。他们在元表中的键是由名称前加上两条下划线;如,操作 "add" 的键是 "__add"。这些操作的语义&这里给出的Lua代码仅仅是说明性的;真正的行为是硬编码在解释器中的,比下面的的模拟的效率要高很多。描述中用到的函数 (rawget, tonumber, 等等) 在 5.1 中会对他们进行描述。特别地,要获得一个给定对象的元方法,我们使用这个表达式:&metatable(obj)[event]这个要读作:&rawget(metatable(obj) or {}, event)也就是,访问元方法时不会调用其它元方法,同时调用没有元表的对象不会出错(它返回一个 nil值)。&"add": + 加法操作。&下面的 getbinhandler 函数定义了Lua如何给一个二元操作选择一个处理器。首先,Lua尝试第一个操作数。如果它的类型没有定义这个操作的处理器,那么然后Lua尝试第二个操作数。&function getbinhandler (op1, op2, event)& return metatable(op1)[event] or metatable(op2)[event]end利用该函数,op1 + op2 的行为方式可看作是&function add_event (op1, op2)& local o1, o2 = tonumber(op1), tonumber(op2)& if o1 and o2 then -- both operands are numeric?& return o1 + o2 & -- `+' here is the primitive `add'& else -- at least one of the operands is not numeric& local h = getbinhandler(op1, op2, "__add")& if h then& & -- call the handler with both operands& & return h(op1, op2)& else -- no handler available: default behavior& & error("...")& end& endend"sub": - 操作。行为方式类似 "add" 操作。&"mul": 操作。行为方式类似 "add" 操作。&"div": / 操作。行为方式类似 "add" 操作。&"pow": ^ (指数) 操作&function pow_event (op1, op2)& local o1, o2 = tonumber(op1), tonumber(op2)& if o1 and o2 then -- both operands are numeric?& return __pow(o1, o2) & -- call global `__pow'& else -- at least one of the operands is not numeric& local h = getbinhandler(op1, op2, "__pow")& if h then& & -- call the handler with both operands& & return h(op1, op2)& else -- no handler available: default behavior& & error("...")& end& endend"unm": 一元取负 - 操作。&function unm_event (op)& local o = tonumber(op)& if o then -- operand is numeric?& return -o -- `-' here is the primitive `unm'& else -- the operand is not numeric.& -- Try to get a handler from the operand& local h = metatable(op).__unm& if h then& & -- call the handler with the operand and nil& & return h(op, nil)& else -- no handler available: default behavior& & error("...")& end& endend"concat": .. (串连接)操作。&function concat_event (op1, op2)& if (type(op1) == "string" or type(op1) == "number") and& & (type(op2) == "string" or type(op2) == "number") then& return op1 .. op2 -- primitive string concatenation& else& local h = getbinhandler(op1, op2, "__concat")& if h then& & return h(op1, op2)& else& & error("...")& end& endend"eq": == 操作。函数 getcomphandler 定义了Lua是如何为比较操作选择一个元方法的。只有当参与比较的两个对象属于同一类型而且需要的元方法一样时,才会选择这个元方法。&function getcomphandler (op1, op2, event)& if type(op1) ~= type(op2) then return nil end& local mm1 = metatable(op1)[event]& local mm2 = metatable(op2)[event]& if mm1 == mm2 then return mm1 else return nil endend事件如下定义:&function eq_event (op1, op2)& if type(op1) ~= type(op2) then -- different types?& return false & -- different objects& end& if op1 == op2 then & -- primitive equal?& return true & -- objects are equal& end& -- try metamethod& local h = getcomphandler(op1, op2, "__eq")& if h then& return h(op1, op2)& else& return false& endenda ~= b is equivalent to not (a == b).&"lt": & 操作。&function lt_event (op1, op2)& if type(op1) == "number" and type(op2) == "number" then& return op1 & op2 & -- numeric comparison& elseif type(op1) == "string" and type(op2) == "string" then& return op1 & op2 & -- lexicographic comparison& else& local h = getcomphandler(op1, op2, "__lt")& if h then& & return h(op1, op2)& else& & error("...");& end& endenda & b is equivalent to b & a.&"le": &= 操作。&function le_event (op1, op2)& if type(op1) == "number" and type(op2) == "number" then& return op1 &= op2 & -- numeric comparison& elseif type(op1) == "string" and type(op2) == "string" then& return op1 &= op2 & -- lexicographic comparison& else& local h = getcomphandler(op1, op2, "__le")& if h then& & return h(op1, op2)& else& & h = getcomphandler(op1, op2, "__lt")& & if h then& & & return not h(op2, op1)& & else& & & error("...");& & end& end& endenda &= b is equivalent to b &= a. Note that, in the absence of a "le" metamethod, Lua tries the "lt", assuming that a &= b is equivalent to not (b & a).&"index": 通过索引访问 table[key]。&function gettable_event (table, key)& local h& if type(table) == "table" then& local v = rawget(table, key)& if v ~= nil then return v end& h = metatable(table).__index& if h == nil then return nil end& else& h = metatable(table).__index& if h == nil then& & error("...");& end& end& if type(h) == "function" then& return h(table, key) & & -- call the handler& else return h[key] & & & -- or repeat operation on itend"newindex": 给表的索引赋值 table[key] = value。&function settable_event (table, key, value)& local h& if type(table) == "table" then& local v = rawget(table, key)& if v ~= nil then rawset(table, key, value); return end& h = metatable(table).__newindex& if h == nil then rawset(table, key, value); return end& else& h = metatable(table).__newindex& if h == nil then& & error("...");& end& end& if type(h) == "function" then& return h(table, key,value) & -- call the handler& else h[key] = value & & & & -- or repeat operation on itend"call": 当Lua调用某个值时调用。&function function_event (func, ...)& if type(func) == "function" then& return func(unpack(arg)) & -- primitive call& else& local h = metatable(func).__call& if h then& & return h(func, unpack(arg))& else& & error("...")& end& endend2.9 - 垃圾收集Lua 会自动进行内存管理。这意味着你不需要担心新对象的内存分配问题,也不需要释放不用的对象。Lua 通过不断地运行 垃圾收集器 收集 dead objects (也就是那些Lua中无法访问的对象)来自动管理内存。Lua中所有的对象都是自动管理的目标:表,用户数据,函数,线程,和字符串。Lua使用两个数字控制垃圾收集循环。一个数字表示Lua使用的动态内存的字节数,另一个是阀值。当内存字节数到达阀值时,Lua就运行垃圾收集器,来释放死对象的空间。一旦字节计数器被调整,那么阀值就会被设为字节计数器新值的两倍。&通过C API,你可以查询和更改阀值(见 3.7)。将阀值设为零时会强制立刻进行垃圾收集,同时把他设为足够大就可以停止垃圾收集。仅使用Lua代码中的 gcinfo 和 collectgarbage 函数 (见 5.1)可以获得一定程度上对垃圾收集循环的控制。&2.9.1 - 垃圾收集元方法 (Garbage-Collection Metamethods)使用 C API,你可以对用户数据设置一个垃圾收集元方法(见 2.8)。这些元方法也称为 终结器(finalizers)。终结器允许你用外部的资源管理来调整Lua的垃圾收集(如关闭文件,网络或数据库连接,或者释放你自己的内存。&用元表中包含 __gc 字段的自由用户数据不会立即被垃圾收集器回收。而是,Lua把它们放在一个列表中。收集完毕之后,Lua会对这个列表中的用户数据执行和以下函数相等的操作:&function gc_event (udata)& local h = metatable(udata).__gc& if h then& h(udata)& endend在每个垃圾收集过程最后,调用用户数据的终结器的顺序,将按照他们在收集过程中添加到列表中的相反顺序进行。也就是,第一个被调用的终结器是和在程序中创建的最后一个用户数据相关的那个终结器。&2.9.2 - 弱表一个 弱表(weak table) 是一个包含的元素是 弱引用(weak references)的表。垃圾收集器会忽略弱引用。换句话说,如果指向一个对象的引用只有弱引用,那么这个对象还是要被垃圾收集器回收。&弱表可以包含弱的键,弱的值,或者两者皆有。一个包含弱键的表允许它的键被回收,但值不可以。一个同时包含弱键和弱值的表允许键和值的回收。无论哪种情况,只要键或者值中的一个被回收了,那么这一对键值将会从表中删除。这个表的弱属性是由它的元表的 __mode 字段控制的。如果 __mode 字段是一个包含字符 `k′的字符串,那么表中的键是弱键。如果 __mode 字段是一个包含字符 `v′ 的字符串,那么表中的值是弱值。&在你将表用作元表之后,你不应该更改 __mode 字段的值。否则,这个元表控制的表的弱表行为将会不确定。&2.10 - 同步程序Lua支持同步程序,也称为 半同步程序(semi-coroutines) 或 协同多线程(collaborative multithreading)。Lua中的一个同步程序代表了一个独立的执行线程。然而,不像在多线程系统中的线程那样,一个同步程序只有在调用了一个yield(产生结果)函数才能挂起它的执行。&你可以调用 coroutine.create 来创建一个同步程序。它唯一的一个参数是一个函数,代表同步程序的主函数。create 函数仅仅建立一个新的同步程序然后返回一个它的句柄 (一个线程 thread 类型的对象);它不会启动该同步程序。&当你第一次调用 coroutine.resume,将 coroutine.create 返回的线程对象作为第一个参数传递给它,然后同步程序就启动了,从它的主函数的第一行开始。传给 coroutine.resume 的额外的参数会作为同步程序主函数的参数传递过去。在同步程序开始执行之后,它一直运行到它结束或产生结果。&一个同步程序通过两种方式结束它的运行:正常情况下,当它的主函数返回(显式地或隐式的,在最后一个指令之后)时结束;异常地,如果有未保护的错误。第一各情况下,coroutine.resume 返回 true,加上同步程序主函数返回的其它值。在有错误的情况下,coroutine.resume 返回 false ,并附上错误信息。&一个同步程序通过调用 coroutine.yield 来产生结果。当一个同步程序产生结果,相应的 coroutine.resume 就立刻返回,即使操作发生在嵌套函数调用中(也就是,不在主函数中,而在被主函数直接或间接调用的函数中)。在这种情况下, coroutine.resume 也返回 true,以及传给 coroutine.yield。的所有参数。下次你继续同一个同步程序时,它会从它原来yield的地方继续执行,而 coroutine.yield 将返回给主程序传给 coroutine.resume 的额外参数。&coroutine.wrap 函数创建一个和 coroutine.create 一样的同步程序,但它不返回同步程序本身,而是返回一个继续同步程序的函数(当调用的时候)。传递给这个函数的参数作为继续resume的额外参数。函数将返回resume返回的所有值,出除了第一个(布尔值的错误代码)。不像 coroutine.resume,这个函数不捕获错误;出现任何错误都传回给调用者。&请考虑以下例子:&function foo1 (a)print("foo", a)return coroutine.yield(2a)endco = coroutine.create(function (a,b)& & print("co-body", a, b)& & local r = foo1(a+1)& & print("co-body", r)& & local r, s = coroutine.yield(a+b, a-b)& & print("co-body", r, s)& & return b, "end"end)& &&a, b = coroutine.resume(co, 1, 10)print("main", a, b)a, b, c = coroutine.resume(co, "r")print("main", a, b, c)a, b, c = coroutine.resume(co, "x", "y")print("main", a, b, c)a, b = coroutine.resume(co, "x", "y")print("main", a, b)当你运行它的时候,它会产生以下输出结果:&co-body 1 & & 10foo & 2main & true & 4co-body rmain & true & 11 & & -9co-body x & & ymain & true & 10 & & endmain & false & cannot resume dead coroutine&
3 - 应用程序接口这一节描述Lua中的C API,这是对于宿主程序可用的C函数集合,用以和Lua通讯。所有的API函数及其相关类型和常量都声明在头文件lua.h中。&即便每次我都使用“函数”这个词,任何设施在API里面都可能被一个宏所替代。所有这些宏(macro)都只使用一次它的参数(除了第一个参数、这个每次总是一个Lua状态),所以不会产生隐藏的副作用。&3.1 - 状态Lua库是可重入的(reentrant)的:它没有全局变量。整个Lua解释器的状态(全局变量、栈、等等)储存在一个动态分配的 lua_State 结构类型中。一个指向这个状态的指针必须作为库中每一个函数的第一个参数,除了 lua_open 这个函数。该函数从最开始创建一个Lua状态。&在调用任何API函数之前,你必须通过调用 lua_open 创建一个状态:&& & lua_State lua_open (void);调用 lua_close 去释放这个由 lua_open 创建的状态:&& & void lua_close (lua_State L);这个函数销毁所有被给予Lua状态的对象(调用相应的垃圾收集元方法)并且释放那个状态使用的所有动态内存。在个别的平台上,你或许不需要调用这个函数,因为当宿主程序结束的时候会自然的释放所有的资源。另一方面,长时间运行的程序,像一些守护进程或者Web服务器,可能需要立即释放那些不需要的状态资源,以避免占用太多内存。&3.2 - 堆栈和索引Lua使用一个来自于C语言的 虚拟栈(virtual stack) 传递值。栈里面的每一个元素都代表一个Lua值 (nil, number, string, etc.)。&只要Lua调用C语言函数,这个所调用的函数将得到一个新的栈,这个栈将独立于先前的栈以及那些仍然活跃的C函数的栈。这个栈最初包含了C函数的所有参数,并且这也会存放C函数的返回值(见 3.16)。&为了方便起见,大多数查询操作的API不需要遵守一个严格的栈定义(注:即不需要遵循FILO)。他们可以使用 索引(index) 引用任何栈中元素:一个正数索引代表了栈中的绝对位置(从1开始);一个负数索引代表了从栈顶的偏移量。更特别的是,如果栈有 n 个元素,那么索引 1 代表第一个元素(这就是说,这个元素首先入栈)并且索引 n 代表了最后一个元素;索引 -1 也代表了最后一个元素(也就是栈顶)并且索引 -n 代表了第一个元素。我们说一个索引存在于 1 和栈顶之间是有效的,换句话说,如果 1 &= abs(index) &= top。&在任何时间里,你可以调用 lua_gettop 得到栈顶元素的索引:&& & int lua_gettop (lua_State L);因为索引从 1 开始,lua_gettop 的结果等于栈中的元素数量(如果是0就意味着栈为空)。&当你与Lua API交互的时候,你有责任控制堆栈以避免溢出。。这个函数&& & int lua_checkstack (lua_State L, int extra);使栈的大小增长为 top + extra 个元素;如果无法将栈增加到那个大小将返回false。这个函数从不对栈进行收缩;如果栈已经比新的大小更大,它将不产生任何作用那个。&只要Lua调用C 函数,它必须至少保证 LUA_MINSTACK 这个栈中的位置是可用的。LUA_MINSTACK 定义在 lua.h 中,它的值是 20,所以你不需要总担心栈空间除非你的代码通过循环将元素压入栈。&大多数插叙函数接受指向有效栈空间的索引,那就是说,索引达到栈空间的最大值是你需要使用 lua_checkstack。这样的索引称为可接受索引(acceptable indices)。更正规的说法,我们给出一个严格的定义如下:&& (index & 0 && abs(index) &= top) || (index & 0 && index &= stackspace)注意,0永远不是一个可接受索引。&除非另外说明,任何函数接受有效索引可以被称为是 伪索引(pseudo-indices),这些索引代表一些Lua值可以被C 代码访问但是却不存在于栈中。假索引通常用于访问全局环境变量,注册表,和一个C 函数的上值(见 3.17)。&3.3 - 堆栈操作一下的API提供了基本的栈操作:&& & void lua_settop & (lua_State L, int index);& & void lua_pushvalue (lua_State L, int index);& & void lua_remove & (lua_State L, int index);& & void lua_insert & (lua_State L, int index);& & void lua_replace & (lua_State L, int index);lua_settop 接受任何可接受的索引,或者0,并且将该索引设置为栈顶。如果新的栈顶比旧的更大,那么新元素被填上 nil 值。如果索引为 0,那么所有栈元素会被清除。在 lua.h 里面定义了一个有用的宏&& & #define lua_pop(L,n) & lua_settop(L, -(n)-1)用以从栈中弹出 n 个元素。&lua_pushvalue 将一个索引指向的元素的拷贝压入栈。 lua_remove 删除指定位置的元素,将该元素上方的所有元素下移以填满空缺。lua_insert 将栈顶元素移动到指定位置,将该位置以上的元素上移。lua_replace 将栈顶元素移动到指定位置而不移动其他任何其他元素(因此替代了给定位置的元素的值)。所有这些函数只接受有效的索引。(你不能使用伪索引调用 lua_remove 或 lua_insert,因为他们不代表栈中的位置。)&举个例子,如果栈开始于 10 20 30 40 50(自底向上;`′ 标记了栈顶),那么:&& & lua_pushvalue(L, 3) & --& 10 20 30 40 50 30& & lua_pushvalue(L, -1) & --& 10 20 30 40 50 30 30& & lua_remove(L, -3) & & --& 10 20 30 40 30 30& & lua_remove(L, 6) & & --& 10 20 30 40 30& & lua_insert(L, 1) & & --& 30 10 20 30 40& & lua_insert(L, -1) & & --& 30 10 20 30 40 (no effect)& & lua_replace(L, 2) & & --& 30 40 20 30& & lua_settop(L, -3) & & --& 30 40& & lua_settop(L, 6) & & --& 30 40 nil nil nil nil3.4 - 堆栈查询下面的函数可以用来检测栈内元素的类型:&& & int lua_type & & & & (lua_State L, int index);& & int lua_isnil & & & (lua_State L, int index);& & int lua_isboolean & & (lua_State L, int index);& & int lua_isnumber & & (lua_State L, int index);& & int lua_isstring & & (lua_State L, int index);& & int lua_istable & & & (lua_State L, int index);& & int lua_isfunction & & (lua_State L, int index);& & int lua_iscfunction & (lua_State L, int index);& & int lua_isuserdata & & (lua_State L, int index);& & int lua_islightuserdata (lua_State L, int index);这些函数只能使用可接受的索引。&lua_type 返回栈中元素值的类型,如果所有索引无效则返回 LUA_TNONE(就是说如果栈为空)。这些lua_type 代表的返回值作为常量定义在 lua.h 中:LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_TLIGHTUSERDATA。下面的函数将这些常量转换成字符串:&& & const char lua_typename (lua_State L, int type);lua_is 函数返回 1 当对象与所给类型兼容的时候,其他情况返回 0。 lua_isboolean 是一个例外:它只针对布尔值时才会成功(否则将是无用的,因为任何值都是一个布尔值)。这些函数对于无效引用返回 0。 lua_isnumber 接受数字和用数字表示的字符串;lua_isstring 接受字符串和数字(见 2.2.1);lua_isfunction 接受Lua函数和C函数; lua_isuserdata 接受完整的和轻量的用户数据。要区分C 函数和Lua 函数,你可以使用 lua_iscfunction。要区分用户数据,你可以使用 lua_islightuserdata。要区分数字还是用数字表示的字符串,你可以使用 lua_type。&这些API还包含了用于比较栈中的两个值的操作:&& & int lua_equal & (lua_State L, int index1, int index2);& & int lua_rawequal (lua_State L, int index1, int index2);& & int lua_lessthan (lua_State L, int index1, int index2);lua_equal 和 lua_lessthan 在比较他们的副本的时候是等效的(见 2.5.2)。 lua_rawequal 用于比较基本类型但不包括元方法。如果有任何形式的无效索引,这些函数都返回 0(false)。&3.5 - 堆栈取值为了将一个栈中的值转变为指定的C语言类型,你需要使用以下的转换函数:&& & int & & & & lua_toboolean & (lua_State L, int index);& & lua_Number & lua_tonumber & (lua_State L, int index);& & const char & lua_tostring & (lua_State L, int index);& & size_t & & & lua_strlen & & (lua_State L, int index);& & lua_CFunction lua_tocfunction (lua_State L, int index);& & void & & & lua_touserdata (lua_State L, int index);& & lua_State & lua_tothread & (lua_State L, int index);& & void & & & lua_topointer & (lua_State L, int index);这些函数由任何可接受索引作为参数进行调用。当遇到一个无效索引,函数表现为就好像接受了一个错误类型的值。&lua_toboolean 将索引指向的Lua值转换为C语言类型的布尔值(0 或 1)。就像所有Lua中的测试一样,任何不等于 false 或者 nil 的Lua值通过 lua_toboolean 都将返回 1;否则将返回 0。当然,如果是一个无效索引,也将返回 0。(如果你只想接受真实的布尔值,使用 lua_isboolean 去测试值的类型。)&lua_tonumber 将索引指向的Lua值转换成一个数字(默认情况下,lua_Number 是 double类型)。Lua值必须是一个数字或者可转化为数字的字符串(见 2.2.1);否则,lua_tonumber 返回 0。&lua_tostring 将索引指向的Lua值转换成字符串(const char)。Lua值必须是一个字符串或者数字;否则,函数返回 NULL。如果值是一个数字,lua_tostring 会将栈中的真实值变成一个字符串类型。(当 lua_tostring 应用于键时这个改变将引起 lua_next 的混乱。)lua_tostring 在Lua 状态内部返回一个字符串的指针。这个字符串总是以 0('/0')结尾,就像C 语言里的一样,但是也可能包含其他 0 在其中。如果你不知道一个字符串中是否存在 0 ,你可以使用 lua_strlen 得到它的实际长度。因为Lua具有垃圾收集机制,所以不能保证 lua_tostring 返回的指针仍然有效,当相应的值从栈中删除之后。如果你在当前函数返回之后还需要这个字符串,你需要复制它并且将它存入注册表(见 3.18)。&lua_tocfunction 将栈中的值转换为C 函数。这个值必须是一个C 函数;否则,lua_tocfunction 返回 NULL。类型 lua_CFunction 在 3.16 中有详细解释。&lua_tothread 将栈中的值转换为Lua线程(被描绘成 lua_State )。这个值必须是一个线程;否则;lua_tothread 返回 NULL。&lua_topointer 将栈中的值转换为通用的C 语言指针(void )。这个值可能是一个用户数据、表、线程、或者函数;否则,lua_topointer 返回 NULL。Lua保证同种类型的不同对象将返回不同指针。没有直接的方法将指针转换回原来的值。这个函数通常用于调试。&lua_touserdata 在 3.8 中有详细解释。&3.6 - 将值压入堆栈以下的API函数将C 语言值压入栈:&& & void lua_pushboolean & & (lua_State L, int b);& & void lua_pushnumber & & (lua_State L, lua_Number n);& & void lua_pushlstring & & (lua_State L, const char s, size_t len);& & void lua_pushstring & & (lua_State L, const char s);& & void lua_pushnil & & & (lua_State L);& & void lua_pushcfunction & (lua_State L, lua_CFunction f);& & void lua_pushlightuserdata (lua_State L, void p);这些函数接受一个C 语言值,将其转换成相应的Lua 值,并且将结果压入栈。需要特别注意的是,lua_pushlstring 和 lua_pushstring 将对所给的字符串做一个内部拷贝。lua_pushstring 只能压入合适的C 语言字符串(也就是说,字符串要以 '/0' 结尾,并且不能包含内嵌的 0);否则,你需要使用更通用的 lua_pushlstring 函数,它可以接受一个指定的大小。&你可以压入“格式化的”字符串:&& & const char lua_pushfstring (lua_State L, const char fmt, ...);& & const char lua_pushvfstring (lua_State L, const char fmt, va_list argp);这些函数将格式化的

我要回帖

更多关于 c 获取lua数组 的文章

 

随机推荐