Lua1
使用lua进行脚本编程有很多优点:
1 代码体积小
2 执行速度快
3 安全性较高等
4 但是最大的优点是修改后的代码不需要重新编译即可生效,而高级语言的代码经过修改后需要经过重新编译或者解释后才能生效。
lua主要用于游戏应用层的开发。由于游戏上层的逻辑会而经常发生变化,比如游戏的任务系统或者活动系统。
我们可以设想一下,如果每次都要修改我们所用的C#代码重新编译,那么玩家根本就无法进行游戏。
lua和python则是两个常用的脚本语言,lua相对于python而言,lua比较轻量级罢了
1. 脚本在手游中是类于“大脑”的功能,所有游戏相关的逻辑代码一般都放在脚本中,而客户端(前台)的代码都则属于“肢体”,也可以说是“播放器”,
作用只是用户展示出UI界面的功能;那么脚本的作用那么不仅仅如此,比如地图数据等都可以利用脚本使用;
2. 脚本在手机网游中的作用尤为重要,比如一款网游“Himi”没有使用脚本,如果“Himi”1.0版本在发布后突然发现客户端出现一些棘手的bug需要修复,
那么你想修改那么也要等待再次更新客户端重新提交发布才可以解决,这样会流失一大批用户,而且游戏每次更新也会流失掉部分用户,这是肯定的;
但是如果“Himi”这款网游使用脚本的话,那么解决此类问题很eazy,比如我在“Himi”游戏中的逻辑代码都放在脚本a.lua 中,那么如果a.lua逻辑中哪里出现了问题,
我们直接可以将修复后的a.lua脚本更新至服务器中,因为一般脚本都会定义version号,比如a.lua有bug的version:1.0,那么我们修复后的a.lua version改成1.1,
当用户每次启动游戏的时候,客户端都会将脚本的version与服务器脚本version做对比,当server端脚本version号比当前脚本新,那么自动下载并覆盖当前脚本,OK,问题解决;
不仅仅如此,比如游戏中做个活动呀,换个图片呀等等都可以即使更新,而不是每次修改前端代码都要重新发布新的游戏版本,造成一些损失!
lua代码都是运行时才编译的,不运行的时候就如同一张图片、一段音频一样,都是文件;所以更新逻辑只需要更新脚本,不需要再编译,因而Lua能轻松实现“热更新”。
Ulua是一款非常实用的unity插件,它能让unity支持Lua语言,而且运行效率还不错。
在Lua中,一切都是变量,除了关键字。
lua基础知识
1、and or not 逻辑运算符
逻辑运算符认为false 和nil 是假(false),其他为真,0 也是true.
and 和or 的运算结果不是true 和false,而是和它的两个操作数相关。
Lua中的and和or都使用“短路原则”。
a and b -- 如果a 为false,则返回a,否则返回b
a or b -- 如果a 为true,则返回a,否则返回b
例如:
print( and ) --> 5
print(nil and ) --> nil
print(false and ) --> false
print( or ) --> 4
print(false or ) --> 5
--一个很实用的技巧:如果x 为false 或者nil 则给x 赋初始值v
x = x or v
等价于
if not x then --not作用:判断某个值是否为true,【if not x : 如果x不为true】
x = v
end
and的优先级比or高。 --C 语言中的三元运算符
--a ? b : c
--在Lua中可以这样实现:
(a and b) or c --但是这里其实有个问题,就是当b是false或者nil时无论a是什么,最后结果都会返回c的值,用这个(a and {b} or {c})[1]
--not 的结果只返回false 或者true
print(not nil) --> true --not为否定,nil为否定,两两否定为肯定(true)
print(not false) --> true --not为否定,false为否定,两两否定为肯定(true)
print(not ) --> false --not为否定,0为肯定,一肯一否为否定
print(not not nil) --> false --三个否定为否定
2、注释
单行注释中,连续两个减号"--"表示注释的开始
多行注释中,由"--[["表示注释开始,并且一直延续到"]]"为止。
3、条件语句
另外、不要在for的循环体内修改变量i的值,否则会导致不可预知的结果。
for的另一种用法,是用来遍历迭代器
function abc( arg )
local i =
local function cde( )
i = i +
return i,arg[i]
end
return cde
end for k,v in abc{'a','b','c'} do
if v==nil then
break
end
print('key = '..k..',value='..v)
end
--key=1,value=a
--key=2,value=b
--key=3,value=c
foreach
for k, v in ipairs(a) do --ipairs是Lua自带的系统函数,返回遍历数组的迭代器。k表示是数组a的key,v表示的是数组a的value,不能有第三个变量
print(v)
end for k in pairs(t) do --打印table t中的所有key。
print(k)
end
见如下示例代码:
days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }
revDays = {}
for k, v in ipairs(days) do
revDays[k] = v
end for k in pairs(revDays) do
print(k .. " = " .. revDays[k])
end ----输出结果为:
--1 = Sunday = 7
--2 = Monday = 3
--3 = Tuesday = 4
--4 = Wednesday = 6
--5 = Thursday = 1
--6 = Friday= 5
--7 = Saturday= 2
4、语句块
语句块在C中是用"{"和"}"括起来的,在Lua中,它是用do 和 end 括起来的。比如:
do print("Hello") end
可以在 函数 中和 语句块 中定局部变量。
5、赋值语句
Lua中的赋值语句和其它编程语言基本相同,唯一的差别是Lua支持“多重赋值”,如:a, b = 10, 2 * x,其等价于a = 10; b = 2 * x。
然而需要说明的是,Lua在赋值之前需要先计算等号右边的表达式,在每一个表达式都得到结果之后再进行赋值。
因此,我们可以这样写变量交互:x,y = y,x。如果等号右侧的表达式数量少于左侧变量的数量,Lua会将左侧多出的变量的值置为nil,如果相反,Lua将忽略右侧多出的表达式。
6、数值运算
和C语言一样,支持 +, -, *, /。但Lua还多了一个"^"。这表示指数乘方运算。比如2^3 结果为8, 2^4结果为16。
连接两个字符串,可以用".."运处符。如:"This a " .. "string." -- 等于 "this a string"
7、比较运算
所有这些操作符总是返回true或false。
操作符==用于相等性测试,操作符~=用于不等性测试。这两个操作符可以应用于任意两个值。如果两个值的类型不同,Lua就认为他们不等。nil值与其自身相等。
对于字符串的比较,Lua是按照字符次序比较的。
a="";
print(a=b); --false
print(c==b); --true
对于Table,Function和Userdata类型的数据,只有 == 和 ~=可以用。相等表示两个变量引用的是同一个数据。比如:
a={,}
b=a
print(a==b, a~=b) --输出 true, false
a={,}
b={,}
print(a==b, a~=b) --输出 false, true
8、变量类型
> print(type("hello world"))
string
> print(type(10.4))
number
> print(type(print))
function
> print(type(true))
boolean
> print(type(nil))
nil
> print(type(type(X)))
string
> print(type({,,}))
table
9、 变量的定义
在Lua中还有一个特殊的规则,即以下划线(_)开头,后面紧随多个大写字母(_VERSION),这些变量一般被Lua保留并用于特殊用途,因此我们在声明变量时需要尽量避免这样的声明方式,以免给后期的维护带来不必要的麻烦。
Lua是大小写敏感的,因此对于一些Lua保留关键字的使用要特别小心,如and。但是And和AND则不是Lua的保留字。
在Lua中,不管在什么地方使用变量,都不需要声明,并且所有的这些变量总是全局变量,除非我们在前面加上"local"。这一点要特别注意,因为我们可能想在函数里使用局部变量,却忘了用local来说明。
和其它编程语言一样,如果有可能尽量使用局部变量,以免造成全局环境的变量名污染。同时由于局部变量的有效期更短,这样垃圾收集器可以及时对其进行清理,从而得到更多的可用内存。
不同lua文件之间的require的情况:如果a文件require了b文件,那么b文件里的全局变量在a文件里是能访问到的,而局部变量是访问不到的!
function fun1
a=;
end function fun2
print("a's value is "..a) -- a's value is 10
end
function fun1
local a=;
end function fun2
print("a's value is "..a) -- 报错:attempt to concatenate global 'a' (a nil value)
end
(1)Nil
正如前面所说的,没有使用过的变量的值,都是Nil。有时候我们也需要将一个变量清除,这时候,我们可以直接给变量赋以nil值。
将nil赋予一个全局变量等同于删除它。如:
var1=nil -- 请注意 nil 一定要小写
另外还可以像全局变量一样,将nil赋予table的某个元素(key)来删除该元素。
(2)Boolean
布尔值通常是用在进行条件判断的时候。布尔值有两种:true 和 false。在Lua中,只有false和nil才被计算为false,而所有任何其它类型的值,都是true。比如0,空串等等,都是true。
0在Lua中的的确确是true。你也可以直接给一个变量赋以Boolean类型的值,如:
theBoolean = true
(3)Number
Lua中的number用于表示实数。Lua中没有专门的类型表示整数。
(4)String
字符串,总是一种非常常用的高级类型。在Lua中,我们可以非常方便的定义很长很长的字符串。
字符串在Lua中有几种方法来表示,最通用的方法,是用双引号或单引号来括起一个字符串的,如:
"That's go!"
或
'Hello world!'
和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中,可以用另一种表示方法:用"[["和"]]"将多行的字符串括起来。(lua5.1: 中括号中间可以加入若干个"="号,如 [==[ ... ]==],详见下面示例)
示例:下面的语句所表示的是完全相同的字符串:
a = 'alo\n123\''
a = "alo\n123\"" a = [[alo
123']] a = [==[
alo
123']==]
值得注意的是,在这种字符串中,如果含有单独使用的"[["或"]]"就仍然得用"\["或"\]"来避免歧义。当然,这种情况是极少会发生的。
在Lua中还可以通过[[ all strings ]]的方式来禁用[[ ]]中转义字符,如:
page = [[abc\n123]] --abc\n123
如果两个方括号中包含这样的内容:a = b[c[i]],这样将会导致Lua的误解析,[[a = b[c[i]]]],lua会认为倒数第二个]]为结束标志,因此在这种情况下,我们可以将其改为[===[ 和 ]===]的形式,从而避免了误解析的发生。
(5)Table
A、 如果键是int类型的,那么键只能为这种格式的:[1]或者['1']
B、 如果键是string类型的,那么键只能为这种格式的:['name']或者name
关系表类型,这是一个很强大的类型。我们可以把这个类型看作是一个数组。只是C#语言的数组,只能用正整数来作索引;在Lua中,你可以用任意类型来作数组的索引,除了nil。
同样,在C#语言中,数组的内容只允许一种类型;在Lua中,你也可以用任意类型的值来作数组的内容,除了nil。
Table的定义很简单,它的主要特征是用"{"和"}"来括起一系列数据元素的。比如:
T1 = {} -- 定义一个空表
T1[1]=10 --然后我们就可以象C#语言一样来使用它了。
T1["John"]={Age=, Gender="Male"}
--这一句相当于:
T1["John"]={} -- 必须先定义成一个表,还记得未定义的变量是nil类型吗
T1["John"]["Age"]=
T1["John"]["Gender"]="Male"
--当表的索引是字符串的时候,我们可以简写成:
T1.John={}
T1.John.Age=
T1.John.Gender="Male"
--或
T1.John = {Age=, Gender="Male"}
在定义表的时候,我们可以把所有的数据内容一起写在"{"和"}"之间,这样子是非常方便,而且很好看。比如,前面的T1的定义,我们可以这么写:
T1=
{
, -- 相当于 [1] = 10 ,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编
[] = , --相当于指定索引为100对应的值为40
John= -- 如果你原意,你还可以写成:["John"] =
{
Age=, -- 如果你原意,你还可以写成:["Age"] =27
Gender=Male -- 如果你原意,你还可以写成:["Gender"] =Male
},
-- 相当于 [2] = 20
}
看起来很漂亮,不是吗?我们在写的时候,需要注意三点:
第一,所有元素之间,总是用逗号","隔开;
第二,所有索引值都需要用["和"]括起来;如果是字符串,还可以去掉引号和中括号;
第三,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编
表类型的构造是如此的方便,以致于常常被人用来代替配置文件。是的,不用怀疑,它比ini文件要漂亮,并且强大的多。
对于table的构造器,还有两个需要了解的语法规则,如:
a = { [1] = "red", [2] = "green", [3] = "blue", }
这里需要注意最后一个元素的后面仍然可以保留逗号(,)
table可视为数组,数组下标不同于其他大多数语言,其下标从1
开始。
(6)Function
函数,在Lua中,函数的定义也很简单。典型的定义如下:
function add(a,b) -- add 是函数名字,a和b是参数名字
return a+b -- return 用来返回函数的运行结果
end
还记得前面说过,函数也是变量类型吗?上面的函数定义,其实相当于:
add = function (a,b) return a+b end
当重新给add赋值时,它就不再表示这个函数了。我们甚至可以赋给add任意数据,包括nil (这样,赋值为nil,将会把该变量清除)。
Lua的函数可以接受可变参数个数,它同样是用"..."来定义的,比如:
function sum (a,b,...)
如果想取得...所代表的参数,可以在函数中访问arg局部变量(表类型)得到 (lua5.1: 取消arg,并直接用"..."来代表可变参数了,本质还是arg)。
如 sum(1,2,3,4),则在函数中,a = 1, b = 2, arg = {3, 4} (lua5.1: a = 1, b = 2, ... = {3, 4})
在含有变长参数的函数中,同样可以带有固定参数,但是固定参数一定要在变长参数之前声明,如:
function test(arg1,arg2,...)
...
end
关于Lua的变长参数最后需要说明的是,由于变长参数中可能包含nil值,因此在使用类似获取table元素数量(#)的方式获取变参的数量就会出现问题。如果要想始终获得正确的参数数量,可以使用Lua提供的select函数,如:
function Start()
MyTest(,,,a,b);
end function MyTest(...)
print(select('#',...)) -- 5 , 这里'#'值表示让select返回变参的数量(其中包括nil)。
for i = , select('#',...) do
local arg = select(i, ...) --这里的i表示获取第i个变参,1为第一个。
print(arg);
end
end --输出结果
--5 =
--
--
--
--nil
--nil
更可贵的是,它可以同时返回多个结果,比如:
function s()
return ,,,
end
a,b,c,d = s() -- 此时,a = 1, b = 2, c = 3, d = 4
function maximum(a)
local mi =
local m = a[mi]
for i, val in ipairs(a) do
if val > m then
mi,m = i,val
end
end
return m,mi
end
print(maximum{,,,,})
-- 输出结果为:
-- 23
-- 233
function abcwyq( ... )
-- body
return ,,
end print(abcwyq())
--输出结果为
--
--11 22
--11 22 33
前面说过,表类型可以拥有任意类型的值,包括函数!因此,有一个很强大的特性是,拥有函数的表,哦,我想更恰当的应该说是对象吧。Lua可以使用面向对象编程了。不信?举例如下:
t =
{
Age = 27,
add = function(self, n) self.Age = self.Age+n end
}
print(t.Age) --
t.add(t, )
print(t.Age) --
不过,t.add(t,10) 这一句实在是有点土对吧?没关系,在Lua中,我们可以简写成:
t:add() -- 相当于 t.add(t,10)
1、在Lua中函数的调用方式和C语言基本相同,如:print("Hello World")和a = add(x, y)。唯一的差别是,如果函数只有一个参数,并且该参数的类型为字符串常量或table的构造器,那么圆括号可以省略,如print "Hello World"和f {x = 20, y = 20}。
2、Lua为面对对象式的调用也提供了一种特殊的语法--冒号操作符。表达式o.foo(o,x)的另一种写法是o:foo(x)。冒号操作符使调用o.foo时将o隐含的作为函数的第一个参数。
3、需要说明的是,Lua中实参和形参的数量可以不一致,一旦出现这种情况,Lua的处理规则等同于多重赋值,即实参多于形参,多出的部分被忽略,如果相反,没有被初始化的形参的缺省值为nil。
4、 Lua会调整一个函数的返回值数量以适应不同的调用情况。若将函数调用作为一条单独语句时,Lua会丢弃函数的所有返回值。若将函数作为表达式的一部分来调用时,Lua只保留函数的第一个返回值。
只有当一个函数调用是一系列表达式中的最后一个元素时,才能获得所有返回值。这里先给出三个样例函数,如:
function foo0() end
function foo1() return "a" end
function foo2() return "a","b" end
最后一个需要介绍的是Lua中unpack函数,该函数将接收数组作为参数,并从下标1开始返回该数组(之所以不称之为table,因为说成数组,更好理解,数组的特性就是从1开始,并以1为增量排序的集合)的所有元素。如:
> lua
> print(unpack{,,}) > a,b = unpack{,,}
> print(a,b) > string.find(unpack{"hello","ll"}) --等同于string.find("hello","ll"),find函数的作用是寻找第二个参数ll在第一个参数中的索引位置
在Lua中unpack函数是用C语言实现的。为了便于理解,下面给出在Lua中通过递归实现一样的效果,如:
function unpack(t,i)
i = i or
if t[i] then
return t[i], unpack(t,i + )
end
end
具名实参:
在函数调用时,Lua的传参规则和C语言相同,并不真正支持具名实参。但是我们可以通过table来模拟,比如:
function rename1(old,new)
...
end
这里我们可以让上面的rename函数只接收一个参数,即table类型的参数,与此同时,该table对象将含有old和new两个key。如:
function rename2(arg)
local old = arg.old
local new = arg.new
...
end
这种修改方式有些类似于JavaBean,即将多个参数合并为一个JavaBean。
在Lua中函数和所有其它值一样都是匿名的,即它们都没有名称。在使用时都是操作持有该函数的变量,如:
a = { p = print }
a.p("Hello World")
b = print
b("Hello World")
在声明Lua函数时,可以直接给出所谓的函数名,如:
function foo(x) return 2 * x end
我们同样可以使用下面这种更为简化的方式声明Lua中的函数,如:
foo = function(x) return 2 * x end
我们将这种函数构造式的结果称为一个"匿名函数"。下面的示例显示了匿名函数的方便性,它的使用方式有些类似于Java中的匿名类,如:
table.sort(test_table,function(a,b) return (a.name > b.name) end)
test_table={{age=},{age=},{age=},{age=},{age=}}
table.sort(test_table,function(a,b) return (a.age > b.age) end)
for i=, do
print(test_table[i].age)
end
end --输出结果
--
--
--
--
--
1. closure(闭合函数):
若将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,见如下示例:
function newCounter()
local i =
return function() --匿名函数
i = i +
return i
end
end
c1 = newCounter()
print("The return value of first call is " .. c1())
print("The return value of second call is " .. c1())
--输出结果为:
--The return value of first call is 1
--The return value of second call is 2
在上面的示例中,我们将newCounter()函数称为闭包函数。其函数体内的局部变量i被称为"非局部变量",和普通局部变量不同的是该变量被newCounter函数体内的匿名函数访问并操作。
再有就是在函数newCounter返回后,其值仍然被保留并可用于下一次计算。再看一下下面的调用方式。
function newCounter()
local i =
return function() --匿名函数
i = i +
return i
end
end
c1 = newCounter()
c2 = newCounter()
print("The return value of first call with c1 is " .. c1())
print("The return value of first call with c2 is " .. c2())
print("The return value of second call with c1 is " .. c1())
--输出结果为:
--The return value of first call with c1 is 1
--The return value of first call with c2 is 1
--The return value of second call with c1 is 2
由此可以推出,Lua每次在给新的闭包变量赋值时,都会让不同的闭包变量拥有独立的"非局部变量"。下面的示例将给出基于闭包的更为通用性的用法:
do
--这里将原有的文件打开函数赋值给"私有变量"oldOpen,该变量在块外无法访问。
local oldOpen = io.open
--新增一个匿名函数,用于判断本次文件打开操作的合法性。
local access_OK = function(filename,mode) <检查访问权限> end
--将原有的io.open函数变量指向新的函数,同时在新函数中调用老函数以完成真正的打开操作。
io.open = function(filename,mode)
if access_OK(filename,mode) then
return oldOpen(filename,mode)
else
return nil,"Access denied"
end
end
end
非全局函数:
从上一小节中可以看出,Lua中的函数不仅可以直接赋值给全局变量,同时也可以赋值给其他类型的变量,如局部变量和table中的字段等。
事实上,Lua库中大多数table都带有函数,如io.read、math.sin等。这种写法有些类似于C++中的结构体。如:
Lib = {}
Lib.add = function(x,y) return x + y end
Lib.sub = function(x,y) return x - y end
或者是在table的构造式中直接初始化,如:
Lib = { add = function(x,y) return x + y end,
sub = function(x,y) return x - y end
}
除此之外,Lua还提供另外一种语法来定义此类函数,如:
Lib = {}
function Lib.add(x,y) return x + y end
function Lib.sub(x,y) return x - y end
对于Lua中的局部函数,其语义在理解上也是非常简单的。由于Lua中都是以程序块作为执行单元,因此程序块内的局部函数在程序块外是无法访问的,如:
do
local f = function(x,y) return x + y end
--do something with f.
f(,)
end
对于这种局部函数,Lua还提供另外一种更为简洁的定义方式,如:
local function f(x,y) return x + y end
该写法等价于:
local f
f = function(x,y) return x + y end
正确的尾调用:
在Lua中支持这样一种函数调用的优化,即“尾调用消除”。我们可以将这种函数调用方式视为goto语句,如:
function f(x) return g(x) end
由于g(x)函数是f(x)函数的最后一条语句,在函数g返回之后,f()函数将没有任何指令需要被执行,因此在函数g()返回时,可以直接返回到f()函数的调用点。
由此可见,Lua解释器一旦发现g()函数是f()函数的尾调用,那么在调用g()时将不会产生因函数调用而引起的栈开销。这里需要强调的是,尾调用函数一定是其调用函数的最后一条语句,否则Lua不会进行优化。
然而事实上,我们在很多看似是尾调用的场景中,实际上并不是真正的尾调用,如:
function f(x) g(x) end --没有return语句的明确提示
function f(x) return g(x) + 1 --在g()函数返回之后仍需执行一次加一的指令。
function f(x) return x or g(x) --如果g()函数返回多个值,该操作会强制要求g()函数只返回一个值。
function f(x) return (g(x)) --原因同上。
在Lua中,只有"return <func>(<args>)"形式才是标准的尾调用,至于参数中(args)是否包含表达式,由于表达式的执行是在函数调用之前完成的,因此不会影响该函数成为尾调用函数。
(7)Userdata 和 Thread
10、字符串
(1)"#"标识符,该标识符在字符串变量的前面将返回其后字符串的长度
a = "hello"
print(#a) --
(2)Lua提供了运行时的数字与字符串的自动转换。如:
> print("10" + 1)
11
> print("10" + "1")
11
> print(10 + 1)
11
> print("10 + 1")
10 + 1
如果在实际编程中,不希望两个数字字符串被自动转换,而是实现字符串之间的连接,可以通过" .. "操作符来完成。如:
> print(10 .. 20)
1020
注意..和两边的数字之间必须留有空格,否则就会被Lua误解析为小数点儿。
尽管Lua提供了这种自动转换的功能,为了避免一些不可预测的行为发生,特别是因为Lua版本升级而导致的行为不一致现象。
鉴于此,还是应该尽可能使用显示的转换,如字符串转数字的函数tonumber(),或者是数字转字符串的函数tostring()。对于前者,如果函数参数不能转换为数字,该函数返回nil。如:
line = io.read()
n = tonumber(line)
if n == nil then
error(line .. " is not a valid number")
else
print(n * )
end
(3)由于数组实际上仍为一个table,所以对于数组大小的计算需要留意某些特殊的场景,如:
a = {}
a[] =
在上面的示例中,数组a中索引值为1--999的元素的值均为nil。而Lua则将nil作为界定数据结尾的标志。当一个数组含有“空隙”时,即中间含有nil值,长度操作符#会认为这些nil元素就是结尾标志。
当然这肯定不是我们想要的结果。因此对于这些含有“空隙”的数组,我们可以通过函数table.maxn()返回table的最大正数索引值。如:
a = {}
a[]=;
a["sss"]=;
a[]=;
a[]=;
print(table.maxn(a)) --
print(#a) --
table.maxn()是返回table的最大正数索引值
一般来说#是获得一个table的长度(即元素数),但这个操作符实际上陷阱很多,#仅对键值为连续的数值才有效,并且键值必须从1开始!
如果你的table是纯粹当一个连续的数组在用,那么#t是很方便的获得table长度的方法;但如果你的table中key的数值不连续,或者有其他类型的key那么还是不要指望#能给出多有意义的结果来……
连续的数组指的就是往一个空的table中插入数据,由此table自动分配键值,由1开始,递增量为1
类似这样:t = {"a", "b", "c"},只有这种情况用#运算符才最保险!不过#运算符主要用于计算字符串的长度
table遍历:
for key, value in pairs(tbtest) do
XXX
end
这样的遍历顺序并非是tbtest中table的排列顺序,而是根据tbtest中key的hash值排列的顺序来遍历的。
如果不需要保证遍历的顺序,就用pairs
pairs也可以用来遍历连续的数组(之所以不称之为table,因为说成数组,更好理解,数组的特性就是从1开始,并以1为增量排序的集合),会按顺序输出
也可以遍历不是以1开始,也不是按1递增的连续的key的table,也会按顺序输出的,例如:
a = {[] = "red1",[] = "red2",[] = "red3",[] = "red4"}
for k,v in pairs(a) do
print(v) -- red4 red2 red1 red3
end
for key, value in ipairs(tbtest) do
XXX
end
这样的循环必须要求tbtest中的key为顺序的,而且必须是从1开始,ipairs只会从1开始按连续的key顺序遍历到key不连续为止。
如果需要保证遍历的顺序,就用ipairs,前提是table是一个连续的数组
for i=, #(tbtest) do
XXX
end
这种遍历,只能遍历连续的数组(键从1开始,以增量为1递增的键的table),不然遍历会出现意想不到的结果!
for i=, table.maxn(tbtest) do
XXX
end
这种效率太低了,不建议用于遍历table
参考:http://www.cnblogs.com/ly4cn/archive/2006/08/04/467550.html
http://www.cnblogs.com/stephen-liu74/archive/2012/06/15/2409324.html
Lua1的更多相关文章
- 【转】封装Lua for C#
原文:http://blog.csdn.net/rcfalcon/article/details/5583095 为了搞懂LUA在我们的GDEX中到底怎么用,我决定研究一下如何比较好的在WPF里封装一 ...
- 《The Evolution of Lua》翻译part 2
Lua2 1990年的时候,面向对象迈向巅峰,对于Lua没有面向对象的支持,我们受到了很大的压力.我们不想将Lua变成面向对象,因为我们不想“修复”一种编程范式(fix a programming p ...
- 《The Evolution of Lua》读书笔记 1
lua的优点: 可移植性 容易嵌入 体积小 高效率 这些优点都来自于lua的设计目标:简洁.从Scheme获得了很多灵感,包括匿名函数,合理的语义域概念 lua前身: 巴西被禁运,引入计算机软件和 ...
- lua学习之table类型
关系表类型,这是一个很强大的类型.我们可以把这个类型看作是一个数组.只是 C语言的数组,只能用正整数来作索引: 在Lua中,你可以用任意类型的值来作数组的索引,但这个值不能是 nil.同样,在C语言中 ...
- NGINX + LUA实现复杂的控制 --源自http://outofmemory.cn/code-snippet/14396/nginx-and-lua
安装lua_nginx_module 模块 lua_nginx_module 可以一步步的安装,也可以直接用淘宝的OpenResty Centos和debian的安装就简单了.. 这里说下freebs ...
- lua 和 c
lua程序其实本身并不能执行,它必须依靠c语言编写的解释器来解释执行,或者说解释器为lua脚本的执行,提供了一个运行环境(lua_state),其中包括函数堆栈,内存分配和回收等机制. 理论上,lua ...
- 为cocos2d-x项目增加Lua支持
开始为游戏增加Lua脚本支持,今天主要配置了一下开发环境:cocos2d-x 2.2.1,xcode5. 1. 创建cocos2d-x-lua项目 类似于创建C++项目,用以下命令即可: python ...
- Lua学习系列(一)
从现在开始,打算学习一门新的脚本语言-lua. 1.什么是lua? a) lua1 • Lua 1.0 was implemented as a library, in less then 6000 ...
- Lua与.net的CLR相互调用
工程环境搭建: 下载luainterface-1.5.3.zip文件,使用到的dll为Built目录下的LuaInterface.dll.lua51.dll.luanet.dll LuaInterfa ...
随机推荐
- hdu-3015 Disharmony Trees---离散化+两个树状数组
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3015 题目大意: 有一些树,这些树的高度和位置给出.现在高度和位置都按从小到大排序,对应一个新的ra ...
- Shell编程学习之Shell编程基础(一)
这篇随笔将要介绍关于Shell编程的基本知识,这些将会在假设你已经熟悉了Linux系统和命令行的基本知识. 构建基本脚本 你应该了解或熟悉使用Shell命令行了,但是只是使用Shell命令行的命令,有 ...
- ASP.NET中无刷新分页
上次介绍了我们代码写的刷新分页,这次就来说说无刷新分页. 这次我们是在上次的基础上改动了一些,我们都知道想要无刷新,就需要Ajax,在我们的ASP.NET中AJax是和一般处理程序配合着用的. 无刷新 ...
- js对象动态添加属性,方法
1. 动态添加属性,方法 var object = new Object(); object.name = "name"; object.age = 19; >>> ...
- js获取url参数方法
function GetQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&] ...
- Python__for循环和列表生成式的区别
话不多,上例子 >>> L = [,,] >>> for i in range(len(L)): L[i] = L[i] + L[i-] print(L) #结果 ...
- Laravel操作上传文件的方法
1.获取上传的文件 $file=$request->file('file');2.获取上传文件的文件名(带后缀,如abc.png) $filename=$file->getClientOr ...
- python之doctest的用法
doctest是python自带的一个模块,你可以把它叫做“文档测试”(doctest)模块. doctest的使用有两种方式:一个是嵌入到python源中.另一个是放到一个独立文件. doctest ...
- 创建数据库配置文件ini(转)
一.有必要了解INI文件的结构: ;注释 [小节名] 关键字=值 ... ---- INI文件允许有多个小节,每个小节又允许有多个关键字, “=”后面是该关键字的值. ---- 值的类型有三种:字符串 ...
- <Docker学习>4. docker容器的使用
简单的说, 容器是独立运行的一个或一组应用, 以及它们的运行态环境. 对应的, 虚拟机可以理解为模拟运行的一整套操作系统( 提供了运行态环境和其他系统环境) 和跑在上面的应用.容器的运行是基于镜像的. ...