Lua基础语法

注释

  • 单行注释
    两个减号,--
  • 多行注释
    --[[ 内容 --]]

    数据类型

  • Lua是动态类型语言,变量不需要类型定义,只需为变量赋值
数据类型 描述
nil 只有值”nil”属于该类,表示一个无效值(在条件表达式里相当于false)
boolean false 和 true
number 双精度类型的实浮点数
string 由一对双引号" "或单引号' '表示
function 由C或Lua编写的函数
userdata 表示任意存储在变量中的C数据结构
thread 表示执行的独立线路
table Lua 中的表(table)其实是一个”关联数组”(associative arrays),数组的索引可以是数字或者是字符串。在 Lua 里,table 的创建是通过”构造表达式”来完成,最简单构造表达式是{},用来创建一个空表。

nil

  • nil类型表示一种没有任何有效值,它只有一个值”nil”
  • 对于全局变量和table,nil的另外一个作用为删除,给全局变量或table表里的变量赋值nil,等于把它们删掉

    boolean

  • Lua把false和nil看作“假”,其余为“真”

    number

  • Lua默认只有一种number类型,即double(双精度)类型,默认类型可以修改luaconf.h里的定义。以下几种写法都是number类型
    >print(type(2))
    >print(type(2.2))
    >print(type(0.2))
    >print(type(2e+1))
    >print(type(0.2e-1))
    >print(type(7.8263692594256e-06))

    number
    number
    number
    number
    number
    number

