Lua

Lua 学习笔记(一)

Posted by Richbabe on June 20, 2018

Lua 数据类型

Lua是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。

Lua中有8个基本类型分别为:nil、boolean、number、string、userdata、function、thread和table。

可以通过type函数测试给定变量或者值得类型:

print(type("Hello world"))      --> string
print(type(10.4*3))             --> number
print(type(print))              --> function
print(type(type))               --> function
print(type(true))               --> boolean
print(type(nil))                --> nil
print(type(type(X)))            --> string

nil(空)

nil 类型表示一种没有任何有效值,它只有一个值 – nil,例如打印一个没有赋值的变量,便会输出一个 nil 值:

> print(type(a))
nil
  • nil与type(变量)作比较时应该加上双引号””
> type(X) == nil
false

>type(X) == "nil"
true

未经赋值的全局变量与 nil 比较亦不会报错:

print(a===nil) -- 结果为 true

nil与非type(变量)作比较不用加双引号!

  • Lua把false和nil都看作是假
  • 对于全局变量和 table,nil 还有一个”删除”作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉

boolean(布尔值)

不同于 C/C++,在 Lua 中对 0(number) 进行逻辑判断,得到的结果也是 true

if 0 then
    print("True.")
else
    print("False.")
end

运行结果为:True

string(字符串)

  • 运行时,Lua会自动在string和numbers之间自动进行类型转换,当一个字符串使用算术操作符时, string 就会被转成数字:
print("10"+ 1)     --> 11
print("10 + 1")  --> 10 + 1
print("hello"+ 1)    -- 报错 (无法转换 "hello")

反过来,当Lua期望一个string而碰到数字时,会将数字转成string:

print(10 .. 20) --> 1020

.. 在Lua中是字符串连接符,注意:当在一个数字后面写 .. 时,必须加上空格以防止被解释错。

  • 使用#来计算字符串的长度,放在字符串前面,比如:
