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(4 and 5) --> 5
print(nil and 13) --> nil
print(false and 13) --> false
print(4 or 5) --> 4
print(false or 5) --> 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 0) --> false  --not为否定,0为肯定,一肯一否为否定
print(not not nil) --> false  --三个否定为否定

2、注释

单行注释中,连续两个减号"--"表示注释的开始

多行注释中,由"--[["表示注释开始,并且一直延续到"]]"为止。

3、条件语句

另外、不要在for的循环体内修改变量i的值,否则会导致不可预知的结果。

for的另一种用法,是用来遍历迭代器

function abc( arg )
     local i = 0
     local function cde(  )
         i = i + 1
        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="123";
print(a=b);   --false
print(c==b); --true

对于Table,Function和Userdata类型的数据,只有 == 和 ~=可以用。相等表示两个变量引用的是同一个数据。比如:

a={1,2}
b=a
print(a==b, a~=b)  --输出 true, false
a={1,2}
b={1,2}
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({1,2,3}))
    table

9、 变量的定义

在Lua中还有一个特殊的规则,即以下划线(_)开头,后面紧随多个大写字母(_VERSION),这些变量一般被Lua保留并用于特殊用途,因此我们在声明变量时需要尽量避免这样的声明方式,以免给后期的维护带来不必要的麻烦。
Lua是大小写敏感的,因此对于一些Lua保留关键字的使用要特别小心,如and。但是And和AND则不是Lua的保留字。

在Lua中,不管在什么地方使用变量,都不需要声明,并且所有的这些变量总是全局变量,除非我们在前面加上"local"。这一点要特别注意,因为我们可能想在函数里使用局部变量,却忘了用local来说明。

和其它编程语言一样,如果有可能尽量使用局部变量,以免造成全局环境的变量名污染。同时由于局部变量的有效期更短,这样垃圾收集器可以及时对其进行清理,从而得到更多的可用内存。

不同lua文件之间的require的情况:如果a文件require了b文件,那么b文件里的全局变量在a文件里是能访问到的,而局部变量是访问不到的!

function fun1
  a=10;
end

function fun2
 print("a's value is "..a) -- a's value is 10
end
function fun1
  local a=10;
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=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 ,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编
      [100] = 40, --相当于指定索引为100对应的值为40 
      John=  -- 如果你原意,你还可以写成:["John"] =
      {
        Age=27,   -- 如果你原意,你还可以写成:["Age"] =27
        Gender=Male   -- 如果你原意,你还可以写成:["Gender"] =Male
      },
      20  -- 相当于 [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(2,4,6,a,b);
end

function MyTest(...)
print(select('#',...)) -- 5 , 这里'#'值表示让select返回变参的数量(其中包括nil)。
for i = 1, select('#',...) do 
     local arg = select(i, ...) --这里的i表示获取第i个变参,1为第一个。
     print(arg);
end
end

--输出结果
--5 =
--2
--4
--6
--nil
--nil

更可贵的是,它可以同时返回多个结果,比如:

function s()
return 1,2,3,4
end
a,b,c,d = s()  -- 此时,a = 1, b = 2, c = 3, d = 4
function maximum(a)
    local mi = 1
    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{8,10,23,12,5})
-- 输出结果为:
-- 23
-- 233
function abcwyq( ... )
    -- body
    return 11,22,33
end

print(abcwyq())
--输出结果为
--11
--11 22
--11 22 33

前面说过,表类型可以拥有任意类型的值,包括函数!因此,有一个很强大的特性是,拥有函数的表,哦,我想更恰当的应该说是对象吧。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)

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{10,20,30})
   10  20  30
> a,b = unpack{10,20,30}
> print(a,b)
    10  20
> string.find(unpack{"hello","ll"})  --等同于string.find("hello","ll"),find函数的作用是寻找第二个参数ll在第一个参数中的索引位置

在Lua中unpack函数是用C语言实现的。为了便于理解,下面给出在Lua中通过递归实现一样的效果,如:

function unpack(t,i)
     i = i or 1
     if t[i] then
         return t[i], unpack(t,i + 1)
     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=5},{age=11},{age=1},{age=3},{age=9}}
table.sort(test_table,function(a,b) return (a.age > b.age) end)
for i=1,5 do
   print(test_table[i].age)
end
end

--输出结果
--11
--9
--5
--3
--1

 1. closure(闭合函数):
    若将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,见如下示例:

function newCounter() 
    local i = 0
    return function() --匿名函数
        i = i + 1
        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 = 0
    return function() --匿名函数
        i = i + 1
        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(4,5)
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)  --5

(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 * 2)
end

(3)由于数组实际上仍为一个table,所以对于数组大小的计算需要留意某些特殊的场景,如:   

a = {}
a[1000] = 1

在上面的示例中,数组a中索引值为1--999的元素的值均为nil。而Lua则将nil作为界定数据结尾的标志。当一个数组含有“空隙”时,即中间含有nil值,长度操作符#会认为这些nil元素就是结尾标志。

当然这肯定不是我们想要的结果。因此对于这些含有“空隙”的数组,我们可以通过函数table.maxn()返回table的最大正数索引值。如:

a = {}
a[1]=20;
a["sss"]=444;
a[100]=10;
a[18]=20;
print(table.maxn(a))    -- 100
print(#a)   -- 1

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 = {[4] = "red1",[3] = "red2",[5] = "red3",[1] = "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=1, #(tbtest) do 
     XXX 
end

这种遍历,只能遍历连续的数组(键从1开始,以增量为1递增的键的table),不然遍历会出现意想不到的结果!

 

for i=1, 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

posted @ 2015-08-12 22:22  MrZivChu  阅读(695)  评论(0编辑  收藏  举报
分享按钮