string

  • 由一对双引号" "或单引号' '表示
  • 两个方括号[[ ]]表示一块字符串

    >html = [[
    <html>
    <head></head>
    <body>
    <a href="http://www.w3cschool.cc/">w3cschool菜鸟教程</a>
    </body>
    </html>
    ]]
    print(html)

    <html>
    <head></head>
    <body>
    <a href="http://www.w3cschool.cc/">w3cschool菜鸟教程</a>
    </body>
    </html>
  • 在对一个数字字符串上进行算术操作时,Lua会将数字字符串转成一个数字

    > print("2" + 6)
    8.0
    > print("2" + "6")
    8.0
    > print("2 + 6")
    2 + 6
    > print("-2e2" * "6")
    -1200.0
    > print("error" + 1)
    stdin:1: attempt to perform arithmetic on a string value
    stack traceback:
    stdin:1: in main chunk
    [C]: in ?
    >
  • 字符串连接需要用..,需加上空格

    >print('a' .. 'b')
    ab

    >print(157 .. 333)
    157333
  • 使用#计算字符串的长度,放在字符串前面

    >len='www.w3cshool.cc'
    >print(#len)
    16
    >print(#'www.w3cschool.cc')
    16

table

--创建一个空表
local table1={}

--初始表
local table2={'apple','pear','orange','grape'}
  • Lua中的table是一个”关联数组”,数组的索引可以数字或字符串
    【注】Lua中table 默认初始索引从1开始
    a={}
    a['key']='value'
    key=10
    a[key]=22
    a[key]=a[key]+11

    for k,v in ipairs(a) do
    print(k .. ':' .. v)
    end

    结果
    key:value
    10:33

function

  • 在Lua中,function被看作是”第一类值(First-Class Value)”,函数可以存在变量里
function factorial1(n)
if n==0 then
return 1
else
return n*factorial1(n-1)
end
end

print(factorial1(5))

factorial2=factorial1
print(fatorial2(5))

结果
120
120

userdata

userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。

变量

  • 变量在使用前,必须在代码中进行声明,即创建该变量。
  • Lua 变量有三种类型:全局变量、局部变量、表中的域
  • 变量的默认值均为 nil。

标识符

  • 一般用下划线加大写字母的标识符,为Lua的保留字。如:_VERSION 为保留用于Lua内部全局变量
  • 区分大小写

    全局变量

  • 默认是全局变量
  • 全局变量无需声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,得到的结果为:nil

    print(b)
    >nil
    b=10
    print(b)
    >10
  • 删除一个全局变量,将变量赋值为nil即可

    b=nil
    print(b)
    >nil

局部变量

使用local声明
应该尽可能的使用局部变量,有两个好处:

  1. 避免命名冲突。
  2. 访问局部变量的速度比全局变量更快。

    赋值语句

  • Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。

    a, b = 10, 2*x       <-->       a=10; b=2*x
  • 遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:

    x, y = y, x                     -- swap 'x' for 'y'
    a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[j]'
  • 当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:

  1. 变量个数 > 值的个数 按变量个数补足nil
  2. 变量个数 < 值的个数 多余的值会被忽略
>a, b, c = 0, 1
>print(a,b,c)

0 1 nil

>a, b = a+1, b+1, b+2 -- value of b+2 is ignored
>print(a,b)
>1 2

>a, b, c = 0
>print(a,b,c)

0 nil nil
  • 多值赋值经常用来交换变量,或将函数调用返回给变量:
    a,b=fun()

f()返回两个值,第一个赋给a,第二个赋给b。

循环

while

while(condition)
do
statements
end

for

Lua中for语句有两大类:

  • 数值for循环
  • 泛型for循环

数值for循环

for var=exp1,exp2,exp3 do
statements
end
  • var从exp1变化到exp2,每次变化以exp3为步长递增var,并执行一次”执行体”。exp3是可选的,如果不指定,默认为1
  • for的三个表达式在循环开始前一次性求值,以后不再进行求值。

    泛型for循环

    用过一个迭代器遍历所有值
    --打印数组a 的所有值
    for i,v in ipairs(a)
    do print(v)
    end

i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。

days={"Suanday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}

for i,v in ipairs(days) do
print(v)
end


结果
Suanday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

repeat…until

Lua 编程语言中 repeat…until 循环语句不同于 for 和 while循环,for 和 while 循环的条件语句在当前循环执行开始时判断,而 repeat…until 循环的条件语句在当前循环结束后判断。

repeat
statements
until(contidion)

我们注意到循环条件判断语句(condition)在循环体末尾部分,所以在条件进行判断前循环体都会执行一次。

函数

函数作为参数

Lua 中我们可以将函数作为参数传递给函数

myprint = function(param)
print("这是打印函数 - ##",param,"##")
end

function add(num1,num2,functionPrint)
result = num1 + num2
-- 调用传递的函数参数
functionPrint(result)
end
myprint(10)
-- myprint 函数作为参数传递
add(2,5,myprint)

结果
这是打印函数 - ## 10 ##
这是打印函数 - ## 7 ##

多返回值

  • Lua可以返回多个结果值,如string.find,返回匹配串“开始和结束的下标”(如果不存在匹配串返回nil)

    >s,e=string.find('www.runoob.com','runoob')
    >print(s,e)

    5 10
  • Lua函数中,在return后列出要返回的值得列表即可返回多值

function maximum(a)
local mi=1 --最大值索引
local m=a[mi] --最大值

for i,val in ipairs(a) do
if val > m then
mi=i
m=val
end
end
return m,mi
end

print(maximum({8,10,23,12,5}))


结果
23 3
  • return f()这种形式,则返回“f()的返回值”:

    function foo0() end
    function foo1() return 'a' end
    function foo2() return 'a','b' end

    function foo(i)
    if i==0 then return foo0()
    elseif i==1 then return foo1()
    elseif i==2 then return foo2()
    end
    end

    >print(foo(1))
    a
    >print(foo(2))
    a b
    >print(foo(0))
    --(no result)
    >print(foo(3))
    --(no result)
  • 可以使用圆括号( )强制使调用返回一个值

    >print((foo0()))
    nil
    >print((foo1()))
    a
    >print((foo2()))
    a
  • 函数多值返回的特殊函数unpack,接受一个数组作为输入参数,返回数组的所有元素。unpack被用来实现范型调用机制,在C语言中可以使用函数指针调用可变的函数,可以声明参数可变的函数,但不能两者同时可变。在Lua中如果你想调用可变参数的可变函数只需要这样:
    f(unpack(a))
    unpack返回a所有的元素作为f()的参数

    f=string.find
    a={'hello','ll'}
    print(f(unpack(a)))

    -->3 4

预定义的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语言类似在函数参数列表中使用三点(…) 表示函数有可变的参数。
  • Lua将函数的参数放在一个叫arg的表中,#arg 表示传入参数的个数。
    function average(...)
    result = 0
    local arg={...}
    for i,v in ipairs(arg) do
    result = result + v
    end
    print('总共传入' .. #arg .. '个数')
    return result/#arg
    end

    print('平均值为',average(10,5,3,4,5,6))

    结果
    总共传入 6 个数
    平均值为 5.5

再论函数

Lua中的函数是带有词法定界(lexical scoping)的第一类值(first-class value)

第一类值

在Lua中函数和其他值(数值、字符串)一样,函数可以被存放在变量中,也可以存放在table中,可以作为函数的参数,还可以作为函数的返回值

词法定界

嵌套的函数可以访问它外部函数中的变量

  • Lua中关于函数难以理解的地方时函数也可以是匿名的。
    函数名(如print)实际上是一个指向函数的变量,像具有其他类型值的变量一样
    a={p=print}
    a.p('hello world') -->hello world

    print=math.sin -- print now refers to the sin function

    a.p(print(1)) -->0.841470

    sin=a.p --sin now refers to the print function
    sin(10,20) -->10 20

闭包

当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界。虽然这看起来很清楚,事实并非如此,词法定界加上第一类函数在编程语言里是一个功能强大的概念,很少语言提供这种支持。
下面看一个简单的例子,假定有一个学生姓名的列表和一个学生名和成绩对应的表;现在想根据学生的成绩从高到低对学生进行排序,可以这样做:

names={'Peter','Paul','Mary'}
grades={Mary=10,Paul=7,Peter=8}
table.sort(names,function (n1,n2)
return grades[n1]>grades[n2]
end)

假定创建一个函数实现此功能

function sortByGrade(names,grades)
table.sort(names,function(n1,n2)
return grades[n1]>grades[n2]
end)
end

例子中包含在sortbygrade函数内部的sort中的匿名函数可以访问sortbygrade的参数grades,在匿名函数内部grades不是全局变量也不是局部变量,我们称作外部的局部变量(external local variable)或者upvalue。(upvalue意思有些误导,然而在Lua中他的存在有历史的根源,还有他比起external local variable简短)。

function newCounter()
local i = 0
return function() -- anonymous function
i = i + 1
return i
end
end

c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2

匿名函数使用upvalue i保存他的计数,当我们调用匿名函数的时候i已经超出了作用范围,因为创建i的函数newCounter已经返回了。然而Lua用闭包的思想正确处理了这种情况。简单的说,闭包是一个函数以及它的upvalues。如果我们再次调用newCounter,将创建一个新的局部变量i,因此我们得到了一个作用在新的变量i上的新闭包。

c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2

c1、c2是建立在同一个函数上,但作用在同一个局部变量的不同实例上的两个不同的闭包。
技术上来讲,闭包指值而不是指函数,函数仅仅是闭包的一个原型声明;尽管如此,在不会导致混淆的情况下我们继续使用术语函数代指闭包。
闭包在上下文环境中提供很有用的功能,如前面我们见到的可以作为高级函数(sort)的参数;作为函数嵌套的函数(newCounter)。这一机制使得我们可以在Lua的函数世界里组合出奇幻的编程技术。闭包也可用在回调函数中,比如在GUI环境中你需要创建一系列button,但用户按下button时回调函数被调用,可能不同的按钮被按下时需要处理的任务有点区别。具体来讲,一个十进制计算器需要10个相似的按钮,每个按钮对应一个数字,可以使用下面的函数创建他们:

function digitButton (digit)
return Button{ label = digit,
action = function ()
add_to_display(digit)
end
}
end

这个例子中我们假定Button是一个用来创建新按钮的工具, label是按钮的标签,action是按钮被按下时调用的回调函数。(实际上是一个闭包,因为他访问upvalue digit)。digitButton完成任务返回后,局部变量digit超出范围,回调函数仍然可以被调用并且可以访问局部变量digit。
闭包在完全不同的上下文中也是很有用途的。因为函数被存储在普通的变量内我们可以很方便的重定义或者预定义函数。通常当你需要原始函数有一个新的实现时可以重定义函数。例如你可以重定义sin使其接受一个度数而不是弧度作为参数:

oldSin = math.sin
math.sin = function (x)
return oldSin(x*math.pi/180)
end

更清楚的方式

do
local oldSin = math.sin
local k = math.pi/180
math.sin = function (x)
return oldSin(x*k)
end
end

这样我们把原始版本放在一个局部变量内,访问sin的唯一方式是通过新版本的函数。
利用同样的特征我们可以创建一个安全的环境(也称作沙箱,和java里的沙箱一样),当我们运行一段不信任的代码(比如我们运行网络服务器上获取的代码)时安全的环境是需要的,比如我们可以使用闭包重定义io库的open函数来限制程序打开的文件。

do
local oldOpen = 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标准库使用这种机制来实现的比如io.read、math.sin)。这种情况下,必须注意函数和表语法:
  1. 表和函数放在一起

    Lib={}
    Lib.foo=function (x,y) return x+y end
    Lib.foo=function (x,y) return x-y end
  2. 使用表构造函数

    Lib={
    foo = function (x,y) return x+y end,
    goo = function (x,y) return x-y end
    }
  3. Lua提供另一种语法方式

    Lib={}
    function Lib.foo (x,y)
    return x+y
    end
    function Lib.goo (x,y)
    return x-y
    end

当我们将函数保存在一个局部变量内时,我们得到一个局部函数,也就是说局部函数像局部变量一样在一定范围内有效。这种定义在包中是非常有用的:因为Lua把chunk当作函数处理,在chunk内可以声明局部函数(仅仅在chunk内可见),词法定界保证了包内的其他函数可以调用此函数。下面是声明局部函数的两种方式:
1.方式一

local f=function()
...
end

local g=function()
...
f() -- external loca 'f' is visible here
...
end

2.方式二

local funciton f()
...
end

  • 有一点需要注意的是在声明递归局部函数的方式:
    local fact=function (n)
    if n==0 then
    return 1
    else
    return n*fact(n-1) -- buggy
    end
    end

上面这种方式导致Lua编译时遇到fact(n-1)并不知道他是局部函数fact,Lua会去查找是否有这样的全局函数fact。为了解决这个问题我们必须在定义函数以前先声明:

local fact

fact=function (n)
if n==0 then
return 1
else
return n*fact(n-1)
end
end

这样在fact内部fact(n-1)调用是一个局部函数调用,运行时fact就可以获取正确的值了。
但是Lua扩展了他的语法使得可以在直接递归函数定义时使用两种方式都可以。
在定义非直接递归局部函数时要先声明然后定义才可以:

local f,g			--forward declarations

function g()
...
f()
...
end

function f()
...
g()
...
end

运算符

关系运算符

  • ~= 不等于

    逻辑运算符

    Lua中false 和 nil 是假,其余为真,0也是true
  • and 逻辑与
  • or 逻辑或
  • not 逻辑非

and优先级比or

and 和 or 的运算结果不是truefalse,而是和它的两个操作数有关。

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为falsenil,则给x赋值初始值v

    x = x or v
    <==>
    if not x then
    x=v
    end
  • C语言中的三元运算符
    a?b:c
    在Lua中可以如此实现
    (a and b) or c

  • not的结果只返回falsetrue

    其他运算符

  • .. 连接两个字符串
  • # 返回字符串或table 的长度

字符串

常用操作

函数 用途
string.upper(arg) 转为大写字母
string.lower(arg) 转为小写字母
string.gsub(mainString,findString,replaceString,num) 在字符串中替换,mainString为要替换的字符串,findString为被替换的字符,replaceString为要替换的字符,num为替换次数(可忽略,则全部替换)
string.find(str,subStr,[init,end]) 在一个指定的目标字符串中搜索指定的内容,第三个参数为索引,返回具体位置,不存在返回nil
string.reverse(arg) 字符串反转
string.format(…) 返回一个类似print的格式化字符串
string.char(arg)和string.byte(arg,[int]) char将整形数字转成字符并连接,byte将字符转为整数值(可以指定某个字符,默认第一个字符)
string.len(arg) 计算长度
string.rep(string,n) 返回字符串的n个拷贝

table

  • table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。

  • table 是不固定大小的,你可以根据自己需要进行扩容。

  • Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用”format”来索引table string。

    table的构造

    --初始表
    myTable={}

    --指定值
    myTable[1]='Lua'

    --移除引用
    myTable=nil --Lua垃圾回收会释放内存
  • 当我们为 table a 并设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存。如果 a 设置为 nil ,则 b 同样能访问 table 的元素。如果没有指定的变量指向a,Lua的垃圾回收机制会清理相对应的内存。

    -- 简单的 table
    mytable = {}
    print("mytable 的类型是 ",type(mytable))

    mytable[1]= "Lua"
    mytable["wow"] = "修改前"
    print("mytable 索引为 1 的元素是 ", mytable[1])
    print("mytable 索引为 wow 的元素是 ", mytable["wow"])

    -- alternatetable和mytable的是指同一个 table
    alternatetable = mytable

    print("alternatetable 索引为 1 的元素是 ", alternatetable[1])
    print("mytable 索引为 wow 的元素是 ", alternatetable["wow"])

    alternatetable["wow"] = "修改后"

    print("mytable 索引为 wow 的元素是 ", mytable["wow"])

    -- 释放变量
    alternatetable = nil
    print("alternatetable 是 ", alternatetable)

    -- mytable 仍然可以访问
    print("mytable 索引为 wow 的元素是 ", mytable["wow"])

    mytable = nil
    print("mytable 是 ", mytable)


    结果
    mytable 的类型是 table
    mytable 索引为 1 的元素是 Lua
    mytable 索引为 wow 的元素是 修改前
    alternatetable 索引为 1 的元素是 Lua
    mytable 索引为 wow 的元素是 修改前
    mytable 索引为 wow 的元素是 修改后
    alternatetable 是 nil
    mytable 索引为 wow 的元素是 修改后
    mytable 是 nil
  • 构造函数可以使用任何表达式初始化

    tab={sin(1),sin(2),sin(3),sin(4),sin(5)}
  • 在构造函数中域分隔符逗号(”,”)可以用分号(”;”)替代,通常我们使用分号用来分割不同类型的表元素。

tab={x=10, y=45; "one", "two", "three"}

常用操作

函数 用途
table.concat(table,sep,start,end) concat是concatenate(连接)的缩写.table.concat()列出指定table从strat位置到end位置的所有元素,元素间以指定分隔符(sep)隔开
table.insert(table,pos,value) 在table的指定位置pos插入值为value的元素
table.remove(table,pos) 返回table位于pos未知的元素,其余元素前移
table.sort(table,comp) 对table升序排序

模块与包

创建模块

  • 模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
  • Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。
    --定义一个名为module的模块
    module={}

    --定义一个常量
    module.constant='这是一个常量'

    --定义一个函数
    function module.func1()
    io.write('这是一个公有函数')
    end

    local funtion func2()
    print('这是一个私有函数')
    end

    function module.func3()
    func2()
    end

    return module

由上可知,模块的结构就是一个 table 的结构,因此可以像操作调用 table 里的元素那样来操作调用模块里的常量或函数。
上面的 func2 声明为程序块的局部变量,即表示一个私有函数,因此是不能从外部访问模块里的这个私有函数,必须通过模块里的公有函数来调用.

require函数

  • require函数用来加载模块
    require('模块名')
  • 也可以给加载的模块定义一个别名变量,如
    local m=require('module')

C包

  • Lua和C是很容易结合的,使用C为Lua写包。
  • 与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。
  • Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:

    local path = "/usr/local/lua/lib/libluasocket.so"
    local f = loadlib(path, "luaopen_socket")
  • loadlib函数加载指定的库并且连接到Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为Lua的一个函数,这样我们就可以直接在Lua中调用他。

  • 如果加载动态库或者查找初始化函数时出错,loadlib将返回nil和错误信息。我们可以修改前面一段代码,使其检测错误然后调用初始化函数:
    local path = "/usr/local/lua/lib/libluasocket.so"
    -- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
    local f = assert(loadlib(path, "luaopen_socket"))
    f() -- 真正打开库

一般情况下我们期望二进制的发布库包含一个与前面代码段相似的stub文件,安装二进制库的时候可以随便放在某个目录,只需要修改stub文件对应二进制库的实际路径即可。
将stub文件所在的目录加入到LUA_PATH,这样设定后就可以使用require函数加载C库了。

迭代器

在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。Lua 的迭代器包含以下两种类型:

  • 无状态的迭代器
  • 多状态的迭代器

    无状态迭代器

    无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。
    每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。
    这种无状态迭代器的典型的简单的例子是ipairs,他遍历数组的每一个元素。
    以下实例我们使用了一个简单的函数来实现迭代器,实现 数字 n 的平方:
    function square(iteratorMaxCount,currentNumber)
    if currentNumber<iteratorMaxCount then
    currentNumber=currentNumber+1
    return currentNumber,currentNumber*currentNumber
    end
    end

    for i,n in square,3,0 do
    print(i,n)
    end



    结果
    1 1
    2 4
    3 9

迭代的状态包括被遍历的表(循环过程中不会改变的状态常量)和当前的索引下标(控制变量),ipairs和迭代函数都很简单,我们在Lua中可以这样实现:

function iter (a, i)
i = i + 1
local v = a[i]
if v then
return i, v
end
end

function ipairs (a)
return iter, a, 0
end

当Lua调用ipairs(a)开始循环时,他获取三个值:迭代函数iter、状态常量a、控制变量初始值0;然后Lua调用iter(a,0)返回1,a[1](除非a[1]=nil);第二次迭代调用iter(a,1)返回2,a[2]……直到第一个nil元素。

多状态迭代器

很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到table内,将table作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在table内,所以迭代函数通常不需要第二个参数。
以下实例我们创建了自己的迭代器:

array={'Lua','Tutorial'}

function elementIterator(collection)
local index=0
local count=#collection
--闭包函数
return function()
index=index+1
if index<=count then
--返回迭代器当前元素
return collection[index]
end
end
end

for element in elementIterator(array) do
print(element)
end


结果
Lua
Tutorial

pairs和ipairs异同

  • 同:都能遍历集合(table,数组)
  • 异:
    • ipairs仅能遍历值,按照索引升序遍历,索引中断停止遍历。即不能返回nil,只能返回数字0.
      如果遇到nil则退出,只能遍历到集合中出现的第一个不是整数的key
    • pairs能遍历集合的所有元素。
      即pairs可以遍历集合中所有的key,并除了迭代器本身以及遍历表本身还可以返回nil

例1

local tabFiles={
[1]='test2'
[6]='test3'
[4]='test1'
}

for k,v in ipairs(tabFiles) do
print(k,v)
end

--[[
结果为1 test2,在key为2时断开
--]]

例2

local tabFiles={
[2]='test2'
[6]='test3'
[4]='test1'
}

for k,v in ipairs(tabFiles) do
print(k,v)
end

--[[
什么都没输出,因为控制变量初始值按升序遍历,当key为1时,value值为nil,便停止遍历,所以无结果输出
--]]

例3

local tabFiles = {
[2] = "test2",
[6] = "test3",
[4] = "test1"
}

for k, v in pairs(tabFiles) do
print(k, v)
end

--输出2 test2, 6 test3, 4 test1

例4

local tabFiles={
'alpha',
'beta',
[3]='no',
['two']='yes'
}

for k,v in ipairs(tabFiles) do
print(tabFiles[i])
end
--输出前三个,因为第四个key不是整数

for i,v in pairs(tabFiles) do
print(tabFiles[i])
end

--全部输出

协同程序(coroutine)

什么是coroutine?

coroutine与线程类似,拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。

coroutine与线程区别

  • 一个具有多线程的可以同时运行几个线程,coroutine则需要彼此协作运行
  • 在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。

    常用操作

    函数|描述|
    —-|—-|
    coroutine.create()|创建coroutine,返回coroutine|
    coroutine.resume()|重启coroutine|
    coroutine.yield()|挂起coroutine|
    coroutine.statues()|查看coroutine的状态,有三种:dead,suspend,running|
    coroutine.wrap()|创建corountine,返回一个函数,一旦调用这个函数,进入coroutine,和create功能重复|
    corountine.running()|返回正在运行的coroutine,一个coroutine是一个线程,当使用running时,返回一个coroutine的线程号|