> len = "aabb"
> print(#len)
4

table(表)

  • Lua的表可以通过{}来构造,其可以看成是一个关联数组,数组的索引可以是数字或者是字符串:
-- table_test.lua 脚本文件
a = {}
a["key"] = "value"
key = 10
a[key] = 22
a[key] = a[key] + 11
for k, v in pairs(a) do
    print(k .. " : " .. v)
end

脚本执行结果为:

$ lua table_test.lua 
key : value
10 : 33
  • 在Lua里表的默认初始索引一般以1开始(与其他语言不同)
tab = {"Hello","World",a=1,b=2,z=3,x=10,y=20,"Good","Bye"}
for k,v in pairs(tab) do
    print(k.."  "..v)
end

如上代码输出结果存在一定规律,”Hello”、”World”、”Good”、”Bye”是表中的值,在存储时是按照顺序存储的,并且不同于其他脚本语言,Lua是从1开始排序的,因此,使用pairs遍历打印输出时,会先按照顺序输出表的值,然后再按照键值对的键的哈希值打印(按哈希值打印顺序不定!)。

1  Hello
2  World
3  Good
4  Bye
x  10
y  20
z  3
b  2
a  1
  • table不会固定长度大小,有新数据添加时table长度会自动增长,没初始的table都是nil

function(函数)

  • 在Lua中,函数是被看作是“第一类值(First-Class Value)”,函数可以存在变量里:
-- function_test.lua 脚本文件
function factorial1(n)
    if n == 0 then
        return 1
    else
        return n * factorial1(n - 1)
    end
end
print(factorial1(5))
factorial2 = factorial1
print(factorial2(5))

脚本执行结果为:

$ lua function_test.lua 
120
120
  • function可以以匿名函数的方式通过参数传递:
-- function_test2.lua 脚本文件
function testFun(tab,fun)
    for k ,v in pairs(tab) do
        print(fun(k,v));
    end
end


tab={key1="val1",key2="val2"};
testFun(tab,
function(key,val)--匿名函数
    return key.."="..val;
end
);

脚本执行结果为:

$ lua function_test2.lua 
key1 = val1
key2 = val2

thread(线程)

在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。

线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。

userdata(自定义类型)

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

Lua变量

Lua 变量有三种类型:全局变量、局部变量、表中的域。

  • Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。
  • 局部变量的作用域为从声明位置开始到所在语句块结束。
  • 变量的默认值均为 nil。
-- test.lua 文件脚本
a = 5               -- 全局变量
local b = 5         -- 局部变量

function joke()
    c = 5           -- 全局变量
    local d = 6     -- 局部变量
end  -- d的作用域结束

joke()
print(c,d)          --> 5 nil

do 
    local a = 6     -- 局部变量
    b = 6           -- 对局部变量重新赋值
    print(a,b);     --> 6 6
end -- 局部变量a作用域结束,a变为全局变量

print(a,b)      --> 5 6

赋值语句

  • 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会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数             按变量个数补足nil
b. 变量个数 < 值的个数             多余的值会被忽略

例如:

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, c = 0, 0, 0
print(a,b,c)             --> 0   0   0
  • 多值赋值经常用来交换变量,或将函数调用返回给变量:
a, b = f()

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

  • 应该尽可能的使用局部变量,有两个好处:
    • 避免命名冲突
    • 访问局部变量的速度比全局变量更快

索引

对 table 的索引使用方括号 []。Lua 也提供了” . “操作。

t[i]
t.i                 -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用

例如:

> site = {}
> site["key"] = "www.w3cschool.cc"
> print(site["key"])
www.w3cschool.cc
> print(site.key)
www.w3cschool.cc

Lua循环

Lua while 循环

语法:

while(condition)
do
   statements
end

Lua for 循环

Lua中for语句分为两大类:

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

数值for循环

语法:

for var=exp1,exp2,exp3 do  
    <执行体>  
end  

var从exp1变化到exp2,每次变化以exp3为步长递增var,并执行一次”执行体”。exp3是可选的,如果不指定,默认为1。

for的三个表达式在循环开始前一次性求值,以后不再进行求值:

#!/usr/local/bin/lua  
function f(x)  
    print("function")  
    return x*2   
end  
for i=1,f(5) do print(i)  
end  

以上实例输出结果为:

function
1
2
3
4
5
6
7
8
9
10

可以看到 函数f(x)只在循环开始前执行一次。

泛型for循环

泛型for循环通过一个迭代器函数来遍历所有值,类似c#中的foreach语句。 语法为:

--打印数组a的所有值  
for i,v in ipairs(a) 
    do print(v) 
end  

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

例子:

#!/usr/local/bin/lua  
days = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}  
for i,v in ipairs(days) do  print(v) end   

以上实例输出结果为:

Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

Lua repeat…until循环

相当于C++中的do…while:

repeat
   statements
until( condition )

Lua流程控制

if语法:

if(布尔表达式)
then
   --[ 在布尔表达式为 true 时执行的语句 --]
end

if…else语法:

if(布尔表达式)
then
   --[ 布尔表达式为 true 时执行该语句块 --]
else
   --[ 布尔表达式为 false 时执行该语句块 --]
end

if…elseif…else语法:

if( 布尔表达式 1)
then
   --[ 在布尔表达式 1 为 true 时执行该语句块 --]

elseif( 布尔表达式 2)
then
   --[ 在布尔表达式 2 为 true 时执行该语句块 --]

elseif( 布尔表达式 3)
then
   --[ 在布尔表达式 3 为 true 时执行该语句块 --]
else 
   --[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end

Lua 函数

函数语法为:

optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
    function_body
    return result_params_comma_separated
end

解析:

  • optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
  • function_name: 指定函数名称。
  • argument1, argument2, argument3…, argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
  • function_body: 函数体,函数中需要执行的代码语句块。
  • result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。

可变参数

Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 … 表示函数有可变的参数。

function add(...)  
local s = 0  
  for i, v in ipairs{...} do   --> {...} 表示一个由所有变长参数构成的数组  
    s = s + v  
  end  
  return s  
end  
print(add(3,4,5,6,7))  --->25

我们可以将可变参数赋值给一个变量,例如我们计算几个数的平均值:

function average(...)
   result = 0
   local arg={...}    --> arg 为一个表,局部变量,把可变参数赋值给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

我们也可以通过 select(“#”,…) 来获取可变参数的数量:

function average(...)
   result = 0
   local arg={...}
   for i,v in ipairs(arg) do
      result = result + v
   end
   print("总共传入 " .. select("#",...) .. " 个数")
   return result/select("#",...)
end

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

执行结果同上面一样。

有时候我们可能需要几个固定参数加上可变参数,固定参数必须放在变长参数之前:

function fwrite(fmt, ...)  ---> 固定的参数fmt
    return io.write(string.format(fmt, ...))     
end

fwrite("runoob\n")       --->fmt = "runoob", 没有变长参数。  
fwrite("%d%d\n", 1, 2)   --->fmt = "%d%d", 变长参数为 1 和 2

通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select(‘#’, …) 或者 select(n, …)

  • select(‘#’, …) 返回可变参数的长度
  • select(n, …) 用于访问变长参数中第n个数
do  
    function foo(...)  
        for i = 1, select('#', ...) do  -->获取参数总数
            local arg = select(i, ...); -->读取参数
            print("arg", arg);  
        end  
    end  
  
    foo(1, 2, 3, 4);  
end

输出结果为:

arg    1
arg    2
arg    3
arg    4

Lua运算符

  • ~= 表示不等于
  • and、or、not分别表示与、或、非
  • .. 表示连接两个字符串
  • #返回字符串或表的长度
  • ^ 表示次幂
  • table t 的长度被定义成一个整数下标 n 。 它满足 t[n] 不是 nil 而 t[n+1] 为 nil; 此外,如果 t[1] 为 nil ,n 就可能是零。 对于常规的数组,里面从 1 到 n 放着一些非空的值的时候, 它的长度就精确的为 n,即最后一个值的下标。 如果数组有一个“空洞” (就是说,nil 值被夹在非空值之间), 那么 #t 可能是指向任何一个是 nil 值的前一个位置的下标 (就是说,任何一个nil 值都有可能被当成数组的结束)。
tab3 = {}
tab3[1] = nil
tab3[2] = "2"
tab3[3] = "4"
tab3[4] = "1"
tab3[5] = "1"

print("tab4的长度", #tab3)
--tab4的长度    5

tab4 = {}
tab4[1] = nil
tab4[2] = "1"
tab4[3] = "2"
print("tab4的长度", #tab4)

--tab4的长度    0

tab5 = {}
tab5[1] = "1"
tab5[2] = nil
tab5[3] = "2"
tab5[4] = "4"
print("tab5的长度", #tab5)

--tab5的长度    4

tab6 = {1, nil, 3}
print("tab6的长度", #tab6)

--tab6的长度    3

tab6 = {1, nil, 3, nil}
print("tab6的长度", #tab6)

--tab6的长度    1
  • Lua中三目运算符写法:
(condition and {result1} or {result2})[1]

具体可见我的博客

Lua 字符串

Lua 字符串声明

Lua中字符串除了用单引号和双引号表示字符串之外,还能用[[]]表示字符串:

string1 = "Lua"

string2 = 'runoob.com'

string3 = [["Lua 教程"]]

Lua 字符串常用函数

  • string.upper(argument):字符串全部转为大写字母。
  • string.lower(argument): 字符串全部转为小写字母。
  • string.gsub(mainString,findString,replaceString,num):

    在字符串中替换,mainString为要替换的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换),如:

> string.gsub("aaaa","a","z",3);
zzza    3
  • string.find (str, substr, [init, [end]])

    在一个指定的目标字符串中搜索指定的内容(第三个参数为从第几个字符开始检索),返回其具体位置。不存在则返回 nil。

> string.find("Hello Lua user", "Lua", 1) 
7    9
  • string.reverse(arg): 字符串反转
  • string.format(…):返回一个类似printf的格式化字符串,函数的第一个参数是格式 , 之后是对应格式中每个代号的各种数据。
> string.format("the value is:%d",4)
the value is:4
  • string.char(arg) 和 string.byte(arg[,int])

    char 将整型数字按照其ascii码转成字符并连接, byte 按照ascii码转换字符为整数值(可以指定某个字符,默认第一个字符)。

> string.char(97,98,99,100)
abcd
> string.byte("ABCD",4)
68
> string.byte("ABCD")
65
>
  • string.len(arg):计算字符串长度(也可以用#)
  • string.rep(string, n):返回字符串string的n个拷贝
> string.rep("abcd",2)
abcdabcd
  • string.gmatch(str, pattern):

    回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。

> for word in string.gmatch("Hello Lua user", "%a+") do print(word) end
Hello
Lua
user

其中”%a+”表示是字符串,与之类似的还有”%d+”表示是数字,具体可见下面的匹配模式

  • string.match(str, pattern, init):

    string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。

> = string.match("I have 2 questions for you.", "%d+ %a+")
2 questions

> = string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)"))
2, "questions"

匹配模式

Lua 中的匹配模式直接用常规的字符串来描述。 它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。

你还可以在模式串中使用字符类。

字符类指可以匹配一个特定字符集合内任何字符的模式项。比如,字符类%d匹配任意数字。所以你可以使用模式串 ‘%d%d/%d%d/%d%d%d%d’ 搜索 dd/mm/yyyy 格式的日期:

s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date)))    --> 30/05/1999

下面列出了Lua支持的所有字符类:

单个字符(除 ^$()%.[]*+-? 外): 与该字符自身配对

  • .(点): 与任何字符配对
  • %a: 与任何字母配对
  • %c: 与任何控制符配对(例如\n)
  • %d: 与任何数字配对
  • %l: 与任何小写字母配对
  • %p: 与任何标点(punctuation)配对
  • %s: 与空白字符配对
  • %u: 与任何大写字母配对
  • %w: 与任何字母/数字配对
  • %x: 与任何十六进制数配对
  • %z: 与任何代表0的字符配对
  • %x(此处x是非字母非数字字符): 与字符x配对. 主要用来处理表达式中有功能的字符(^$()%.[]*+-?)的配对问题, 例如%%与%配对

当上述的字符类用大写书写时, 表示与非此字符类的任何字符配对. 例如, %S表示与任何非空白字符配对.例如,’%A’非字母的字符:

> print(string.gsub("hello, up-down!", "%A", "."))
hello..up.down.    4

数字4不是字符串结果的一部分,他是gsub返回的第二个结果,代表发生替换的次数。

在模式匹配中有一些特殊字符,他们有特殊的意义,Lua中的特殊字符如下:

( ) . % + - * ? [ ^ $

’%’ 用作特殊字符的转义字符,因此 ‘%.’ 匹配点;’%%’ 匹配字符 ‘%’。转义字符 ‘%’不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。

模式条目

  • 单个字符类匹配该类别中任意单个字符;
  • 单个字符类跟一个 ‘*‘, 将匹配零或多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 ‘+’, 将匹配一或更多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 ‘-‘, 将匹配零或更多个该类的字符。 和 ‘*’ 不同, 这个条目总是匹配尽可能短的串;
  • 单个字符类跟一个 ‘?’, 将匹配零或一个该类的字符。 只要有可能,它会匹配一个;
  • %n, 这里的 n 可以从 1 到 9; 这个条目匹配一个等于 n 号捕获物(后面有描述)的子串。
  • %bxy, 这里的 x 和 y 是两个明确的字符; 这个条目匹配以 x 开始 y 结束, 且其中 x 和 y 保持 平衡 的字符串。 意思是,如果从左到右读这个字符串,对每次读到一个 x 就 +1 ,读到一个 y 就 -1, 最终结束处的那个 y 是第一个记数到 0 的 y。 举个例子,条目 %b() 可以匹配到括号平衡的表达式。
  • %f[set], 指 边境模式; 这个条目会匹配到一个位于 set 内某个字符之前的一个空串, 且这个位置的前一个字符不属于 set 。 集合 set 的含义如前面所述。 匹配出的那个空串之开始和结束点的计算就看成该处有个字符 ‘\0’ 一样。

模式

模式 指一个模式条目的序列。 在模式最前面加上符号 ‘^’ 将锚定从字符串的开始处做匹配。 在模式最后面加上符号 ‘$’ 将使匹配过程锚定到字符串的结尾。 如果 ‘^’ 和 ‘$’ 出现在其它位置,它们均没有特殊含义,只表示自身

捕获

模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物。 当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。 捕获物以它们左括号的次序来编号。 例如,对于模式 “(a(.)%w(%s))” , 字符串中匹配到 “a(.)%w(%s)” 的部分保存在第一个捕获物中 (因此是编号 1 ); 由 “.” 匹配到的字符是 2 号捕获物, 匹配到 “%s*” 的那部分是 3 号。

作为一个特例,空的捕获 () 将捕获到当前字符串的位置(它是一个数字)。 例如,如果将模式 “()aa()” 作用到字符串 “flaaap” 上,将产生两个捕获物: 3 和 5 。

Lua 多维数组

以下是一个三行三列的阵列多维数组:

-- 初始化数组
array = {}
for i=1,3 do
   array[i] = {}
      for j=1,3 do
         array[i][j] = i*j
      end
end

-- 访问数组
for i=1,3 do
   for j=1,3 do
      print(array[i][j])
   end
end

Lua 迭代器

迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址

在Lua中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。

泛型 for 迭代器

泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。

泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:

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

上面代码中,k, v为变量列表;pairs(t)为表达式列表。 查看以下实例:

array = {"Lua", "Tutorial"}

for key,value in ipairs(array) 
do
   print(key, value)
end

输出为:

1  Lua
2  Tutorial

以上实例中我们使用了 Lua 默认提供的迭代函数 ipairs。 下面我们看看泛型 for 的执行过程:

  • 首先,初始化,计算in后面表达式的值,表达式应该返回泛型 for 需要的三个值:迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用nil补足,多出部分会被忽略。
  • 第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于for结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
  • 第三,将迭代函数返回的值赋给变量列表。
  • 第四,如果返回的第一个值为nil循环结束,否则执行循环体。
  • 第五,回到第二步再次调用迭代函数

在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

以上实例中我们可以看到,elementIterator 内使用了闭包函数,实现计算集合大小并输出各个元素。

pairs 和 ipairs的异同

同:都是能遍历集合(表、数组)

异:

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

代码示例1:

local tab= { 
[1] = "a", 
[3] = "b", 
[4] = "c" 
} 
for i,v in pairs(tab) do        -- 输出 "a" ,"b", "c"  ,
    print( tab[i] ) 
end 

for i,v in ipairs(tab) do    -- 输出 "a" ,k=2时断开 
    print( tab[i] ) 
end

代码示例2:

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

for k, v in ipairs(tabFiles) do  --[[什么都没输出,为什么?因为控制变量初始值是按升序来遍历的
,当key为1时,value为nil,此时便停止了遍历, 所有什么结果都没输出]]--
    print(k, v)
end

for k, v in pairs(tabFiles) do  --输出2 test2, 6 test3, 4 test1
    print(k, v)
end

代码示例3:

local tabFiles = {"alpha", "beta", [3] = "no", ["two"] = "yes"}  
for i,v in ipairs(tabFiles ) do    --输出前三个   备注:因为第四个key不是整数
    print( tabFiles [i] )   
end   
  
for i,v in pairs(tabFiles ) do    --全部输出   
    print( tabFiles [i] )   
end