Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。php
Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。html
其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。前端
轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,能够很方便的嵌入别的程序里。java
可扩展: Lua提供了很是易于使用的扩展接口和机制:由宿主语言(一般是C或C++)提供这些功能,Lua能够使用它们,就像是原本就内置的功能同样。mysql
其它特性linux
:ios
接下来咱们使用 Lua 来输出"Hello World!"c++
print("Hello World!")git
尝试一下 »程序员
运行后,会在屏幕上显示 Hello, world!。
由 sf0501 建立,Loen 最后一次修改 2015-09-08
Linux & Mac上安装 Lua 安装很是简单,只须要下载源码包并在终端解压编译便可,本文使用了5.3.0版本进行安装:
curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz tar zxf lua-5.3.0.tar.gz cd lua-5.3.0 make linux test make install
curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz tar zxf lua-5.3.0.tar.gz cd lua-5.3.0 make macosx test make install
接下来咱们建立一个 helloWorld.lua:
print("Hello World!")
执行如下命令:
$ lua helloWorld
输出结果为:
Hello World!
window下你能够使用一个叫"SciTE"的IDE环境来执行lua程序,下载地址为:
双击安装后便可在该环境下编写 Lua 程序并运行。
你也能够使用 Lua 官方推荐的方法使用 LuaDist:http://luadist.org/
若是安装的时候报错: lua.c:80:31: fatal error: readline/readline.h: No such file or directory
解决方法: 缺乏libreadline-dev依赖包
centos 系统: yum install readline-devel
debian 系统: apt-get install libreadline-dev
由 sf0501 建立,youj 最后一次修改 2015-09-06
Lua 学习起来很是简单,咱们能够建立第一个 Lua 程序!
Lua 提供了交互式编程模式。咱们能够在命令行中输入程序并当即查看效果。
Lua 交互式编程模式能够经过命令 lua -i 或 lua 来启用:
$ lua -i $ Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio >
在命令行中,输入如下命令:
> print("Hello World!")
接着咱们按下回车键,输出结果以下:
> print("Hello World!") Hello World! >
咱们能够将 Lua 程序代码保持到一个以 lua 结尾的文件,并执行,该模式称为脚本式编程,如咱们将以下代码存储在名为 hello.lua 的脚本文件中:
print("Hello World!") print("www.w3cschool.cn")
使用 lua 名执行以上脚本,输出结果为:
$ lua test.lua Hello World! www.w3cschool.cn
咱们也能够将代码修改成以下形式来执行脚本(在开头添加:#!/usr/local/bin/lua):
#!/usr/local/bin/lua print("Hello World!") print("www.w3cschool.cn")
以上代码中,咱们指定了 Lua 的解释器 /usr/local/bin directory。加上 # 号标记解释器会忽略它。接下来咱们为脚本添加可执行权限,并执行:
./test.lua Hello World! www.w3cschool.cn
两个减号是单行注释:
--
--[[ 多行注释 多行注释 --]]
Lua 标示符用于定义一个变量,函数获取其余用户定义的项。标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上0个或多个字母,下划线,数字(0到9)。
最好不要使用下划线加大写字母的标示符,由于Lua的保留字也是这样的。
Lua 不容许使用特殊字符如 @, $, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。所以在 Lua 中 W3c 与 w3c 是两个不一样的标示符。如下列出了一些正确的标示符:
mohd zara abc move_name a_123 myname50 _temp j a23b9 retVal
如下列出了 Lua 的保留关键字。保留关键字不能做为常量或变量或其余用户自定义标示符:
and | break | do | else |
---|---|---|---|
elseif | end | false | for |
function | if | in | local |
nil | not | or | repeat |
return | then | true | until |
while |
通常约定,如下划线开头链接一串大写字母的名字(好比 _VERSION)被保留用于 Lua 内部全局变量。
在默认状况下,变量老是认为是全局的。
全局变量不须要声明,给一个变量赋值后即建立了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过获得的结果是:nil。
> print(b) nil > b=10 > print(b) 10 >
若是你想删除一个全局变量,只须要将变量赋值为nil。
b = nil print(b) --> nil
这样变量b就好像从没被使用过同样。换句话说, 当且仅当一个变量不等于nil时,这个变量即存在。
由 sf0501 建立,youj 最后一次修改 2015-09-10
Lua是动态类型语言,变量不要类型定义,只须要为变量赋值。 值能够存储在变量中,做为参数传递或结果返回。
Lua中有8个基本类型分别为:nil、boolean、number、string、userdata、function、thread和table。
数据类型 | 描述 |
---|---|
nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中至关于false)。 |
boolean | 包含两个值:false和true。 |
number | 表示双精度类型的实浮点数 |
string | 字符串由一对双引号或单引号来表示 |
function | 由 C 或 Lua 编写的函数 |
userdata | 表示任意存储在变量中的C数据结构 |
thread | 表示执行的独立线路,用于执行协同程序 |
table | Lua 中的表(table)实际上是一个"关联数组"(associative arrays),数组的索引能够是数字或者是字符串。在 Lua 里,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 值:
> print(type(a)) nil >
对于全局变量和 table,nil 还有一个"删除"做用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉,执行下面代码就知:
tab1 = { key1 = "val1", key2 = "val2", "val3" } for k, v in pairs(tab1) do print(k .. " - " .. v) end tab1.key1 = nil for k, v in pairs(tab1) do print(k .. " - " .. v) end
boolean 类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看做是"假",其余的都为"真":
print(type(true)) print(type(false)) print(type(nil)) if type(false) or type(nil) then print("false and nil are false!") else print("other is true!") end
以上代码执行结果以下:
$ lua test.lua boolean boolean nil false and nil are false!
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
字符串由一对双引号或单引号来表示。
string1 = "this is string1" string2 = 'this is string2'
也能够用 2 个方括号 "[[]]" 来表示"一块"字符串。
html = [[ <html> <head></head> <body> <a href="//www.w3cschool.cn/">w3cschoolW3Cschool教程</a> </body> </html> ]] print(html)
如下代码执行结果为:
<html> <head></head> <body> <a href="//www.w3cschool.cn/">w3cschoolW3Cschool教程</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 ? >
以上代码中"error" + 1执行报错了,字符串链接使用的是 .. ,如:
> print("a" .. 'b') ab > print(157 .. 428) 157428 >
使用 # 来计算字符串的长度,放在字符串前面,以下实例:
> len = "www.w3cschool.cn" > print(#len) 16 > print(#"www.w3cschool.cn") 16 >
在 Lua 里,table 的建立是经过"构造表达式"来完成,最简单构造表达式是{},用来建立一个空表。也能够在表里添加一些数据,直接初始化表:
-- 建立一个空的 table local tbl1 = {} -- 直接初始表 local tbl2 = {"apple", "pear", "orange", "grape"}
Lua 中的表(table)实际上是一个"关联数组"(associative arrays),数组的索引能够是数字或者是字符串。
注意:Lua 中的表(table)只能遍历打印,直接打印会打印出table加一串数字
-- 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
不一样于其余语言的数组把 0 做为数组的初始索引,在 Lua 里表的默认初始索引通常以 1 开始。
-- table_test2.lua 脚本文件 local tbl = {"apple", "pear", "orange", "grape"} for key, val in pairs(tbl) do print("Key", key) end
脚本执行结果为:
$ lua table_test2.lua Key 1 Key 2 Key 3 Key 4
table 不会固定长度大小,有新数据添加时 table 长度会自动增加,没初始的 table 都是 nil。
-- table_test3.lua 脚本文件 a3 = {} for i = 1, 10 do a3[i] = i end a3["key"] = "val" print(a3["key"]) print(a3["none"])
脚本执行结果为:
$ lua table_test3.lua val nil
在 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 能够以匿名函数(anonymous function)的方式经过参数传递:
-- function_test2.lua 脚本文件 function anonymous(tab, fun) for k, v in pairs(tab) do print(fun(k, v)) end end tab = { key1 = "val1", key2 = "val2" } anonymous(tab, function(key, val) return key .. " = " .. val end)
脚本执行结果为:
$ lua function_test2.lua key1 = val1 key2 = val2
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差很少,拥有本身独立的栈、局部变量和指令指针,能够跟其余协同程序共享全局变量和其余大部分东西。
线程跟协程的区别:线程能够同时多个运行,而协程任意时刻只能运行一个,而且处于运行状态的协程只有被挂起(suspend)时才会暂停。
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所建立的类型,能够将任意 C/C++ 的任意数据类型的数据(一般是 struct 和 指针)存储到 Lua 变量中调用。
由 sf0501 建立,youj 最后一次修改 2015-09-27
变量在使用前,必须在代码中进行声明,即建立该变量。编译程序执行代码以前编译器须要知道如何给语句变量开辟存储区,用于存储变量的值。
Lua 变量有三种类型:全局变量、局部变量、表中的域。
函数外的变量默认为全局变量,除非用 local 显示声明。函数内变量与函数的参数默认为局部变量。
局部变量的做用域为从声明位置开始到所在语句块结束(或者是直到下一个同名局部变量的声明)。
变量的默认值均为 nil。
若是没有用local定义,即便在函数内部定义的变量也是全局变量!
-- test.lua 文件脚本 a = 5 -- 全局变量 local b = 5 -- 局部变量 function joke() c = 5 -- 全局变量 local d = 6 -- 局部变量 end joke() print(c,d) --> 5 nil do local a = 6 -- 局部变量 b = 6 -- 全局变量 print(a,b); --> 6 6 end print(a,b) --> 5 6
执行以上实例输出结果为:
$ lua test.lua 5 nil 6 6 5 6
赋值是改变一个变量的值和改变表域的最基本的方法。
a = "hello" .. "world" t.n = t.n + 1
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[i]'
当变量个数和值的个数不一致时,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.cn" > print(site["key"]) www.w3cschool.cn > print(site.key) www.w3cschool.cn
由 sf0501 建立,youj 最后一次修改 2015-09-05
不少状况下咱们须要作一些有规律性的重复操做,所以在程序中就须要重复执行某些语句。
一组被重复执行的语句称之为循环体,可否继续重复,决定循环的终止条件。
循环结构是在必定条件下反复执行某段程序的流程结构,被反复执行的程序被称为循环体。
循环语句是由循环体及循环的终止条件两部分组成的。
Lua 语言提供了如下几种循环处理方式:
循环类型 | 描述 |
---|---|
while 循环 | 在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。 |
for 循环 | 重复执行指定语句,重复次数可在 for 语句中控制。 |
Lua repeat...until | 重复执行循环,直到 指定的条件为真时为止 |
循环嵌套 | 能够在循环内嵌套一个或多个循环语句(while、for、do..while) |
循环控制语句用于控制程序的流程, 以实现程序的各类结构方式。
Lua 支持如下循环控制语句:
控制语句 | 描述 |
---|---|
break 语句 | 退出当前循环或语句,并开始脚本执行紧接着的语句。 |
在循环体中若是条件永远为 true 循环语句就会永远执行下去,如下以 while 循环为例:
while( true ) do print("循环将永远执行下去") end
Lua 编程语言中 while 循环语句在判断条件为 true 时会重复执行循环体语句。
Lua 编程语言中 while 循环语法:
while(condition) do statements end
statements(循环体语句) 能够是一条或多条语句,condition(条件) 能够是任意表达式,在 condition(条件) 为 true 时执行循环体语句。
流程图以下:
在以上流程图中咱们能够看出在condition(条件)为 false 时会跳过当前循环并开始脚本执行紧接着的语句。
如下实例循环输出 a 的值:
a=10 while( a < 20 ) do print("a 的值为:", a) a = a+1 end
执行以上代码,输出结果以下:
a 的值为: 10 a 的值为: 11 a 的值为: 12 a 的值为: 13 a 的值为: 14 a 的值为: 15 a 的值为: 16 a 的值为: 17 a 的值为: 18 a 的值为: 19
Lua 编程语言中 for 循环语句能够重复执行指定语句,重复次数可在 for 语句中控制。
Lua 编程语言中 for语句有两大类::
Lua 编程语言中数值for循环语法格式:
for var=exp1,exp2,exp3 do <执行体> end
var从exp1变化到exp2,每次变化以exp3为步长递增var,并执行一次"执行体"。exp3是可选的,若是不指定,默认为1。
for i=1,f(x) do print(i) end for i=10,1,-1 do print(i) end
for的三个表达式在循环开始前一次性求值,之后再也不进行求值。好比上面的f(x)只会在循环开始前执行一次,其结果用在后面的循环中。
验证以下:
#!/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循环经过一个迭代器函数来遍历全部值,相似java中的foreach语句。
Lua 编程语言中泛型for循环语法格式:
--打印数组a的全部值 for i,v in ipairs(a) do print(v) end
i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。
循环数组 days:
#!/usr/local/bin/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
Lua 编程语言中 repeat...until 循环语句不一样于 for 和 while循环,for 和 while循环d的条件语句在当前循环执行开始时判断,而 repeat...until 循环的条件语句在当前循环结束后判断。
Lua 编程语言中 repeat...until 循环语法格式:
repeat statements while( condition )
repeat...until 是条件后行,因此repeat...until 的循环体里面至少要运行一次。
statements(循环体语句) 能够是一条或多条语句,condition(条件) 能够是任意表达式,在 condition(条件) 为 true 时执行循环体语句。
在condition(条件)为 false 时会跳过当前循环并开始脚本执行紧接着的语句。
Lua repeat...until 循环流程图以下:
--[ 变量定义 --] a = 10 --[ 执行循环 --] repeat print("a的值为:", a) a = a + 1 until( a > 15 )
执行以上代码,程序输出结果为:
a的值为: 10 a的值为: 11 a的值为: 12 a的值为: 13 a的值为: 14 a的值为: 15
Lua 编程语言中容许循环中嵌入循环。如下实例演示了 Lua 循环嵌套的应用。
Lua 编程语言中 for 循环嵌套语法格式:
for init,max/min value, increment do for init,max/min value, increment do statements end statements end
Lua 编程语言中 while 循环嵌套语法格式:
while(condition) do while(condition) do statements end statements end
Lua 编程语言中 repeat...until 循环嵌套语法格式:
repeat statements repeat statements until( condition ) until( condition )
除了以上同类型循环嵌套外,咱们还能够使用不一样的循环类型来嵌套,如 for 循环体中嵌套 while 循环。
如下实例使用了for循环嵌套:
j =2 for i=2,10 do for j=2,(i/j) , 2 do if(not(i%j)) then break end if(j > (i/j))then print("i 的值为:",i) end end end
以上代码执行结果为:
i 的值为: 8 i 的值为: 9 i 的值为: 10
Lua 编程语言 break 语句插入在循环体中,用于退出当前循环或语句,并开始脚本执行紧接着的语句。
若是你使用循环嵌套,break语句将中止最内层循环的执行,并开始执行的外层的循环语句。
Lua 编程语言中 break 语句语法格式:
break
流程图:
如下实例执行 while 循环,在变量 a 小于 20 时输出 a 的值,并在 a 大于 15 时终止执行循环:
--[ 定义变量 --] a = 10 --[ while 循环 --] while( a < 20 ) do print("a 的值为:", a) a=a+1 if( a > 15) then --[ 使用 break 语句终止循环 --] break end end
以上代码执行结果以下:
a 的值为: 10 a 的值为: 11 a 的值为: 12 a 的值为: 13 a 的值为: 14 a 的值为: 15
由 sf0501 建立,youj 最后一次修改 2015-09-05
Lua 编程语言流程控制语句经过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码,在条件为 false 时执行其余指定代码。
如下是典型的流程控制流程图:
控制结构的条件表达式结果能够是任何值,Lua认为false和nil为假,true 和非nil为真。
要注意的是Lua中 0 为 true:
--[ 0 为true ] if(0) then print("0 为真") end
以上代码输出结果为:
0 为真
Lua 提供了如下控制结构语句:
语句 | 描述 |
---|---|
if 语句 | if 语句 由一个布尔表达式做为条件判断,其后紧跟其余语句组成。 |
if...else 语句 | if 语句 能够与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码。 |
if 嵌套语句 | 你能够在if 或 else if中使用一个或多个 if 或 else if 语句 。 |
Lua if 语句 由一个布尔表达式做为条件判断,其后紧跟其余语句组成。
if(布尔表达式) then --[ 在布尔表达式为 true 时执行的语句 --] end
在布尔表达式为 true 时会if中的代码块会被执行,在布尔表达式为 false 时,紧跟在 if 语句 end 以后的代码会被执行。 Lua认为false和nil为假,true 和非nil为真。要注意的是Lua中 0 为 true。 if 语句流程图以下: 如下实例用于判断变量 a 的值是否小于 20:
--[ 定义变量 --] a = 10; --[ 使用 if 语句 --] if( a < 20 ) then --[ if 条件为 true 时打印如下信息 --] print("a 小于 20" ); end print("a 的值为:", a);
以上代码执行结果以下:
a 小于 20 a 的值为: 10
Lua if 语句能够与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码块。
Lua if...else 语句语法格式以下:
if(布尔表达式) then --[ 布尔表达式为 true 时执行该语句块 --] else --[ 布尔表达式为 false 时执行该语句块 --] end
在布尔表达式为 true 时会if中的代码块会被执行,在布尔表达式为 false 时,else 的代码块会被执行。
Lua认为false和nil为假,true 和非nil为真。要注意的是Lua中 0 为 true。
if 语句流程图以下:
如下实例用于判断变量 a 的值:
--[ 定义变量 --] a = 100; --[ 检查条件 --] if( a < 20 ) then --[ if 条件为 true 时执行该语句块 --] print("a 小于 20" ) else --[ if 条件为 false 时执行该语句块 --] print("a 大于 20" ) end print("a 的值为 :", a)
以上代码执行结果以下:
a 大于 20 a 的值为 : 100
Lua if 语句能够与 else if...else 语句搭配使用, 在 if 条件表达式为 false 时执行 else if...else 语句代码块,用于检测多个条件语句。
Lua if...else if...else 语句语法格式以下:
if( 布尔表达式 1) then --[ 在布尔表达式 1 为 true 时执行该语句块 --] else if( 布尔表达式 2) --[ 在布尔表达式 2 为 true 时执行该语句块 --] else if( 布尔表达式 3) --[ 在布尔表达式 3 为 true 时执行该语句块 --] else --[ 若是以上布尔表达式都不为 true 则执行该语句块 --] end
如下实例对变量 a 的值进行判断:
--[ 定义变量 --] a = 100 --[ 检查布尔条件 --] if( a == 10 ) then --[ 若是条件为 true 打印如下信息 --] print("a 的值为 10" ) elseif( a == 20 ) then --[ if else if 条件为 true 时打印如下信息 --] print("a 的值为 20" ) elseif( a == 30 ) then --[ if else if condition 条件为 true 时打印如下信息 --] print("a 的值为 30" ) else --[ 以上条件语句没有一个为 true 时打印如下信息 --] print("没有匹配 a 的值" ) end print("a 的真实值为: ", a )
以上代码执行结果以下:
没有匹配 a 的值 a 的真实值为: 100
Lua if 语句容许嵌套, 这就意味着你能够在一个 if 或 else if 语句中插入其余的 if 或 else if 语句。
if( 布尔表达式 1) then --[ 布尔表达式 1 为 true 时执行该语句块 --] if(布尔表达式 2) then --[ 布尔表达式 2 为 true 时执行该语句块 --] end end
你能够用一样的方式嵌套 else if...else 语句。如下实例用于判断变量 a 和 b 的值:
--[ 定义变量 --] a = 100; b = 200; --[ 检查条件 --] if( a == 100 ) then --[ if 条件为 true 时执行如下 if 条件判断 --] if( b == 200 ) then --[ if 条件为 true 时执行该语句块 --] print("a 的值为 100 b 的值为 200" ); end end print("a 的值为 :", a ); print("b 的值为 :", b );
以上代码执行结果以下:
a 的值为 100 b 的值为 200 a 的值为 : 100 b 的值为 : 200
由 sf0501 建立,陈 最后一次修改 2015-09-28
在Lua中,函数是对语句和表达式进行抽象的主要方法。既能够用来处理一些特殊的工做,也能够用来计算一些值。
Lua 提供了许多的内建函数,你能够很方便的在程序中调用它们,如print()函数能够将传入的参数打印在控制台上。
Lua 函数主要有两种用途:
Lua 编程语言函数定义格式以下:
optional_function_scope function function_name( argument1, argument2, argument3..., argumentn) function_body return result_params_comma_separated end
解析:
: 该参数是可选的指定函数是全局函数仍是局部函数,未设置该参数默认为全局函数,若是你须要设置函数为局部函数须要使用关键字
local。
指定函数名称。
函数参数,多个参数以逗号隔开,函数也能够不带参数。
函数体,函数中须要执行的代码语句块。
函数返回值,Lua语言函数能够返回多个值,每一个值以逗号隔开。
如下实例定义了函数 max(),参数为 num1, num2,用于比较两值的大小,并返回最大值:
--[[ 函数返回两个值的最大值 --]] function max(num1, num2) if (num1 > num2) then result = num1; else result = num2; end return result; end -- 调用函数 print("两值比较最大值为 ",max(10,4)) print("两值比较最大值为 ",max(5,6))
以上代码执行结果为:
两值比较最大值为 10 两值比较最大值为 6
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.w3cschool.cn", "w3cschool") > print(s, e) 5 13
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
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
由 sf0501 建立,youj 最后一次修改 2015-09-22
运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。Lua提供了如下几种运算符类型:
下表列出了 Lua 语言中的经常使用算术运算符,设定 A 的值为10,B 的值为 20:
操做符 | 描述 | 实例 |
---|---|---|
+ | 加法 | A + B 输出结果 30 |
- | 减法 | A - B 输出结果 -10 |
* | 乘法 | A * B 输出结果 200 |
/ | 除法 | B / A w输出结果 2 |
% | 取余 | B % A 输出结果 0 |
^ | 乘幂 | A^2 输出结果 100 |
- | 负号 | -A 输出结果v -10 |
咱们能够经过如下实例来更加透彻的理解算术运算符的应用:
a = 21 b = 10 c = a + b print("Line 1 - c 的值为 ", c ) c = a - b print("Line 2 - c 的值为 ", c ) c = a * b print("Line 3 - c 的值为 ", c ) c = a / b print("Line 4 - c 的值为 ", c ) c = a % b print("Line 5 - c 的值为 ", c ) c = a^2 print("Line 6 - c 的值为 ", c ) c = -a print("Line 7 - c 的值为 ", c )
以上程序执行结果为:
Line 1 - c 的值为 31 Line 2 - c 的值为 11 Line 3 - c 的值为 210 Line 4 - c 的值为 2.1 Line 5 - c 的值为 1 Line 6 - c 的值为 441 Line 7 - c 的值为 -21
下表列出了 Lua 语言中的经常使用关系运算符,设定 A 的值为10,B 的值为 20:
操做符 | 描述 | 实例 |
---|---|---|
== | 等于,检测两个值是否相等,相等返回 true,不然返回 false | (A == B) 为 false。 |
~= | 不等于,检测两个值是否相等,相等返回 false,不然返回 true< | (A ~= B) 为 true。 |
> | 大于,若是左边的值大于右边的值,返回 true,不然返回 false | (A > B) 为 false。 |
< | 小于,若是左边的值大于右边的值,返回 false,不然返回 true | (A < B) 为 true。 |
>= | 大于等于,若是左边的值大于等于右边的值,返回 true,不然返回 false | (A >= B) is not true. |
<= | 小于等于, 若是左边的值小于等于右边的值,返回 true,不然返回 false | (A <= B) is true. |
咱们能够经过如下实例来更加透彻的理解关系运算符的应用:
a = 21 b = 10 if( a == b ) then print("Line 1 - a 等于 b" ) else print("Line 1 - a 不等于 b" ) end if( a ~= b ) then print("Line 2 - a 不等于 b" ) else print("Line 2 - a 等于 b" ) end if ( a < b ) then print("Line 3 - a 小于 b" ) else print("Line 3 - a 大于等于 b" ) end if ( a > b ) then print("Line 4 - a 大于 b" ) else print("Line 5 - a 小于等于 b" ) end -- 修改 a 和 b 的值 a = 5 b = 20 if ( a <= b ) then print("Line 5 - a 小于等于 b" ) end if ( b >= a ) then print("Line 6 - b 大于等于 a" ) end
以上程序执行结果为:
Line 1 - a 不等于 b Line 2 - a 不等于 b Line 3 - a 大于等于 b Line 4 - a 大于 b Line 5 - a 小于等于 b Line 6 - b 大于等于 a
下表列出了 Lua 语言中的经常使用逻辑运算符,设定 A 的值为 true,B 的值为 false:
操做符 | 描述 | 实例 |
---|---|---|
and | 逻辑与操做符。 若是两边的操做都为 true 则条件为 true。 | (A and B) 为 false。 |
or | 逻辑或操做符。 若是两边的操做任一一个为 true 则条件为 true。 | (A or B) 为 true。 |
not | 逻辑非操做符。与逻辑运算结果相反,若是条件为 true,逻辑非为 false。 | not(A and B) 为 true。 |
实例
咱们能够经过如下实例来更加透彻的理解逻辑运算符的应用:
a = true b = true if ( a and b ) then print("a and b - 条件为 true" ) end if ( a or b ) then print("a or b - 条件为 true" ) end print("---------分割线---------" ) -- 修改 a 和 b 的值 a = false b = true if ( a and b ) then print("a and b - 条件为 true" ) else print("a and b - 条件为 false" ) end if ( not( a and b) ) then print("not( a and b) - 条件为 true" ) else print("not( a and b) - 条件为 false" ) end
以上程序执行结果为:
a and b - 条件为 true a or b - 条件为 true ---------分割线--------- a and b - 条件为 false not( a and b) - 条件为 true
下表列出了 Lua 语言中的链接运算符与计算表或字符串长度的运算符:
操做符 | 描述 | 实例 |
---|---|---|
.. | 链接两个字符串 | a..b ,其中 a 为 "Hello " , b 为 "World", 输出结果为 "Hello World"。 |
# | 一元运算符,返回字符串或表的长度。 | #"Hello" 返回 5 |
咱们能够经过如下实例来更加透彻的理解链接运算符与计算表或字符串长度的运算符的应用:
a = "Hello " b = "World" print("链接字符串 a 和 b ", a..b ) print("b 字符串长度 ",#b ) print("字符串 Test 长度 ",#"Test" ) print("w3cschool在线教程网址长度 ",#"www.w3cschool.cn" )
以上程序执行结果为:
链接字符串 a 和 b Hello World b 字符串长度 5 字符串 Test 长度 4 w3cschool在线教程网址长度 16
从高到低的顺序:
^ not - (unary) * / + - .. < > <= >= ~= == and or
除了^和..外全部的二元运算符都是左链接的。
a+i < b/2+1 <--> (a+i) < ((b/2)+1) 5+x^2*8 <--> 5+((x^2)*8) a < y and y <= z <--> (a < y) and (y <= z) -x^2 <--> -(x^2) x^y^z <--> x^(y^z)
咱们能够经过如下实例来更加透彻的了解 Lua 语言运算符的优先级:
a = 20 b = 10 c = 15 d = 5 e = (a + b) * c / d;-- ( 30 * 15 ) / 5 print("(a + b) * c / d 运算值为 :",e ) e = ((a + b) * c) / d; -- (30 * 15 ) / 5 print("((a + b) * c) / d 运算值为 :",e ) e = (a + b) * (c / d);-- (30) * (15/5) print("(a + b) * (c / d) 运算值为 :",e ) e = a + (b * c) / d; -- 20 + (150/5) print("a + (b * c) / d 运算值为 :",e )
以上程序执行结果为:
(a + b) * c / d 运算值为 : 90.0 ((a + b) * c) / d 运算值为 : 90.0 (a + b) * (c / d) 运算值为 : 90.0 a + (b * c) / d 运算值为 : 50.0
由 sf0501 建立,youj 最后一次修改 2015-09-26
字符串或串(String)是由数字、字母、下划线组成的一串字符。
Lua 语言中字符串能够使用如下三种方式来表示:
以上三种方式的字符串实例以下:
string1 = "Lua" print("\"字符串 1 是\"",string1) string2 = 'w3cschool.cn' print("字符串 2 是",string2) string3 = [["Lua 教程"]] print("字符串 3 是",string3)
以上代码执行输出结果为:
"字符串 1 是" Lua 字符串 2 是 w3cschool.cn 字符串 3 是 "Lua 教程"
转义字符用于表示不能直接显示的字符,好比后退键,回车键,等。如在字符串转换双引号能够使用 """。
全部的转义字符和所对应的意义:
转义字符 | 意义 | ASCII码值(十进制) |
---|---|---|
\a | 响铃(BEL) | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\ | 表明一个反斜线字符''' | 092 |
' | 表明一个单引号(撇号)字符 | 039 |
" | 表明一个双引号字符 | 034 |
空字符(NULL) | 000 | |
\ddd | 1到3位八进制数所表明的任意字符 | 三位八进制 |
\xhh | 1到2位十六进制所表明的任意字符 | 二位十六进制 |
Lua 提供了不少的方法来支持字符串的操做:
序号 | 方法 & 用途 |
---|---|
1 | string.upper(argument): 字符串所有转为大写字母。 |
2 | string.lower(argument): 字符串所有转为小写字母。 |
3 | string.gsub(mainString,findString,replaceString,num) 在字符串中替换,mainString为要替换的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(能够忽略,则所有替换),如: > string.gsub("aaaa","a","z",3); zzza 3 |
4 | string.find (str, substr, [init, [end]]) 在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil。 > string.find("Hello Lua user", "Lua", 1) 7 9 |
5 | string.reverse(arg) 字符串反转> string.reverse("Lua") auL |
6 | string.format(...) 返回一个相似printf的格式化字符串 > string.format("the value is:%d",4) the value is:4 |
7 | string.char(arg) 和 string.byte(arg[,int]) char 将整型数字转成字符并链接, byte 转换字符为整数值(能够指定某个字符,默认第一个字符)。 > string.char(97,98,99,100) abcd string.byte("ABCD",4) 68 string.byte("ABCD") 65 |
8 | string.len(arg) 计算字符串长度。 string.len("abc") 3 |
9 | string.rep(string, n)) 返回字符串string的n个拷贝 > string.rep("abcd",2) abcdabcd |
10 | .. 连接两个字符串 > print("www.w3cschool"..".cn") www.w3cschool.cn |
如下实例演示了如何对字符串大小写进行转换:
string1 = "Lua"; print(string.upper(string1)) print(string.lower(string1))
以上代码执行结果为:
LUA lua
如下实例演示了如何对字符串进行查找与反转操做:
string = "Lua Tutorial" -- 查找字符串 print(string.find(string,"Tutorial")) reversedString = string.reverse(string) print("新字符串为",reversedString)
以上代码执行结果为:
5 12 新字符串为 lairotuT auL
如下实例演示了如何对字符串进行格式化操做:
string1 = "Lua" string2 = "Tutorial" number1 = 10 number2 = 20 -- 基本字符串格式化 print(string.format("基本格式化 %s %s",string1,string2)) -- 日期格式化 date = 2; month = 1; year = 2014 print(string.format("日期格式化 %02d/%02d/%03d", date, month, year)) -- 十进制格式化 print(string.format("%.4f",1/3))
以上代码执行结果为:
基本格式化 Lua Tutorial 日期格式化 02/01/2014 0.3333
如下实例演示了字符与整数相互转换:
-- 字符转换 -- 转换第一个字符 print(string.byte("Lua")) -- 转换第三个字符 print(string.byte("Lua",3)) -- 转换末尾第一个字符 print(string.byte("Lua",-1)) -- 第二个字符 print(string.byte("Lua",2)) -- 转换末尾第二个字符 print(string.byte("Lua",-2)) -- 整数 ASCII 码转换为字符 print(string.char(97))
以上代码执行结果为:
76 97 97 117 117 a
如下实例演示了其余字符串操做,如计算字符串长度,字符串链接,字符串复制等:
string1 = "www." string2 = "w3cschool" string3 = ".cn" -- 使用 .. 进行字符串链接 print("链接字符串",string1..string2..string3) -- 字符串长度 print("字符串长度 ",string.len(string2)) -- 字符串复制 2 次 repeatedString = string.rep(string2,2) print(repeatedString)
以上代码执行结果为:
链接字符串 www.w3cschool.cn 字符串长度 9 w3cschoolw3cschool
由 sf0501 建立, 最后一次修改 2015-09-29
数组,就是相同数据类型的元素按必定顺序排列的集合,能够是一维数组和多维数组。
Lua 数组的索引键值能够使用整数表示,数组的大小不是固定的。
一维数组是最简单的数组,其逻辑结构是线性表。一维数组能够用for循环出数组中的元素,以下实例:
array = {"Lua", "Tutorial"} for i= 0, 2 do print(array[i]) end
以上代码执行输出结果为:
nil Lua Tutorial
正如你所看到的,咱们能够使用整数索引来访问数组元素,若是知道的索引没有值则返回nil。
在 Lua 索引值是以 1 为起始,但你也能够指定 0 开始。
除此外咱们还能够以负数为数组索引值:
array = {} for i= -2, 2 do array[i] = i *2 end for i = -2,2 do print(array[i]) end
以上代码执行输出结果为:
-4 -2 0 2 4
多维数组即数组中包含数组或一维数组的索引键对应一个数组。
如下是一个三行三列的阵列多维数组:
-- 初始化数组 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
以上代码执行输出结果为:
1 2 3 2 4 6 3 6 9
不一样索引键的三行三列阵列多维数组:
-- 初始化数组 array = {} maxRows = 3 maxColumns = 3 for row=1,maxRows do for col=1,maxColumns do array[row*maxColumns +col] = row*col end end -- 访问数组 for row=1,maxRows do for col=1,maxColumns do print(array[row*maxColumns +col]) end end
以上代码执行输出结果为:
1 2 3 2 4 6 3 6 9
正如你所看到的,以上的实例中,数组设定了指定的索引值,这样能够避免出现 nil 值,有利于节省内存空间。
由 sf0501 建立,youj 最后一次修改 2015-09-27
迭代器(iterator)是一种对象,它可以用来遍历标准模板库容器中的部分或所有元素,每一个迭代器对象表明容器中的肯定的地址
在Lua中迭代器是一种支持指针类型的结构,它能够遍历集合的每个元素。
泛型 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的执行过程:
。在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 内使用了闭包函数,实现计算集合大小并输出各个元素。
由 sf0501 建立,youj 最后一次修改 2015-09-16
table 是 Lua 的一种数据结构用来帮助咱们建立不一样的数据类型,如:数字、字典等。
Lua table 使用关联型数组,你能够用任意类型的值来做数组的索引,但这个值不能是 nil。
Lua table 是不固定大小的,你能够根据本身须要进行扩容。
Lua也是经过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
构造器是建立和初始化表的表达式。表是Lua特有的功能强大的东西。最简单的构造函数是{},用来建立一个空表。能够直接初始化数组:
-- 初始化表 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
如下列出了 Table 操做经常使用的方法:
序号 | 方法 & 用途 |
---|---|
1 | table.concat (table [, step [, start [, end]]]):concat是concatenate(连锁, 链接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的全部元素, 元素间以指定的分隔符(sep)隔开。 |
2 | table.insert (table, [pos,] value):在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾. |
3 | table.maxn (table)指定table中全部正数key值中最大的key值. 若是不存在key值为正数的元素, 则返回0。(Lua5.2以后该方法已经不存在了,本文使用了自定义函数实现) |
4 | table.remove (table [, pos])返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。 |
5 | table.sort (table [, comp])对给定的table进行升序排序。 |
接下来咱们来看下这几个方法的实例。
咱们能够使用 concat() 方法来链接两个 table:
fruits = {"banana","orange","apple"} -- 返回 table 链接后的字符串 print("链接后的字符串 ",table.concat(fruits)) -- 指定链接字符 print("链接后的字符串 ",table.concat(fruits,", ")) -- 指定索引来链接 table print("链接后的字符串 ",table.concat(fruits,", ", 2,3))
执行以上代码输出结果为:
链接后的字符串 bananaorangeapple 链接后的字符串 banana, orange, apple 链接后的字符串 orange, apple
如下实例演示了 table 的插入和移除操做:
fruits = {"banana","orange","apple"} -- 在末尾插入 table.insert(fruits,"mango") print("索引为 4 的元素为 ",fruits[4]) -- 在索引为 2 的键处插入 table.insert(fruits,2,"grapes") print("索引为 2 的元素为 ",fruits[2]) print("最后一个元素为 ",fruits[5]) table.remove(fruits) print("移除后最后一个元素为 ",fruits[5])
执行以上代码输出结果为:
索引为 4 的元素为 mango 索引为 2 的元素为 grapes 最后一个元素为 mango 移除后最后一个元素为 nil
如下实例演示了 sort() 方法的使用,用于对 Table 进行排序:
fruits = {"banana","orange","apple","grapes"} print("排序前") for k,v in ipairs(fruits) do print(k,v) end table.sort(fruits) print("排序后") for k,v in ipairs(fruits) do print(k,v) end
执行以上代码输出结果为:
排序前 1 banana 2 orange 3 apple 4 grapes 排序后 1 apple 2 banana 3 grapes 4 orange
table.maxn 在 Lua5.2 以后该方法已经不存在了,咱们定义了 table_maxn 方法来实现。
如下实例演示了如何获取 table 中的最大值:
function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end tbl = {[1] = "a", [2] = "b", [3] = "c", [26] = "z"} print("tbl 长度 ", #tbl) print("tbl 最大值 ", table_maxn(tbl))
执行以上代码输出结果为:
tbl 长度 3 tbl 最大值 26
由 sf0501 建立, 最后一次修改 2015-09-08
模块相似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,能够把一些公用的代码放在一个文件里,以 API 接口的形式在其余地方调用,有利于代码的重用和下降代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,所以建立一个模块很简单,就是建立一个 table,而后把须要导出的常量、函数放入其中,最后返回这个 table 就行。如下为建立自定义模块 module.lua,文件代码格式以下:
-- 文件名为 module.lua -- 定义一个名为 module 的模块 module = {} -- 定义一个常量 module.constant = "这是一个常量" -- 定义一个函数 function module.func1() io.write("这是一个公有函数!\n") end local function func2() print("这是一个私有函数!") end function module.func3() func2() end return module
由上可知,模块的结构就是一个 table 的结构,所以能够像操做调用 table 里的元素那样来操做调用模块里的常量或函数。
上面的 func2 声明为程序块的局部变量,即表示一个私有函数,所以是不能从外部访问模块里的这个私有函数,必须经过模块里的公有函数来调用.
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只须要简单地调用就能够了。例如:
require("<模块名>")
或者
require "<模块名>"
执行 require 后会返回一个由模块常量或函数组成的 table,而且还会定义一个包含该 table 的全局变量。
-- test_module.php 文件 -- module 模块为上文提到到 module.lua require("module") print(module.constant) module.func3()
以上代码执行结果为:
这是一个常量 这是一个私有函数!
或者给加载的模块定义一个别名变量,方便调用:
-- test_module2.php 文件 -- module 模块为上文提到到 module.lua -- 别名变量 m local m = require("module") print(m.constant) m.func3()
以上代码执行结果为:
这是一个常量 这是一个私有函数!
对于自定义的模块,模块文件不是放在哪一个文件目录都行,函数 require 有它本身的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。若是没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
固然,若是没有 LUA_PATH 这个环境变量,也能够自定义设置,在当前用户根目录下打开 .profile 文件(没有则建立,打开 .bashrc 文件也能够),例如把 "~/lua/" 路径加入 LUA_PATH 环境变量里:
#LUA_PATH export LUA_PATH="~/lua/?.lua;;"
文件路径以 ";" 号分隔,最后的 2 个 ";;" 表示新加的路径后面加上原来的默认路径。
接着,更新环境变量参数,使之当即生效。
source ~/.profile
这时假设 package.path 的值是:
/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua
那么调用 require("module") 时就会尝试打开如下文件目录去搜索目标。
/Users/dengjoe/lua/module.lua; ./module.lua /usr/local/share/lua/5.1/module.lua /usr/local/share/lua/5.1/module/init.lua /usr/local/lib/lua/5.1/module.lua /usr/local/lib/lua/5.1/module/init.lua
若是找过目标文件,则会调用 package.loadfile 来加载模块。不然,就会去找 C 程序库。
搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是经过环境变量 LUA_CPATH 来初始。
搜索的策略跟上面的同样,只不过如今换成搜索的是 so 或 dll 类型的文件。若是找获得,那么 require 就会经过 package.loadlib 来加载它。
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库了。
由 sf0501 建立,youj 最后一次修改 2015-09-07
在 Lua table 中咱们能够访问对应的key来获得value值,可是却没法对两个 table 进行操做。
所以 Lua 提供了元表(Metatable),容许咱们改变table的行为,每一个行为关联了对应的元方法。
例如,使用元表咱们能够定义Lua如何计算两个table的相加操做a+b。
当Lua试图对两个表进行相加时,先检查二者之一是否有元表,以后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add"等即时字段,其对应的值(每每是一个函数或是table)就是"元方法"。
有两个很重要的函数来处理元表:
如下实例演示了如何对指定的表设置元表:
mytable = {} -- 普通表 mymetatable = {} -- 元表 setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
以上代码也能够直接写成一行:
mytable = setmetatable({},{})
如下为返回对象元表:
getmetatable(mytable) -- 这回返回mymetatable
这是 metatable 最经常使用的键。
当你经过键来访问 table 的时候,若是这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。若是__index包含一个表格,Lua会在表格中查找相应的键。
咱们能够在使用 lua 命令进入交互模式查看:
$ lua Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio > other = { foo = 3 } > t = setmetatable({}, { __index = other }) > t.foo 3 > t.bar nil
若是__index包含一个函数的话,Lua就会调用那个函数,table和键会做为参数传递给函数。
__index 元方法查看表中元素是否存在,若是不存在,返回结果为 nil;若是存在则由 __index 返回结果。
mytable = setmetatable({key1 = "value1"}, { __index = function(mytable, key) if key == "key2" then return "metatablevalue" else return nil end end }) print(mytable.key1,mytable.key2)
实例输出结果为:
value1 metatablevalue
实例解析:
mytable 表赋值为 {key1 = "value1"}。
mytable 设置了元表,元方法为 __index。
在mytable表中查找 key1,若是找到,返回该元素,找不到则继续。
在mytable表中查找 key2,若是找到,返回该元素,找不到则继续。
判断元表有没有__index方法,若是__index方法是一个函数,则调用该函数。
元方法中查看是否传入 "key2" 键的参数(mytable.key2已设置),若是传入 "key2" 参数返回 "metatablevalue",不然返回 mytable 对应的键值。
咱们能够将以上代码简单写成:
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } }) print(mytable.key1,mytable.key2)
__newindex 元方法用来对表更新,__index则用来对表访问 。
当你给表的一个缺乏的索引赋值,解释器就会查找__newindex 元方法:若是存在则调用这个函数而不进行赋值操做。
如下实例演示了 __newindex 元方法的应用:
mymetatable = {} mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable }) print(mytable.key1) mytable.newkey = "新值2" print(mytable.newkey,mymetatable.newkey) mytable.key1 = "新值1" print(mytable.key1,mymetatable.newkey1)
以上实例执行输出结果为:
value1 nil 新值2 新值1 nil
以上实例中表设置了元方法 __newindex,在对新索引键(newkey)赋值时(mytable.newkey = "新值2"),会调用元方法,而不进行赋值。而若是对已存在的索引键(key1),则会进行赋值,而不调用元方法 __newindex。
如下实例使用了 rawset 函数来更新表:
mytable = setmetatable({key1 = "value1"}, { __newindex = function(mytable, key, value) rawset(mytable, key, "\""..value.."\"") end }) mytable.key1 = "new value" mytable.key2 = 4 print(mytable.key1,mytable.key2)
以上实例执行输出结果为:
new value "4"
如下实例演示了两表相加操做:
-- 计算表中最大值,table.maxn在Lua5.2以上版本中已没法使用 -- 自定义计算表中最大值函数 table_maxn function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end -- 两表相加操做 mytable = setmetatable({ 1, 2, 3 }, { __add = function(mytable, newtable) for i = 1, table_maxn(newtable) do table.insert(mytable, table_maxn(mytable)+1,newtable[i]) end return mytable end }) secondtable = {4,5,6} mytable = mytable + secondtable for k,v in ipairs(mytable) do print(k,v) end
以上实例执行输出结果为:
1 1 2 2 3 3 4 4 5 5 6 6
__add 键包含在元表中,并进行相加操做。 表中对应的操做列表以下:
模式 | 描述 |
---|---|
__add | 对应的运算符 '+'. |
__sub | 对应的运算符 '-'. |
__mul | 对应的运算符 '*'. |
__div | 对应的运算符 '/'. |
__mod | 对应的运算符 '%'. |
__unm | 对应的运算符 '-'. |
__concat | 对应的运算符 '..'. |
__eq | 对应的运算符 '=='. |
__lt | 对应的运算符 '<'. |
__le | 对应的运算符 '<='. |
__call 元方法在 Lua 调用一个值时调用。如下实例演示了计算表中元素的和:
-- 计算表中最大值,table.maxn在Lua5.2以上版本中已没法使用 -- 自定义计算表中最大值函数 table_maxn function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end -- 定义元方法__call mytable = setmetatable({10}, { __call = function(mytable, newtable) sum = 0 for i = 1, table_maxn(mytable) do sum = sum + mytable[i] end for i = 1, table_maxn(newtable) do sum = sum + newtable[i] end return sum end }) newtable = {10,20,30} print(mytable(newtable))
以上实例执行输出结果为:
70
__tostring 元方法用于修改表的输出行为。如下实例咱们自定义了表的输出内容:
mytable = setmetatable({ 10, 20, 30 }, { __tostring = function(mytable) sum = 0 for k, v in pairs(mytable) do sum = sum + v end return "表全部元素的和为 " .. sum end }) print(mytable)
以上实例执行输出结果为:
表全部元素的和为 60
从本文中咱们能够知道元表能够很好的简化咱们的代码功能,因此了解 Lua 的元表,可让咱们写出更加简单优秀的 Lua 代码。
由 sf0501 建立, 最后一次修改 2015-09-26
Lua 协同程序(coroutine)与线程比较相似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。
协同是很是强大的功能,可是用起来也很复杂。
线程与协同程序的主要区别在于,一个具备多个线程的程序能够同时运行几个线程,而协同程序却须要彼此协做的运行。
在任一指定时刻只有一个协同程序在运行,而且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。
协同程序有点相似同步的多线程,在等待同一个线程锁的几个线程有点相似协同。
方法 | 描述 |
---|---|
coroutine.create() | 建立coroutine,返回coroutine, 参数是一个函数,当和resume配合使用的时候就唤醒函数调用 |
coroutine.resume() | 重启coroutine,和create配合使用 |
coroutine.yield() | 挂起coroutine,将coroutine设置为挂起状态,这个和resume配合使用能有不少有用的效果 |
coroutine.status() | 查看coroutine的状态 注:coroutine的状态有三种:dead,suspend,running,具体何时有这样的状态请参考下面的程序 |
coroutine.wrap() | 建立coroutine,返回一个函数,一旦你调用这个函数,就进入coroutine,和create功能重复 |
coroutine.running() | 返回正在跑的coroutine,一个coroutine就是一个线程,当使用running的时候,就是返回一个corouting的线程号 |
-- coroutine_test.lua 文件 co = coroutine.create( function(i) print(i); end ) coroutine.resume(co, 1) -- 1 print(coroutine.status(co)) -- dead print("----------") co = coroutine.wrap( function(i) print(i); end ) co(1) print("----------") co2 = coroutine.create( function() for i=1,10 do print(i) if i == 3 then print(coroutine.status(co2)) --running print(coroutine.running()) --thread:XXXXXX end coroutine.yield() end end ) coroutine.resume(co2) --1 coroutine.resume(co2) --2 coroutine.resume(co2) --3 print(coroutine.status(co2)) -- suspended print(coroutine.running()) --nil print("----------")
以上实例执行输出结果为:
1 dead ---------- 1 ---------- 1 2 3 running thread: 0x7fb801c05868 false suspended thread: 0x7fb801c04c88 true ----------
coroutine.running就能够看出来,coroutine在底层实现就是一个线程。
当create一个coroutine的时候就是在新线程中注册了一个事件。
当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就表明挂起当前线程,等候再次resume触发事件。
接下来咱们分析一个更详细的实例:
function foo (a) print("foo 函数输出", a) return coroutine.yield(2 * a) -- 返回 2*a 的值 end co = coroutine.create(function (a , b) print("第一次协同程序执行输出", a, b) -- co-body 1 10 local r = foo(a + 1) print("第二次协同程序执行输出", r) local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入 print("第三次协同程序执行输出", r, s) return b, "结束协同程序" -- b的值为第二次调用协同程序时传入 end) print("main", coroutine.resume(co, 1, 10)) -- true, 4 print("--分割线----") print("main", coroutine.resume(co, "r")) -- true 11 -9 print("---分割线---") print("main", coroutine.resume(co, "x", "y")) -- true 10 end print("---分割线---") print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine print("---分割线---")
以上实例执行输出结果为:
第一次协同程序执行输出 1 10 foo 函数输出 2 main true 4 --分割线---- 第二次协同程序执行输出 r main true 11 -9 ---分割线--- 第三次协同程序执行输出 x y main true 10 结束协同程序 ---分割线--- main false cannot resume dead coroutine ---分割线---
以上实例接下以下:
resume和yield的配合强大之处在于,resume处于主程中,它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。
如今我就使用Lua的协同程序来完成生产者-消费者这一经典问题。
local newProductor function productor() local i = 0 while true do i = i + 1 send(i) -- 将生产的物品发送给消费者 end end function consumer() while true do local i = receive() -- 从生产者那里获得物品 print(i) end end function receive() local status, value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) -- x表示须要发送的值,值返回之后,就挂起该协同程序 end -- 启动程序 newProductor = coroutine.create(productor) consumer() local newProductor function productor() local i = 0 while true do i = i + 1 send(i) -- 将生产的物品发送给消费者 end end function consumer() while true do local i = receive() -- 从生产者那里获得物品 print(i) end end function receive() local status, value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) -- x表示须要发送的值,值返回之后,就挂起该协同程序 end -- 启动程序 newProductor = coroutine.create(productor) consumer()
以上实例执行输出结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13 ……
由 sf0501 建立, 最后一次修改 2015-09-09
Lua I/O 库用于读取和处理文件。分为简单模式(和C同样)、彻底模式。
简单模式在作一些简单的文件操做时较为合适。可是在进行一些高级的文件操做的时候,简单模式就显得力不从心。例如同时读取多个文件这样的操做,使用彻底模式则较为合适。
打开文件操做语句以下:
file = io.open (filename [, mode])
mode 的值有:
模式 | 描述 |
---|---|
r | 以只读方式打开文件,该文件必须存在。 |
w | 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则创建该文件。 |
a | 以附加的方式打开只写文件。若文件不存在,则会创建该文件,若是文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留) |
r+ | 以可读写方式打开文件,该文件必须存在。 |
w+ | 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则创建该文件。 |
a+ | 与a相似,但此文件可读可写 |
b | 二进制模式,若是文件是二进制文件,能够加上b |
+ | 号表示对文件既能够读也能够写 |
简单模式使用标准的 I/O 或使用一个当前输入文件和一个当前输出文件。
如下为 file.lua 文件代码,操做的文件为test.lua(若是没有你须要建立该文件),代码以下:
-- 以只读方式打开文件 file = io.open("test.lua", "r") -- 设置默认输入文件为 test.lua io.input(file) -- 输出文件第一行 print(io.read()) -- 关闭打开的文件 io.close(file) -- 以附加的方式打开只写文件 file = io.open("test.lua", "a") -- 设置默认输出文件为 test.lua io.output(file) -- 在文件最后一行添加 Lua 注释 io.write("-- test.lua 文件末尾注释") -- 关闭打开的文件 io.close(file)
执行以上代码,你会发现,输出了 test.ua 文件的第一行信息,并在该文件最后一行添加了 lua 的注释。如我这边输出的是:
-- test.lua 文件
在以上实例中咱们使用了 io."x" 方法,其中 io.read() 中咱们没有带参数,参数能够是下表中的一个:
模式 | 描述 |
---|---|
"*n" | 读取一个数字并返回它。例:file.read("*n") |
"*a" | 从当前位置读取整个文件。例:file.read("*a") |
"*l"(默认) | 读取下一行,在文件尾 (EOF) 处返回 nil。例:file.read("*l") |
number | 返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:file.read(5) |
其余的 io 方法有:
一般咱们须要在同一时间处理多个文件。咱们须要使用 file:function_name 来代替 io.function_name 方法。如下实例演示了如何同时处理同一个文件:
-- 以只读方式打开文件 file = io.open("test.lua", "r") -- 输出文件第一行 print(file:read()) -- 关闭打开的文件 file:close() -- 以附加的方式打开只写文件 file = io.open("test.lua", "a") -- 在文件最后一行添加 Lua 注释 file:write("--test") -- 关闭打开的文件 file:close()
执行以上代码,你会发现,输出了 test.ua 文件的第一行信息,并在该文件最后一行添加了 lua 的注释。如我这边输出的是:
-- test.lua 文件
read 的参数与简单模式一致。
其余方法:
file:seek(optional whence, optional offset): 设置和获取当前文件位置,成功则返回最终的文件位置(按字节),失败则返回nil加错误信息。参数 whence 值能够是:
不带参数file:seek()则返回当前位置,file:seek("set")则定位到文件头,file:seek("end")则定位到文件尾并返回文件大小
file:flush(): 向文件写入缓冲中的全部数据
io.lines(optional file name): 打开指定的文件filename为读模式并返回一个迭代函数,每次调用将得到文件中的一行内容,当到文件尾时,将返回nil,并自动关闭文件。
若不带参数时io.lines() io.input():lines(); 读取默认输入设备的内容,但结束时不关闭文件,如
for line in io.lines("main.lua") do print(line) end
如下实例使用了 seek 方法,定位到文件倒数第 25 个位置并使用 read 方法的 *a 参数,即从当期位置(倒数第 25 个位置)读取整个文件。
-- 以只读方式打开文件 file = io.open("test.lua", "r") file:seek("end",-25) print(file:read("*a")) -- 关闭打开的文件 file:close()
我这边输出的结果是:
st.lua 文件末尾--test
由 sf0501 建立,youj 最后一次修改 2015-09-12
程序运行中错误处理是必要的,在咱们进行文件操做,数据转移及web service 调用过程当中都会出现不可预期的错误。若是不注重错误信息的处理,就会形成信息泄露,程序没法运行等状况。
任何程序语言中,都须要错误处理。错误类型有:
语法错误一般是因为对程序的组件(如运算符、表达式)使用不当引发的。一个简单的实例以下:
-- test.lua 文件 a == 2
以上代码执行结果为:
lua: test.lua:2: syntax error near '=='
正如你所看到的,以上出现了语法错误,一个 "=" 号跟两个 "=" 号是有区别的。一个 "=" 是赋值表达式两个 "=" 是比较运算。
另一个实例:
for a= 1,10 print(a) end
执行以上程序会出现以下错误:
lua: test2.lua:2: 'do' expected near 'print'
语法错误比程序运行错误更简单,运行错误没法定位具体错误,而语法错误咱们能够很快的解决,如以上实例咱们只要在for语句下添加 do 便可:
for a= 1,10 do print(a) end
运行错误是程序能够正常执行,可是会输出报错信息。以下实例因为参数输入错误,程序执行时报错:
function add(a,b) return a+b end add(10)
当咱们编译运行如下代码时,编译是能够成功的,但在运行的时候会产生以下错误:
lua: test2.lua:2: attempt to perform arithmetic on local 'b' (a nil value) stack traceback: test2.lua:2: in function 'add' test2.lua:5: in main chunk [C]: ?
如下报错信息是因为程序缺乏 b 参数引发的。
咱们能够使用两个函数:assert 和 error 来处理错误。实例以下:
local function add(a,b) assert(type(a) == "number", "a 不是一个数字") assert(type(b) == "number", "b 不是一个数字") return a+b end add(10)
执行以上程序会出现以下错误:
lua: test.lua:3: b 不是一个数字 stack traceback: [C]: in function 'assert' test.lua:3: in local 'add' test.lua:6: in main chunk [C]: in ?
实例中assert首先检查第一个参数,若没问题,assert不作任何事情;不然,assert以第二个参数做为错误信息抛出。
语法格式:
error (message [, level])
功能:终止正在执行的函数,并返回message的内容做为错误信息(error函数永远都不会返回)
一般状况下,error会附加一些错误位置的信息到message头部。
Level参数指示得到错误的位置:
Lua中处理错误,能够使用函数pcall(protected call)来包装须要执行的代码。
pcall接收一个函数和要传递个后者的参数,并执行,执行结果:有错误、无错误;返回值true或者或false, errorinfo。
语法格式以下
if pcall(function_name, ….) then -- 没有错误 else -- 一些错误 end
简单实例:
> =pcall(function(i) print(i) end, 33) 33 true > =pcall(function(i) print(i) error('error..') end, 33) 33 false stdin:1: error.. > function f() return false,2 end > if f() then print '1' else print '0' end 0
pcall以一种"保护模式"来调用第一个参数,所以pcall能够捕获函数执行中的任何错误。
一般在错误发生时,但愿落得更多的调试信息,而不仅是发生错误的位置。但pcall返回时,它已经销毁了调用桟的部份内容。
Lua提供了xpcall函数,xpcall接收第二个参数——一个错误处理函数,当错误发生时,Lua会在调用桟展看(unwind)前调用错误处理函数,因而就能够在这个函数中使用debug库来获取关于错误的额外信息了。
debug库提供了两个通用的错误处理函数:
>=xpcall(function(i) print(i) error('error..') end, function() print(debug.traceback()) end, 33) 33 stack traceback: stdin:1: in function [C]: in function 'error' stdin:1: in function [C]: in function 'xpcall' stdin:1: in main chunk [C]: in ? false nil
xpcall 使用实例 2:
function myfunction () n = n/nil end function myerrorhandler( err ) print( "ERROR:", err ) end status = xpcall( myfunction, myerrorhandler ) print( status)
执行以上程序会出现以下错误:
ERROR: test2.lua:2: attempt to perform arithmetic on global 'n' (a nil value) false
由 sf0501 建立, 最后一次修改 2015-09-26
Lua 提供了 debug 库用于提供建立咱们自定义调速器的功能。Lua 自己并未有内置的调速器,但不少开发者共享了他们的 Lua 调速器代码。
Lua 中 debug 库包含如下函数:
sethook ([thread,] hook, mask [, count]):
序号 | 方法 & 用途 |
---|---|
1. | debug(): 进入一个用户交互模式,运行用户输入的每一个字符串。 使用简单的命令以及其它调试设置,用户能够检阅全局变量和局部变量, 改变变量的值,计算一些表达式,等等。 输入一行仅包含 cont 的字符串将结束这个函数, 这样调用者就能够继续向下运行。 |
2. | getfenv(object): 返回对象的环境变量。 |
3. | gethook(optional thread): 返回三个表示线程钩子设置的值: 当前钩子函数,当前钩子掩码,当前钩子计数 |
4. | getinfo ([thread,] f [, what]): 返回关于一个函数信息的表。 你能够直接提供该函数, 也能够用一个数字 f 表示该函数。 数字 f 表示运行在指定线程的调用栈对应层次上的函数: 0 层表示当前函数(getinfo 自身); 1 层表示调用 getinfo 的函数 (除非是尾调用,这种状况不计入栈);等等。 若是 f 是一个比活动函数数量还大的数字, getinfo 返回 nil。 |
5. | debug.getlocal ([thread,] f, local): 此函数返回在栈的 f 层处函数的索引为 local 的局部变量 的名字和值。 这个函数不只用于访问显式定义的局部变量,也包括形参、临时变量等。 |
6. | getmetatable(value): 把给定索引指向的值的元表压入堆栈。若是索引无效,或是这个值没有元表,函数将返回 0 而且不会向栈上压任何东西。 |
7. | getregistry(): 返回注册表表,这是一个预约义出来的表, 能够用来保存任何 C 代码想保存的 Lua 值。 |
8. | getupvalue (f, up)此函数返回函数 f 的第 up 个上值的名字和值。 若是该函数没有那个上值,返回 nil 。 以 '(' (开括号)打头的变量名表示没有名字的变量 (去除了调试信息的代码块)。 |
10. | 将一个函数做为钩子函数设入。 字符串 mask 以及数字 count 决定了钩子将在什么时候调用。 掩码是由下列字符组合成的字符串,每一个字符有其含义: 'c': 每当 Lua 调用一个函数时,调用钩子; 'r': 每当 Lua 从一个函数内返回时,调用钩子; 'l': 每当 Lua 进入新的一行时,调用钩子。 |
11. | setlocal ([thread,] level, local, value): 这个函数将 value 赋给 栈上第 level 层函数的第 local 个局部变量。 若是没有那个变量,函数返回 nil 。 若是 level 越界,抛出一个错误。 |
12. | setmetatable (value, table): 将 value 的元表设为 table (能够是 nil)。 返回 value。 |
13. | setupvalue (f, up, value): 这个函数将 value 设为函数 f 的第 up 个上值。 若是函数没有那个上值,返回 nil 不然,返回该上值的名字。 |
14. | traceback ([thread,] [message [, level]]):若是 message 有,且不是字符串或 nil, 函数不作任何处理直接返回 message。 不然,它返回调用栈的栈回溯信息。 字符串可选项 message 被添加在栈回溯信息的开头。 数字可选项 level 指明从栈的哪一层开始回溯 (默认为 1 ,即调用 traceback 的那里)。 |
上表列出了咱们经常使用的调试函数,接下来咱们能够看些简单的例子:
function myfunction () print(debug.traceback("Stack trace")) print(debug.getinfo(1)) print("Stack trace end") return 10 end myfunction () print(debug.getinfo(1))
执行以上代码输出结果为:
Stack trace stack traceback: test2.lua:2: in function 'myfunction' test2.lua:8: in main chunk [C]: ? table: 0054C6C8 Stack trace end
在以实例中,咱们使用到了 debug 库的 traceback 和 getinfo 函数, getinfo 函数用于返回函数信息的表。
咱们常常须要调试函数的内的局部变量。咱们能够使用 getupvalue 函数来设置这些局部变量。实例以下:
function newCounter () local n = 0 local k = 0 return function () k = n n = n + 1 return n end end counter = newCounter () print(counter()) print(counter()) local i = 1 repeat name, val = debug.getupvalue(counter, i) if name then print ("index", i, name, "=", val) if(name == "n") then debug.setupvalue (counter,2,10) end i = i + 1 end -- if until not name print(counter())
执行以上代码输出结果为:
1 2 index 1 k = 1 index 2 n = 2 11
在以上实例中,计数器在每次调用时都会自增1。实例中咱们使用了 getupvalue 函数查看局部变量的当前状态。咱们能够设置局部变量为新值。实例中,在设置前 n 的值为 2,使用 setupvalue 函数将其设置为 10。如今咱们调用函数,执行后输出为 11 而不是 3。
命令行调试器有:RemDebug、clidebugger、ctrace、xdbLua、LuaInterface - Debugger、Rldb、ModDebug。
图形界调试器有:SciTE、Decoda、ZeroBrane Studio、akdebugger、luaedit。
由 sf0501 建立, 最后一次修改 2015-09-28
Lua 采用了自动内存管理。 这意味着你不用操心新建立的对象须要的内存如何分配出来, 也不用考虑在对象再也不被使用后怎样释放它们所占用的内存。
Lua 运行了一个垃圾收集器来收集全部死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工做。 Lua 中全部用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。
Lua 实现了一个增量标记-扫描收集器。 它使用这两个数字来控制垃圾收集循环: 垃圾收集器间歇率和垃圾收集器步进倍率。 这两个数字都使用百分数为单位 (例如:值 100 在内部表示 1 )。
垃圾收集器间歇率控制着收集器须要在开启新的循环前要等待多久。 增大这个值会减小收集器的积极性。 当这个值比 100 小的时候,收集器在开启新的循环前不会有等待。 设置这个值为 200 就会让收集器等到总内存使用量达到 以前的两倍时才开始新的循环。
垃圾收集器步进倍率控制着收集器运做速度相对于内存分配速度的倍率。 增大这个值不只会让收集器更加积极,还会增长每一个增量步骤的长度。 不要把这个值设得小于 100 , 那样的话收集器就工做的太慢了以致于永远都干不完一个循环。 默认值是 200 ,这表示收集器之内存分配的"两倍"速工做。
若是你把步进倍率设为一个很是大的数字 (比你的程序可能用到的字节数还大 10% ), 收集器的行为就像一个 stop-the-world 收集器。 接着你若把间歇率设为 200 , 收集器的行为就和过去的 Lua 版本同样了: 每次 Lua 使用的内存翻倍时,就作一次完整的收集。
Lua 提供了如下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:
如下演示了一个简单的垃圾回收实例:
mytable = {"apple", "orange", "banana"} print(collectgarbage("count")) mytable = nil print(collectgarbage("count")) print(collectgarbage("collect")) print(collectgarbage("count"))
执行以上程序,输出结果以下(注意内存使用的变化):
20.9560546875 20.9853515625 0 19.4111328125
由 sf0501 建立, 最后一次修改 2015-09-24
面向对象编程(Object Oriented Programming,OOP)是一种很是流行的计算机编程架构。
如下几种编程语言都支持面向对象编程:
咱们知道,对象由属性和方法组成。LUA中最基本的结构是table,因此须要用table来描述对象的属性。
lua中的function能够用来表示方法。那么LUA中的类能够经过table + function模拟出来。
至于继承,能够经过metetable模拟出来(不推荐用,只模拟最基本的对象大部分时间够用了)。
Lua中的表不只在某种意义上是一种对象。像对象同样,表也有状态(成员变量);也有与对象的值独立的本性,特别是拥有两个不一样值的对象(table)表明两个不一样的对象;一个对象在不一样的时候也能够有不一样的值,但他始终是一个对象;与对象相似,表的生命周期与其由什么建立、在哪建立没有关系。对象有他们的成员函数,表也有:
Account = {balance = 0} function Account.withdraw (v) Account.balance = Account.balance - v end
这个定义建立了一个新的函数,而且保存在Account对象的withdraw域内,下面咱们能够这样调用:
Account.withdraw(100.00)
如下简单的类包含了三个属性: area, length 和 breadth,printArea方法用于打印计算结果:
-- Meta class Rectangle = {area = 0, length = 0, breadth = 0} -- 派生类的方法 new function Rectangle:new (o,length,breadth) o = o or {} setmetatable(o, self) self.__index = self self.length = length or 0 self.breadth = breadth or 0 self.area = length*breadth; return o end -- 派生类的方法 printArea function Rectangle:printArea () print("矩形面积为 ",self.area) end
建立对象是位类的实例分配内存的过程。每一个类都有属于本身的内存并共享公共数据。
r = Rectangle:new(nil,10,20)
咱们能够使用点号(.)来访问类的属性:
print(r.length)
咱们能够使用冒号(:)来访问类的属性:
r:printArea()
内存在对象初始化时分配。
如下咱们演示了 Lua 面向对象的完整实例:
-- Meta class Shape = {area = 0} -- 基础类方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基础类方法 printArea function Shape:printArea () print("面积为 ",self.area) end -- 建立对象 myshape = Shape:new(nil,10) myshape:printArea()
执行以上程序,输出结果为:
面积为 100
继承是指一个对象直接使用另外一对象的属性和方法。可用于扩展基础类的属性和方法。
如下演示了一个简单的继承实例:
-- Meta class Shape = {area = 0} -- 基础类方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基础类方法 printArea function Shape:printArea () print("面积为 ",self.area) end
接下来的实例,Square 对象继承了 Shape 类:
Square = Shape:new() -- Derived class method new function Square:new (o,side) o = o or Shape:new(o,side) setmetatable(o, self) self.__index = self return o end
如下实例咱们继承了一个简单的类,来扩展派生类的方法,派生类中保留了继承类的成员变量和方法:
-- Meta class Shape = {area = 0} -- 基础类方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基础类方法 printArea function Shape:printArea () print("面积为 ",self.area) end -- 建立对象 myshape = Shape:new(nil,10) myshape:printArea() Square = Shape:new() -- 派生类方法 new function Square:new (o,side) o = o or Shape:new(o,side) setmetatable(o, self) self.__index = self return o end -- 派生类方法 printArea function Square:printArea () print("正方形面积为 ",self.area) end -- 建立对象 mysquare = Square:new(nil,10) mysquare:printArea() Rectangle = Shape:new() -- 派生类方法 new function Rectangle:new (o,length,breadth) o = o or Shape:new(o) setmetatable(o, self) self.__index = self self.area = length * breadth return o end -- 派生类方法 printArea function Rectangle:printArea () print("矩形面积为 ",self.area) end -- 建立对象 myrectangle = Rectangle:new(nil,10,20) myrectangle:printArea()
执行以上代码,输出结果为:
面积为 100 正方形面积为 100 矩形面积为 200
Lua 中咱们能够重写基础类的函数,在派生类中定义本身的实现方式:
-- 派生类方法 printArea function Square:printArea () print("正方形面积 ",self.area) end
由 sf0501 建立, 最后一次修改 2015-09-14
本文主要为你们介绍 Lua 数据库的操做库:LuaSQL。他是开源的,支持的数据库有:ODBC, ADO, Oracle, MySQL, SQLite 和 PostgreSQL。
本文为你们介绍MySQL的数据库链接。
LuaSQL 能够使用 LuaRocks 来安装能够根据须要安装你须要的数据库驱动。
LuaRocks 安装方法:
$ wget http://luarocks.org/releases/luarocks-2.2.1.tar.gz $ tar zxpf luarocks-2.2.1.tar.gz $ cd luarocks-2.2.1 $ ./configure; sudo make bootstrap $ sudo luarocks install luasocket $ lua Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio > require "socket"
Window 下安装 LuaRocks:https://github.com/keplerproject/luarocks/wiki/Installation-instructions-for-Windows
安装不一样数据库驱动:
luarocks install luasql-sqlite3 luarocks install luasql-postgres luarocks install luasql-mysql luarocks install luasql-sqlite luarocks install luasql-odbc
你也能够使用源码安装方式,Lua Github 源码地址:https://github.com/keplerproject/luasql
Lua 链接MySql 数据库:
require "luasql.mysql" --建立环境对象 env = luasql.mysql() --链接数据库 conn = env:connect("数据库名","用户名","密码","IP地址",端口) --设置数据库的编码格式 conn:execute"SET NAMES UTF8" --执行数据库操做 cur = conn:execute("select * from role") row = cur:fetch({},"a") --文件对象的建立 file = io.open("role.txt","w+"); while row do var = string.format("%d %s\n", row.id, row.name) print(var) file:write(var) row = cur:fetch(row,"a") end file:close() --关闭文件对象 conn:close() --关闭数据库链接 env:close() --关闭数据库环境
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
本文针对的读者是有经验的C/C++程序员,但愿了解Lua或者迅速抓住Lua的关键概念和模式进行开发的。所以本文并不打算教给读者条件语句的语法或者函数定义的方式等等显而易见的东西,以及一些诸如变量、函数等编程语言的基本概念。本文只打算告诉读者Lua那些与C/C++显著不一样的东西以及它们实际上带来了怎样不一样于C/C++的思考方式。不要小看它们,它们即将颠覆你传统的C/C++的世界观!
本文一共分初阶、进阶和高阶三大部分,每一个部分又有若干章节。读者应当从头到尾按部就班的阅读,可是标有“*”号的章节(主要讨论OO在Lua中的实现方式)能够略去而不影响对后面内容的理解。读者只要把前两部分完成就能够胜任Lua开发的绝大部分任务。高阶部分可做为选择。
基本类型 | 描述 | 备注 |
---|---|---|
数值(number) | 内部以double表示 | |
字符串(string) | 老是以零结尾,但能够包含任意字符(包括零),所以并不等价于C字符串, 而是其超集 | |
布尔(boolean) | 只有“true”或者“false”两个值。 | |
函数(function) | Lua的关键概念之一。不简单等同于C的函数或函数指针。 | |
表(table) | 异构的Hash表。Lua的关键概念之一。 | |
userdata | 用户(非脚本用户)定义的C数据结构。脚本用户只能使用它,不能定义。 | |
线程(thread) | Lua协做线程(coroutine),与通常操做系统的抢占式线程不同。 | |
nil | 表明什么也没有,能够与C的NULL做类比,但它不是空指针。 |
2.1 实例代码
function foo(a,b,c,...) local sum = a+b return sum,c --函数能够返回多个值 end r1,r2 = foo(1,"123","hello")--平行赋值 print(r1,r2);
输出结果:
124 hello
2.2 函数基本使用方法
用关键字function定义函数,以关键字end结束
用关键字local定义。若是没有用local定义,即便在函数内部定义的变量也是全局变量!
return a, b, c, ...
a, b = c, d
前面的代码定义了三个全局变量:foo、r1和r2
3.1 实现代码
local a = {} local b = {x = 1,["hello,"] = "world!"} a["astring"] = "ni,hao!" a[1] = 100 a["a table"] = b for k,v in pairs(a) do print(k,"=>",v); end
输出结果:
1=>100
astring=>ni,hao!
a table=>table: 0xfd59570
3.2 表使用方法
a = {}, b = {...}
经过“.”或者“[]”运算符来访问表的成员。
注意:表达式a.b等价于a[“b”],但不等价于a[b]
表项的键和值
任何类型的变量,除了nil,均可以作为表项的键。从简单的数值、字符串到复杂的函数、表等等均可以;一样,任何类型的变量,除了nil,均可以做为表项的值。给一个表项的值赋nil意味着从表中删除这一项,好比令a.b= nil,则把表a中键为“b”的项删除。若是访问一个不存在的表项,其值也是nil,好比有c = a.b,但表a中没有键为“b”的项,则c等于nil。
4.1 实现代码
function create(name,id) local obj = {name = name,id = id} function obj:SetName(name) self.name = name end function obj:GetName() return self.name end function obj:SetId(id) self.id = id end function obj:GetId() return self.id end return obj end local myCreate = create("sam",001) for k,v in pairs(myCreate) do print(k,"=>",v) end print("myCreate's name:",myCreate:GetName(),"myCreate's id:",myCreate.GetId(myCreate)) myCreate:SetId(100) myCreate:SetName("Hello Kity") print("myCreate's name:",myCreate:GetName(),"myCreate's id:",myCreate:GetId())
SetName=>function: 0x85efc50
GetId=>function: 0x85efc10
id=>1
SetId=>function: 0x85efd00
GetName=>function: 0x85efce0
name=>sam
myCreate's name:sammyCreate's id:1
myCreate's name:Hello KitymyCreate's id:100
4.2对象实现描述
如前面代码的create函数
把对象的数据和方法都放在一张表内,虽然没有隐藏私有成员,但对于简单脚原本说彻底能够接受。
成员方法的定义
function obj:method(a1, a2, ...) ... end 等价于function obj.method(self, a1, a2, ...) ... end 等价于obj.method = function (self, a1, a2, ...) ... end
成员方法的调用
obj:method(a1, a2, ...) 等价于obj.method(obj, a1, a2, ...)
5.1 实现代码
local function CreateRobot(name,id) local obj = {name = name,id = id} function obj:SetName(name) self.name = name end function obj:GetName() return self.name end function obj:SetId(id) self.id = id end function obj:GetId() return self.id end return obj end local function createFootballRobot(name ,id ,position) local obj = CreateRobot(name ,id) obj.position = "right back" function obj:SetPosition(p) self.position = p end function obj:GetPosition() return self.position end return obj end local mycreateFootballRobot = createFootballRobot("Tom",1000,"广州") print("mycreateFootballRobot's name:",mycreateFootballRobot:GetName(),"myCreate's id:",mycreateFootballRobot:GetId(),mycreateFootballRobot:GetPosition()) mycreateFootballRobot:SetName("麦迪") mycreateFootballRobot:SetId(2000) mycreateFootballRobot:SetPosition("北京") print("mycreateFootballRobot's name:",mycreateFootballRobot:GetName(),"myCreate's id:",mycreateFootballRobot:GetId(),mycreateFootballRobot:GetPosition())
输出结果:
mycreateFootballRobot's name:TommyCreate's id:1000right back
mycreateFootballRobot's name:麦迪myCreate's id:2000北京
5.2 简单继承优缺点
优势: 简单、直观
缺点: 传统、不够动态
参考文献《C/C++程序员的Lua快速入门》
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
1.1 实例代码
function createCountdownTimer(second) local ms = second * 1000 --ms为countDown的Upvalue local function countDown() ms = ms -1 return ms end return countDown end local timer1 = createCountdownTimer(1) for i = 1, 3 do print(timer1()) end
输出结果:
999
998
997
1.2 关于函数闭包描述
Upvalue
一个函数所使用的定义在它的函数体以外的局部变量(external local variable)称为这个函数的upvalue。 在前面的代码中,函数countDown使用的定义在函数createCountdownTimer 中的局部变量ms就是countDown的upvalue,但ms对createCountdownTimer而 言只是一个局部变量,不是upvalue。 Upvalue是Lua不一样于C/C++的特有属性,须要结合代码仔细体会。
函数闭包
一个函数和它所使用的全部upvalue构成了一个函数闭包。
Lua函数闭包使函数具备保持它本身的状态的能力,从这个意义上说,能够 与带静态局部变量的C函数相类比。但两者有显著的不一样:对Lua来讲,函数 是一种基本数据类型——表明一种(可执行)对象,能够有本身的状态;但 是对带静态局部变量的C函数来讲,它并非C的一种数据类型,更不会产生 什么对象实例,它只是一个静态地址的符号名称。
2.2 实例代码
local function create(name ,id ) local data = {name = name ,id = id} --data为obj.SetName,obj.GetName,obj.SetId,obj.GetId的Upvalue local obj = {} --把须要隐藏的成员放在一张表里,把该表做为成员函数的upvalue。 function obj.SetName(name) data.name = name end function obj.GetName() return data.name end function obj.SetId(id) data.id = id end function obj.GetId() return data.id end return obj end
输出结果:
mycreate's id:1mycreate's name:Sam
mycreate's id:1mycreate's name:Lucy
2.2 有关对象实现的描述
实现方式: 把须要隐藏的成员放在一张表里,把该表做为成员函数的upvalue。
局限性: 基于对象的实现不涉及继承及多态。但另外一方面,脚本编程是否须要继承和多态要视状况而定。
3.1 实例代码(1):
local t = {} local m = {a = "and",b = "Li Lei", c = "Han Meimei"} setmetatable(t,{__index = m}) --表{ __index=m }做为表t的元表 for k,v in pairs(t) do --穷举表t print("有值吗?") print(k,"=>",v) end print("-------------") print(t.b, t.a, t.c)
输出结果:
Li LeiandHan Meimei
3.2 实例代码(2):
local function add(t1,t2) --‘#’运算符取表长度 assert(#t1 == #t2) local length = #t1 for i = 1,length do t1[i] = t1[i] + t2[i] end return t1 end --setmetatable返回被设置的表 t1 = setmetatable({1,2,3},{__add = add}) t2 = setmetatable({10,20,30},{__add = add}) for k,v in pairs(t1) do print(k,"=>",v) end for k,v in pairs(t2) do print(k,"=>",v) end print("---------两元表相加--------------") t1 = t1 + t2 for i = 1 ,#t1 do print(t1[i]) end
输出结果:
1=>1
2=>2
3=>3
1=>10
2=>20
3=>30
---------两元表相加--------------
11
22
33
3.3 有关元表的描述:
定义 :
元表自己只是一个普通的表,经过特定的方法(好比setmetatable)设置到某个对象上,进而影响这个对象的行为;一个对象有哪些行为受到元表影响以及这些行为按照何种方式受到影响是受Lua语言约束的。好比在前面的代码里,两个表对象的加法运算,若是没有元表的干预,就是一种错误;可是Lua规定了元表能够“重载”对象的加法运算符,所以若把定义了加法运算的元表设置到那两个表上,它们就能够作加法了。元表是Lua最关键的概念之一,内容也很丰富,请参考Lua文档了解详情。
元表与C++虚表的比较:
若是把表比做对象,元表就是能够改变对象行为的“元”对象。在某种程度上,元表能够与C++的虚表作一类比。但两者仍是迥然不一样的:元表能够动态的改变,C++虚表是静态不变的;元表能够影响表(以及其余类型的对象)的不少方面的行为,虚表主要是为了定位对象的虚方法(最多再带上一点点RTTI)。
4.1 实例代码
local Robot = { name = "Sam", id = 001 } function Robot:New(extension) local t = setmetatable(extension or { }, self) self.__index = self return t end function Robot:SetName(name) self.name = name end function Robot:GetName() return self.name end function Robot:SetId(id) self.id = id end function Robot:GetId() return self.id end robot = Robot:New() print("robot's name:", robot:GetName()) print("robot's id:", robot:GetId()) print("-----------------") local FootballRobot = Robot:New({position = "right back"}) function FootballRobot:SetPosition(p) self.position = p end function FootballRobot:GetPosition() return self.position end fr = FootballRobot:New() print("fr's position:", fr:GetPosition()) print("fr's name:", fr:GetName()) print("fr's id:", fr:GetId()) print("-----------------") fr:SetName("Bob") print("fr's name:", fr:GetName()) print("robot's name:", robot:GetName())
输出结果:
robot's name:Sam
robot's id:1
fr's position:right back
fr's name:Sam
fr's id:1
fr's name:Bob
robot's name:Sam
4.2 相关描述:
prototype模式一个对象既是一个普通的对象,同时也能够做为建立其余对象的原型的对象(即类对象,class object);动态的改变原型对象的属性就能够动态的影响全部基于此原型的对象;另外,基于一个原型被建立出来的对象能够重载任何属于这个原型对象的方法、属性而不影响原型对象;同时,基于原型被建立出来的对象还能够做为原型来建立其余对象。
5.1 实例代码:
hello.lua
local pack = require "mypack" --导入包[注:包的名字与定义包的文件的名字相同(除去文件名后缀,在前面的代码中,就是“mypack”)] print(ver or "No ver defined!") print(pack.ver) pack.aFunInMyPack() print(aFunInMyPack or "No aFunInMyPack defined!") aFuncFromMyPack()
mypack.lua
module(..., package.seeall) --定义包 ver = "0.1 alpha" function aFunInMyPack() print("Hello!") end _G.aFuncFromMyPack = aFunInMyPack
输出结果:
No ver defined!
0.1 alpha
Hello!
No aFunInMyPack defined!
Hello!
5.2有关包的描述:
包是一种组织代码的方式。
通常在一个Lua文件内以module函数开始定义一个包。module同时定义了一个新的包的函数环境,以使在此包中定义的全局变量都在这个环境中,而非使用包的函数的环境中。理解这一点很是关键。之前面的代码为例, “module(..., package.seeall)”的意思是定义一个包,包的名字与定义包的文件的名字相同(除去文件名后缀,在前面的代码中,就是“mypack”),而且在包的函数环境里能够访问使用包的函数环境(好比,包的实现使用了print,这个变量没有在包里定义,而是定义在使用包的外部环境中)。
通常用require函数来导入一个包,要导入的包必须被置于包路径(packagepath)上。包路径能够经过package.path或者环境变量来设定。通常来讲,当前工做路径老是在包路径中。
请参考Lua手册进一步了解包的详细说明。
参考文献《C/C++程序员的Lua快速入门》
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
1.1 实例代码:
--迭代 local function enum(array) local index = 1 return function() local ret = array[index] index = index + 1 return ret end end local function foreach(array,action) for element in enum(array)do action(element) end end foreach({1,2,3},print)
输出结果:
1
2
3
1.2 有关迭代的描述:
迭代是for语句的一种特殊形式,能够经过for语句驱动迭代函数对一个给定集合进行遍历。正式、完备的语法说明较复杂,请参考Lua手册。
如前面代码所示:enum函数返回一个匿名的迭代函数,for语句每次调用该迭代函数都获得一个值(经过element变量引用),若该值为nil,则for循环结束。
2.1 实例代码
--线程 local function producer() return coroutine.create( function(salt) local t = {1,2,3} for i = 1,#t do salt = coroutine.yield(t[i] + salt) end end ) end function consumer(prod) local salt = 10 while true do local running ,product = coroutine.resume(prod, salt) salt = salt*salt if running then print(product or "END!") else break end end end consumer(producer())
输出结果:
11
102
10003
END!
2.2 有关协做线程的描述:
经过coroutine.create能够建立一个协做线程,该函数接收一个函数类型的参数做为线程的执行体,返回一个线程对象。
经过coroutine.resume能够启动一个线程或者继续一个挂起的线程。该函数接收一个线程对象以及其余须要传递给该线程的参数。线程能够经过线程函数的参数或者coroutine.yield调用的返回值来获取这些参数。当线程初次执行时,resume传递的参数经过线程函数的参数传递给线程,线程从线程函数开始执行;当线程由挂起转为执行时,resume传递的参数以yield调用返回值的形式传递给线程,线程从yield调用后继续执行
线程放弃调度
线程调用coroutine.yield暂停本身的执行,并把执行权返回给启动/继续它的线程;线程还可利用yield返回一些值给后者,这些值以resume调用的返回值的形式返回。
lua 论坛(lua 中国开发者 luaer中国官司方网站)
Lua参考手册(最正式、权威的Lua文档)
Lua编程(在线版,一样具权威性的Lua教科书)
Lua正式网站的文档页面(包含不少有价值的文档资料连接)
Lua维基(最全面的Lua维基百科)
LuaForge(最丰富的Lua开源代码基地)
参考文献《C/C++程序员的Lua快速入门》
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
表1
基本函数库 | 功能 | 参数 | 备注 |
---|---|---|---|
assert(v[,mess age]) | 至关于C的断言 | v:当表达式v为nil或false将触发错误,message:发生错误时返回的信息,默认为"assertion failed!" | |
collectgarbage (opt [, arg]) | 是垃圾收集器的通用接口,用于操做垃圾收集器 | opt:操做方法标志"Stop": 中止垃圾收集器 "Restart": 重启垃圾收集器 "Collect": 执行一次全垃圾收集循环"Count": 返回当前Lua中使用的内存量(以KB为单位)"Step": 单步执行一个垃圾收集. 步长 "Size" 由参数arg指定 (大型的值须要多步才能完成),若是要准确指定步长,须要屡次实验以达最优效果。若是步长完成一次收集循环,将返回True"Setpause": 设置 arg/100 的值做为暂定收集的时长 "Setstepmul": 设置 arg/100 的值,做为步长的增幅(即新步长=旧步长*arg/100) | |
dofile (filename) | 打开而且执行一个lua块,当忽略参数filename时,将执行标准输入设备(stdin)的内容。返回全部块的返回值。当发生错误时,dofile将错误反射给调用者 | 注:dofile不能在保护模式下运行 | |
error (message [, level]) | 终止正在执行的函数,并返回message的内容做为错误信息(error函数永远都不会返回) | 一般状况下,error会附加一些错误位置的信息到message头部.Level参数指示得到错误的位置,Level=1[默认]:为调用error位置(文件+行号)Level=2:指出哪一个调用error的函数的函数Level=0:不添加错误位置信息 | |
_G全局环境表(全局变量) | 记录全局环境的变量值的表 _G._G = _G | ||
getfenv(f) | 返回函数f的当前环境表 | f能够为函数或调用栈的级别,级别1[默认]为当前的函数,级别0或其它值将返回全局环境_G | |
getmetatable(object) | 返回指定对象的元表(若object的元表.__metatable项有值,则返回object的元表.__metatable的值),当object没有元表时将返回nil | ||
ipairs (t) | 返回三个值 迭代函数、表、0多用于穷举表的键名和键值对如:for i,v in ipairs(t) do end每次循环将索引赋级i,键值赋给v | 注:本函数只能用于以数字索引访问的表如:t={"1","cash"} | |
load (func [, chunkname]) | 装载一个块中的函数,每次调用func将返回一个链接前一结的字串,在块结尾处将返回nil当没有发生错误时,将返回一个编译完成的块做为函数,不然返回nil加上错误信息,此函数的环境为全局环境chunkname用于错误和调试信息 | ||
loadfile ([filename]) | 与load相似,但装载的是文件或当没有指定filename时装载标准输入(stdin)的内容 | ||
loadstring (string [, chunkname]) | 与load相似,但装载的内容是一个字串如:assert(loadstring(s))() | ||
next (table [, index]) | 容许程序遍历表中的每个字段,返回下一索引和该索引的值。 | table:要遍历的表index:要返回的索引的前一索中的号,当index为nil[]时,将返回第一个索引的值,当索引号为最后一个索引或表为空时将返回nil注:能够用next(t)来检测表是否为空(此函数只能用于以数字索引的表与ipairs相相似) | |
ipairs (t) | 返回三个值 next函数、表、0多用于穷举表的键名和键值对如:for n,v in pairs(t) do end | 每次循环将索引赋级i,键值赋给v注:本函数只能用于以键名索引访问的表如:t={id="1",name="cash"} | |
pcall (f, arg1, ···) | 在保护模式下调用函数(即发生的错误将不会反射给调用者)当调用函数成功能返回true,失败时将返回false加错误信息 | ||
print (···) | 简单的以tostring方式格式化输出参数的内容 | ||
rawequal (v1, v2) | 检测v1是否等于v2,此函数不会调用任何元表的方法 | ||
rawget (table, index) | 获取表中指定索引的值,此函数不会调用任何元表的方法,成功返回相应的值,当索引不存在时返回nil | 注:本函数只能用于以数字索引访问的表如:t={"1","cash"} | |
rawset (table, index, value) | 设置表中指定索引的值,此函数不会调用任何元表的方法,此函数将返回table | ||
select (index, ···) | 当index为数字将返回全部index大于index的参数:如:select(2,"a","b") 返回 "b"当index为"#",则返回参数的总个数(不包括index) | ||
setfenv (f, table) | 设置函数f的环境表为table | f能够为函数或调用栈的级别,级别1[默认]为当前的函数,级别0将设置当前线程的环境表 | |
setmetatable (table, metatable) | 指定的table设置元表metatable,若是metatable为nil则取消table的元表,当metatable有__metatable字段时,将触发错误 | 注:只能为LUA_TTABLE表类型指定元表 | |
tonumber (e [, base]) | 尝试将参数e转换为数字,当不能转换时返回nil | base(2~36)指出参数e当前使用的进制,默认为10进制,如tonumber(11,2)=3 | |
tostirng(e) | 将参数e转换为字符串,此函数将会触发元表的__tostring事件 | ||
type(v) | 返回参数的类型名("nil","number", "string", "boolean", "table", "function", "thread", "userdata") | ||
unpack (list [, i [, j]]) | 返回指定表的索引的值,i为起始索引,j为结束索引 | 注:本函数只能用于以数字索引访问的表,不然只会返回nil如:t={"1","cash"} | |
_VERSION | 返回当前Lua的版本号"Lua 5.1". | ||
xpcall (f, err) | 与pcall相似,在保护模式下调用函数(即发生的错误将不会反射给调用者)但可指定一个新的错误处理函数句柄当调用函数成功能返回true,失败时将返回false加err返回的结果 |
引用博客:http://www.cnblogs.com/whiteyun/archive/2009/08/12/1543184.html
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
Lua5.1中数学库的全部函数以下表:
math.pi 为圆周率常量 = 3.14159265358979323846
表1
数学库 | 说明 | 例子 | 方法 |
---|---|---|---|
abs | 取绝对值 | math.abs(-15) | 15 |
acos | 反余弦函数 | math.acos(0.5) | 1.04719755 |
asin | 反正弦函数 | math.asin(0.5) | 0.52359877 |
atan2 | x / y的反正切值 | math.atan2(90.0, 45.0) | 1.10714871 |
atan | 反正切函数 | math.atan(0.5) | 0.463647609 |
ceil | 不小于x的最大整数 | math.ceil(5.8) | 6 |
cosh | 双曲线余弦函数 | math.cosh(0.5) | 1.276259652 |
cos | 余弦函数 | math.cos(0.5) | 0.87758256 |
deg | 弧度转角度 | math.deg(math.pi) | 180 |
exp | 计算以e为底x次方值 | math.exp(2) | 2.718281828 |
floor | 不大于x的最大整数 | math.floor(5.6) | 5 |
fmod (mod) | 取模运算 | math.mod(14, 5) | 4 |
frexp | 把双精度数val分解为数字部分(尾数)和以2为底的指数n,即val=x*2n | math.frexp(10.0) | 0.625 4 |
ldexp | 计算value * 2的n次方 | math.ldexp(10.0, 3) | 80 = 10 * (2 ^3) |
log10 | 计算以10为基数的对数 | math.log10(100) | 2 |
log | 计算一个数字的天然对数 | math.log(2.71) | 0.9969 |
max | 取得参数中最大值 | math.max(2.71, 100, -98, 23) | 100 |
min | 取得参数中最小值 | math.min(2.71, 100, -98, 23) | -98 |
modf | 把数分为整数和小数 | math.modf(15.98) | 15 98 |
pow | 获得x的y次方 | math.pow(2, 5) | 32 |
rad | 角度转弧度 | math.rad(180) | 3.14159265358 |
random | 获取随机数 | math.random(1, 100) math.random(100) | 获取1-100的随机数 |
randomseed | 设置随机数种子 | math.randomseed(os.time()) | 在使用math.random函数以前必须使用此函数设置随机数种子 |
sinh | 双曲线正弦函数 | math.sinh(0.5) | 0.5210953 |
sin | 正弦函数 | math.sin(math.rad(30)) | 0.5 |
sqrt | 开平方函数 | math.sqrt(16) | 4 |
tanh | 双曲线正切函数 | math.tanh(0.5) | 0.46211715 |
tan | 正切函数 | math.tan(0.5) | 0.5463024 |
引用博客:http://www.cnblogs.com/whiteyun/archive/2009/08/10/1543040.html
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
为了让游戏前端数据输出更加条理,作了一个简单树状结构来打印数据。
ccmlog.lua
local function __tostring(value, indent, vmap) local str = '' indent = indent or '' vmap = vmap or {} --递归结束条件 if (type(value) ~= 'table') then if (type(value) == 'string') then --字符串 str = string.format("[[%s]]", value) else --整数 str = tostring(value) end else if type(vmap) == 'table' then if vmap[value] then return '('..tostring(value)..')' end vmap[value] = true end local auxTable = {} --保存元表KEY(非整数) local iauxTable = {} --保存元表value local iiauxTable = {} --保存数组(key为0) table.foreach(value, function(i, v) if type(i) == 'number' then if i == 0 then table.insert(iiauxTable, i) else table.insert(iauxTable, i) end elseif type(i) ~= 'table' then table.insert(auxTable, i) end end) table.sort(iauxTable) str = str..'{\n' local separator = "" local entry = "\n" local barray = true local kk,vv table.foreachi (iauxTable, function (i, k) if i == k and barray then entry = __tostring(value[k], indent..' \t', vmap) str = str..separator..indent..' \t'..entry separator = ", \n" else barray = false table.insert(iiauxTable, k) end end) table.sort(iiauxTable) table.foreachi (iiauxTable, function (i, fieldName) kk = tostring(fieldName) if type(fieldName) == "number" then kk = '['..kk.."]" end entry = kk .. " = " .. __tostring(value[fieldName],indent..' \t',vmap) str = str..separator..indent..' \t'..entry separator = ", \n" end) table.sort(auxTable) table.foreachi (auxTable, function (i, fieldName) kk = tostring(fieldName) if type(fieldName) == "number" then kk = '['..kk.."]" end vv = value[fieldName] entry = kk .. " = " .. __tostring(value[fieldName],indent..' \t',vmap) str = str..separator..indent..' \t'..entry separator = ", \n" end) str = str..'\n'..indent..'}' end return str end ccmlog = function(m,fmt,...) local args = {...} for k,arg in ipairs(args) do if type(arg) == 'table' or type(arg) == 'boolean' or type(arg) == 'function' or type(arg) == 'userdata' then args[k] = __tostring(arg) end end args[#args+1] = "nil" args[#args+1] = "nil" args[#args+1] = "nil" local str = string.format("[%s]:"..fmt.." %s", m, unpack(args)) print(str) local off = 1 local p = CCLOGWARN if m == 'error' then p = CCLOGERROR elseif m == 'warn' then p = CCLOGWARN end while off <= #str do local subStr = string.sub(str, off, off+1024) off = off + #subStr --p(subStr) end end --打印测试 reserved = { [100] = { 300, 400}, 200, { 300, 500}, abc = "abc",[0] = {1,2,3,"abc"}} ccmlog("d","d",reserved)
打印效果以下:
[d]:d { 200, { 300, 500 }, [0] = { 1, 2, 3, [[abc]] }, [100] = { 300, 400 }, abc = [[abc]] }
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
Lua中每一个值均可具备元表。 元表是普通的Lua表,定义了原始值在某些特定操做下的行为。你可经过在值的原表中设置特定的字段来改变做用于该值的操做的某些行为特征。
例如,当数字值做为加法的操做数时,Lua检查其元表中的"__add"字段是否有个函数。若是有,Lua调用它执行加法。
咱们称元表中的键为事件(event),称值为元方法(metamethod)。前述例子中的事件是"add",元方法是执行加法的函数。
可经过函数getmetatable查询任何值的元表。
在table中,我能够从新定义的元方法有如下几个:
__add(a, b) --加法 __sub(a, b) --减法 __mul(a, b) --乘法 __div(a, b) --除法 __mod(a, b) --取模 __pow(a, b) --乘幂 __unm(a) --相反数 __concat(a, b) --链接 __len(a) --长度 __eq(a, b) --相等 __lt(a, b) --小于 __le(a, b) --小于等于 __index(a, b) --索引查询 __newindex(a, b, c) --索引更新(PS:不懂的话,后面会有讲) __call(a, ...) --执行方法调用 __tostring(a) --字符串输出 __metatable --保护元表
Lua中的每个表都有其Metatable。Lua默认建立一个不带metatable的新表
t = {} print(getmetatable(t)) --> nil
能够使用setmetatable函数设置或者改变一个表的metatable
t1 = {} setmetatable(t, t1) assert(getmetatable(t) == t1)
任何一个表均可以是其余一个表的metatable,一组相关的表能够共享一个metatable(描述他们共同的行为)。一个表也能够是自身的metatable(描述其私有行为)。
接下来就介绍介绍若是去从新定义这些方法。
如今我使用完整的实例代码来详细的说明算术类元方法的使用。
Set = {} local mt = {} -- 集合的元表 -- 根据参数列表中的值建立一个新的集合 function Set.new(l) local set = {} setmetatable(set, mt) for _, v in pairs(l) do set[v] = true end return set end -- 并集操做 function Set.union(a, b) local retSet = Set.new{} -- 此处至关于Set.new({}) for v in pairs(a) do retSet[v] = true end for v in pairs(b) do retSet[v] = true end return retSet end -- 交集操做 function Set.intersection(a, b) local retSet = Set.new{} for v in pairs(a) do retSet[v] = b[v] end return retSet end -- 打印集合的操做 function Set.toString(set) local tb = {} for e in pairs(set) do tb[#tb + 1] = e end return "{" .. table.concat(tb, ", ") .. "}" end function Set.print(s) print(Set.toString(s)) end
如今,我定义“+”来计算两个集合的并集,那么就须要让全部用于表示集合的table共享一个元表,而且在该元表中定义如何执行一个加法操做。首先建立一个常规的table,准备用做集合的元表,而后修改Set.new函数,在每次建立集合的时候,都为新的集合设置一个元表。代码以下:
Set = {} local mt = {} -- 集合的元表 -- 根据参数列表中的值建立一个新的集合 function Set.new(l) local set = {} setmetatable(set, mt) for _, v in pairs(l) do set[v] = true end return set end
在此以后,全部由Set.new建立的集合都具备一个相同的元表,例如:
local set1 = Set.new({10, 20, 30}) local set2 = Set.new({1, 2}) print(getmetatable(set1)) print(getmetatable(set2)) assert(getmetatable(set1) == getmetatable(set2))
最后,咱们须要把元方法加入元表中,代码以下:
mt.__add = Set.union
这之后,只要咱们使用“+”符号求两个集合的并集,它就会自动的调用Set.union函数,并将两个操做数做为参数传入。好比如下代码:
local set1 = Set.new({10, 20, 30}) local set2 = Set.new({1, 2}) local set3 = set1 + set2 Set.print(set3)
在上面列举的那些能够重定义的元方法均可以使用上面的方法进行重定义。如今就出现了一个新的问题,set1和set2都有元表,那咱们要用谁的元表啊?虽然咱们这里的示例代码使用的都是一个元表,可是实际coding中,会遇到我这里说的问题,对于这种问题,Lua是按照如下步骤进行解决的:
以上就是Lua处理这个问题的规则,那么咱们在实际编程中该如何作呢?
好比set3 = set1 + 8这样的代码,就会打印出如下的错误提示:
lua: test.lua:16: bad argument #1 to 'pairs' (table expected, got number)
可是,咱们在实际编码中,能够按照如下方法,弹出咱们定义的错误消息,代码以下:
function Set.union(a, b) if getmetatable(a) ~= mt or getmetatable(b) ~= mt then error("metatable error.") end local retSet = Set.new{} -- 此处至关于Set.new({}) for v in pairs(a) do retSet[v] = true end for v in pairs(b) do retSet[v] = true end return retSet end
当两个操做数的元表不是同一个元表时,就表示两者进行并集操做时就会出现问题,那么咱们就能够打印出咱们须要的错误消息。
上面总结了算术类的元方法的定义,关系类的元方法和算术类的元方法的定义是相似的,这里不作累述。
写过Java或者C#的人都知道,Object类中都有一个tostring的方法,程序员能够重写该方法,以实现本身的需求。在Lua中,也是这样的,当咱们直接print(a)(a是一个table)时,是不能够的。那怎么办,这个时候,咱们就须要本身从新定义tostring元方法,让print能够格式化打印出table类型的数据。函数print老是调用tostring来进行格式化输出,当格式化任意值时,tostring会检查该值是否有一个tostring的元方法,若是有这个元方法,tostring就用该值做为参数来调用这个元方法,剩下实际的格式化操做就由__tostring元方法引用的函数去完成,该函数最终返回一个格式化完成的字符串。例如如下代码:
mt.__tostring = Set.toString
咱们会发现,使用getmetatable就能够很轻易的获得元表,使用setmetatable就能够很容易的修改元表,那这样作的风险是否是太大了,那么如何保护咱们的元表不被篡改呢?在Lua中,函数setmetatable和getmetatable函数会用到元表中的一个字段,用于保护元表,该字段是metatable。当咱们想要保护集合的元表,是用户既不能看也不能修改集合的元表,那么就须要使用metatable字段了;当设置了该字段时,getmetatable就会返回这个字段的值,而setmetatable则会引起一个错误;如如下演示代码:
function Set.new(l) local set = {} setmetatable(set, mt) for _, v in pairs(l) do set[v] = true end mt.__metatable = "You cannot get the metatable" -- 设置完个人元表之后,不让其余人再设置 return set end local tb = Set.new({1, 2}) print(tb) print(getmetatable(tb)) setmetatable(tb, {})
上述代码就会打印如下内容:
{1, 2} You cannot get the metatable lua: test.lua:56: cannot change a protected metatable
是否还记得当咱们访问一个table中不存在的字段时,会返回什么值?默认状况下,当咱们访问一个table中不存在的字段时,获得的结果是nil。可是这种情况很容易被改变;Lua是按照如下的步骤决定是返回nil仍是其它值得:
下面经过一个实际的例子来讲明__index的使用。假设要建立一些描述窗口,每一个table中都必须描述一些窗口参数,例如颜色,位置和大小等,这些参数都是有默认值得,所以,咱们在建立窗口对象时能够指定那些不一样于默认值得参数。
Windows = {} -- 建立一个命名空间 -- 建立默认值表 Windows.default = {x = 0, y = 0, width = 100, height = 100, color = {r = 255, g = 255, b = 255}} Windows.mt = {} -- 建立元表 -- 声明构造函数 function Windows.new(o) setmetatable(o, Windows.mt) return o end -- 定义__index元方法 Windows.mt.__index = function (table, key) return Windows.default[key] end local win = Windows.new({x = 10, y = 10}) print(win.x) -- >10 访问自身已经拥有的值 print(win.width) -- >100 访问default表中的值 print(win.color.r) -- >255 访问default表中的值
根据上面代码的输出,结合上面说的那三步,咱们再来看看,print(win.x)时,因为win变量自己就拥有x字段,因此就直接打印了其自身拥有的字段的值;print(win.width),因为win变量自己没有width字段,那么就去查找是否拥有元表,元表中是否有index对应的元方法,因为存在index元方法,返回了default表中的width字段的值,print(win.color.r)也是一样的道理。
在实际编程中,__index元方法没必要必定是一个函数,它还能够是一个table。当它是一个函数时,Lua以table和不存在key做为参数来调用该函数,这就和上面的代码同样;当它是一个table时,Lua就以相同的方式来从新访问这个table,因此上面的代码也能够是这样的:
-- 定义__index元方法 Windows.mt.__index = Windows.default
newindex元方法与index相似,newindex用于更新table中的数据,而index用于查询table中的数据。当对一个table中不存在的索引赋值时,在Lua中是按照如下步骤进行的:
Lua解释器先判断这个table是否有元表;
那么这里就出现了一个问题,看如下代码:
local tb1 = {} local tb2 = {} tb1.__newindex = tb2 tb2.__newindex = tb1 setmetatable(tb1, tb2) setmetatable(tb2, tb1) tb1.x = 10
发现什么问题了么?是否是循环了,在Lua解释器中,对这个问题,就会弹出错误消息,错误消息以下:
loop in settable
引用博客:http://www.jellythink.com/archives/511
由 sf0501 建立, 最后一次修改 2016-11-16
table库由一些操做table的辅助函数组成。他的主要做用之一是对Lua中array的大小给出一个合理的解释。另外还提供了一些从list中插入删除元素的函数,以及对array元素排序函数。
concat是concatenate(连锁, 链接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的全部元素, 元素间以指定的分隔符(sep)隔开。除了table外, 其余的参数都不是必须的, 分隔符的默认值是空字符, start的默认值是1, end的默认值是数组部分的总长.
sep, start, end这三个参数是顺序读入的, 因此虽然它们都不是必须参数, 但若是要指定靠后的参数, 必须同时指定前面的参数.
Lua 函数回调技巧 由 sf0501 建立,小路依依 最后一次修改 2016-11-16 技巧1: local a = {};function b() print("Hello World")enda["sell"] = {callFunc =b}a["sell"].callFunc() 技巧2: 使用lua 自带的 unpack : 解释:把一直数组(只有连续数字下标的 table)展开成一串返回值,可是对用字符串或别的东西作 key 的 table 无能为力。 function unpackex(tbl, args) local ret = {} for _,v in ipairs(args) do table.insert(ret, tbl[v]) end return unpack(ret)endprint(unpackex({one = {"one", "two", "three"}, two = "T" , three = "TH"},{"one", "two", "three"})) 输出:>> table: 00ABC2D0TTHtest = {"Tom", "Mary", "Jam","Hey"} print(table.concat(test, ":")) print("*************") print(table.concat(test, nil, 1, 2)) print("*************") print(table.concat(test, "\n", 2, 3)) print(table.maxn(test))
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
功能:按table的内容返回一个时间值(数字),若不带参数则返回当前时间.(在许多系统中该数值是当前距离某个特定时间的秒数。)
说明:当为函数调用附加一个特殊的时间表时,该函数就是返回距该表描述的时间的数值。这样的时间表有以下的区间:
前三项是必需的,若是未定义后几项,默认时间为正午(12:00:00)。若是是在里约热内卢(格林威治向西三个时区)的一台Unix计算机上(相对时间为1970年1月1日,00:00:00),对于pc机(中国时区而言)有稍微更改,更改了为1970年1月1日,08:00:00,这是因我国与其它国家时间差致使。
例子:
print(os.time{year=1970, month=1, day=1,hour=8}) print(os.time{year=1970, month=1, day=1}) --若未定义“时,分,秒”,默认时间为正午(04:00:00)
运行结果:
-->0
-->14400(14400 = 46060 )
功能:返回一个按format格式化日期、时间的字串或表
说明:函数date,实际上是time函数的一种“反函数”。它将一个表示日期和时间的数值,转换成更高级的表现形式。其第一个参数是一个格式化字符串,描述了要返回的时间形式。第二个参数就是时间的数字表示,默认为当前的时间。
参数:format:
*t":将返一个带year(4位),month(1-12), day (1--31), hour (0-23), min (0-59), sec (0-61), wday (星期几, 星期天为1), yday (年内天数), and isdst (是否为日光节约时间true/false)的带键名的表;
若没有"*t"则返回一个按C的strftime函数格式化的字符串;
若不带参数,则按当前系统的设置返回格式化的字符串 os.date() <=> os.date("%c")
例子:我当前PC时间,如图:
代码:
t = os.date("*t", os.time()); for i, v in pairs(t) do print(i,"->",v); end
运行结果 :
运行结果和以上时钟的秒,不一致,你想,截图也要时间的,呵呵。
若是使用带标记(见下表)的特殊字符串,os.data函数会将相应的标记位以时间信息进行填充,获得一个包含时间的字符串。
例子:
print(os.date("today is %A, in %B")) print(os.date("%X", 906000490))
运行结果:
同时,也能够使用明确的字符串格式方式(例如"%m/%d/%Y")
例子:
print(os.date("%m/%d/%Y", 906000490))
运行结果:
<img
src="/attachments/image/cimg/ph2os.difftime (t2, t1)/h2p功能:返回t1到t2相差的秒数
/pp例子:/pprecodet1 = os.time();for i = 0, 100000 do os.time();endt2 =
os.time();print(string.format(" t1:="" %d="" t2:="" %d",t1,t2))=""
print(os.date("%x",="" t1))="" t2))="" print(os.difftime(t2,="" t1));=""
code="" pre="" p运行结果:="" p="" pimg="" elapsed="" time="" :=""
%.2f\n",="" os.clock()="" -="" x));<="">
运行结果:
由 sf0501 建立, 最后一次修改 2016-11-16
项目要求对lua脚本进行加密,查了一下相关的资料 ,得知lua自己能够使用luac将脚本编译为字节码(bytecode)从而实现加密,试了一下,确实可行。下面是使用原生的lua解释器编译字节码:
一、新建一个名为1.lua的文件,里面只有一句话print("Hello Lua"),新建一个空的out.lua脚本文件
二、开始--运行--cmd三、luac -o out.lua 1.lua
注: luac -o [编译后脚本名] [脚本名],必要时带上脚本路径,如:
[编译后脚本名] [脚本名],必要时带上脚本路径
回车以后,再打开out.lua就能够看到编译好的字节码了,如:
而后实验一下,执行这个字节码脚本,能够看到lua原生的解释器能够直接解析luac编译出来的bytecode脚本,很方便!
重点:作完了以上的一系列以后,我照着这个方法编译项目中的脚本,而后在cocos2dx环境下使用,发现不行!因而又查了一下资料,发现2dx使用的是luajit,lua原生编译出来的bytecode和luajit是不兼容的,因此照着上面方法编译出来的bytecode脚本没法在2dx中使用。
解决这个问题其实很简单,就是用2dx自带的luajit编译lua脚本,下面附上luajit编译bytecode的方法:
一、在cocos2d-x-2.2.3\scripting\lua\luajit\LuaJIT-2.0.1\src目录下有个msvcbuild.bat批处理文件,须要先把luajit.exe这个东西给编译出来。
二、打开visual studio的命令行工具,这个只要装了vs都会有,在安装目录里面能够找到。
三、用vs的命令行工具cd到luajit的src目录
四、执行msvcbuild.bat批处理文件,编译出luajit.exe
五、将生成的luajit.exe、lua51.dll、jit 复制到打包工具的相对目录下,这样在工具中就能够直接调用luajit –b source_file out_file (通常都是lua后缀,代码不用改动)
<img
src="/attachments/image/cimg/pp接下来就能够使用luajit.exe编译lua脚本的bytecode了:luajit
-b [脚本名] [编译后的脚本名],执行完后会在src目录下生成一个已经编译成bytecode的jit.lua文件。/ppimg src="
attachments="" image="" cimg="" 2016-02-22_56cb2ca40043a.jpg"="" alt=""
p="" pimg="" jit.lua");运行结果为:="" <="">
至此,luajit编译bytecode加密已完成!
严重注意:例子中,我把编译先后的脚本名字取的不同,是为了让你们看出差别化来,实际在项目中使用的时候,脚本的名字编译先后最好都一致,否则在脚本中相互require的时候可能会出现问题!一个一个转换脚太麻烦了,分享一个bat批处理,能够批量转换一个文件夹中的全部lua文件.
代码以下:
@echo off if exist out rd /s /q out mkdir out :input cls set input=: set /p input= 拖入要编译的lua文件夹: set "input=%input:"=%" if "%input%"==":" goto input if not exist "%input%" goto input for %%i in ("%input%") do if /i "%%~di"==%%i goto input pushd %cd% cd /d "%input%">nul 2>nul || exit set cur_dir=%cd% popd set /a num = 0 for /f "delims=" %%i in ('dir /b /a-d /s "%input%"') do (set /a num += 1 & luajit -b %%~fsi out/%%~nxi & echo %%~nxi) echo 编译脚本数量:%num% ATTRIB out/*.* +R pause
编译后,文件夹内全部的lua脚本将被批量编译为字节码,并保存在xxx\out目录下,如:
注:XXX为打包加密文件路径
还有小提示:ios64目前只支持lua,不支持用luajit生成二进制*.lua.
引用博客:http://jingyan.baidu.com/article/0a52e3f4179713bf62ed72f1.html
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
Lua解释器对字符串的支持颇有限。一个程序能够建立字符串并链接字符串,但不能截取子串,检查字符串的大小,检测字符串的内容。在Lua中操纵字符串的功能基原本自于string库。
--返回字符串s的长度 local s = "HelloWorld" print(string.len(s)) -->10 --重复n次字符串s的串 print(string.rep(s,2)) -->HelloWorldHelloWorld --大写字母转换成小写 print(string.lower(s)) -->helloworld --小写转换成大写 print(string.upper(s)) -->HELLOWORLD --截取字符串 local s = "[in brackets]" print(string.sub(s,2,-1)) -->in brackets] --将每个数字转换成字符 print(string.char(97)) -->a --将每个字符转换成数字 print(string.byte("abc")) print(string.byte("abc", 2)) --> 98 print(string.byte("abc", -1)) --> 99 --注:使用负数索引访问字符串的最后一个字符 --对字符串进行格式化输出 PI = 3.14165120 print(string.format("pi = %.4f", PI)) -->pi = 3.1417 --注释:使用和C语言的printf函数几乎如出一辙,你彻底能够照C语言的printf来使用这个函数.
注:
string库中全部的字符索引从前日后是1,2,...;从后往前是-1,-2,...
string库中全部的function都不会直接操做字符串,而是返回一个结果。
在string库中功能最强大的函数是:string.find(字符串查找),string.gsub(全局字符串替换),and string.gfind(全局字符串查找)。这些函数都是基于模式匹配的。
1.string.find
说明:用来在目标串(subject string)内搜索匹配指定的模式的串。函数若是找到匹配的串返回他的位置,不然返回nil.最简单的模式就是一个单词,仅仅匹配单词自己。好比,模式'hello'仅仅匹配目标串中的"hello"。当查找到模式的时候,函数返回两个值:匹配串开始索引和结束索引。
local s = "hello world" i,j = string.find(s,"hello") print(i," ",j) -->1 5 print(string.find(s, "kity")) -->nil
string.find函数第三个参数是可选的:标示目标串中搜索的起始位置。当咱们想查找目标串中全部匹配的子串的时候,这个选项很是有用。
local s = "\nare you ok!\n OK\n" local t = {} local i = 0 while true do i = string.find(s,"\n",i+1) if i == nil then break end table.insert(t,i) end for k,v in pairs(t) do print(k,"->",v) end
运行结果:
1 -> 1 2 -> 13 3 -> 17
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
这篇文章主要介绍了Lua中的模块(module)和包(package)详解,本文讲解了require函数、写一个模块、package.loaded、module函数等内容.
从Lua5.1版本开始,就对模块和包添加了新的支持,但是使用require和module来定义和使用模块和包。require用于使用模块,module用于建立模块。简单的说,一个模块就是一个程序库,能够经过require来加载。而后便获得了一个全局变量,表示一个table。这个table就像是一个命名空间,其内容就是模块中导出的全部东西,好比函数和常量,一个符合规范的模块还应使require返回这个table。如今就来具体的总结一下require和module这两个函数。如:
require "mod" mod.foo() local m2 = require "mod2" local f = mod2.foo f()
require函数的调用形式为require "模块名"。该调用会返回一个由模块函数组成的table,而且还会定义一个包含该table的全局变量。在使用Lua中的标准库时能够不用显示的调用require,由于Lua已经预先加载了他们。
require函数在搜素加载模块时,有一套自定义的模式,如:
?;?.lua;c:/windows/?;/usr/local/lua/?/?.lua
在上面的模式中,只有问号(?)和分号(;)是模式字符,分别表示require函数的参数(模块名)和模式间的分隔符。如:调用require "sql",将会打开如下的文件:
sql
sql.lua
c:/windows/sql
/usr/local/lua/sql/sql.lua
Lua将require搜索的模式字符串放在变量package.path中。当Lua启动后,便以环境变量LUA_PATH的值来初始化这个变量。若是没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。若是require没法找到与模块名相符的Lua文件,就会找C程序库。C程序库的搜索模式存放在变量package.cpath中。而这个变量则是经过环境变量LUA_CPATH来初始化的。
新建一个文件,命名为game.lua,代码以下:
local M = {}; local modelName = ...; _G[modelName] = M; function M.play() print("那么,开始吧"); end function M.quit() print("你走吧,我保证你不会出事的,呵,呵呵"); end return M;
加载game.lua,代码以下:
game = require "test" game.play()
运行:
lua -e "io.stdout:setvbuf 'no'" "HelloWorld.lua"
那么,开始吧
Exit code: 0
仔细阅读上例中的代码,咱们能够发现一些细节上问题。好比模块内函数之间的调用仍然要保留模块名的限定符,若是是私有变量还须要加local关键字,同时不能加模块名限定符。若是须要将私有改成公有,或者反之,都须要必定的修改。那又该如何规避这些问题呢?咱们能够经过Lua的函数“全局环境”来有效的解决这些问题。
咱们把game.lua这个模块里的全局环境设置为M,因而,咱们直接定义函数的时候,不须要再带M前缀。
由于此时的全局环境就是M,不带前缀去定义变量,就是全局变量,这时的全局变量是保存在M里。
因此,实际上,play和quit函数仍然是在M这个table里。
local M = {}; local modelName = ...; _G[modelName] = M; package.loaded[modname] = M setfenv(1, M); function play() print("那么,开始吧"); end function quit() print("你走吧,我保证你不会出事的,呵,呵呵"); end return M;
在Lua 5.1中,咱们能够用module(...)函数来代替如下代码,如:
local modname = ... local M = {} _G[modname] = M package.loaded[modname] = M --[[ 和普通Lua程序块同样声明外部函数。 --]] setfenv(1,M)
便是:
module(..., package.seeall); function play() print("那么,开始吧") end function quit() print("你走吧,我保证你不会出事的,呵,呵呵"); end
因为在默认状况下,module不提供外部访问,必须在调用它以前,为须要访问的外部函数或模块声明适当的局部变量。而后Lua提供了一种更为方便的实现方式,即在调用module函数时,多传入一个package.seeall的参数,至关于 setmetatable(M, {__index = _G}) .
如:
module(...,package.seeall)
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
I/O库为文件操做提供两种模式。简单模式(simple model)拥有一个当前输入文件和一个当前输出文件,而且提供针对这些文件相关的操做。彻底模式(complete model)使用外部的文件句柄来实现。
I/O库将当前输入文件做为标准输入(stdin),将当前输出文件做为标准输出(stdout)。这样当咱们执行io.read,就是在标准输入中读取一行。
写操做较读操做简单,咱们先从写操做入手。
下面这个例子里函数io.write获取任意数目的字符串参数,接着将它们写到当前的输出文件。
local t = io.write("sin (3) = ", math.sin(3), "\n") --> sin (3) = 0.1411200080598672 print("hello", "Lua"); print("Hi") -->hello Lua -->Hi
注:Write函数与print函数不一样在于,write不附加任何额外的字符到输出中去,例如制表符,换行符等等。还有write函数是使用当前输出文件,而print始终使用标准输出。另外print函数会自动调用参数的tostring方法,因此能够显示出表(tables)函数(functions)和nil。
read函数:从当前输入文件读取串,由它的参数控制读取的内容:
例子:
--io.read 从标准输入流中得到,默认设置下,就是你的屏幕输入 t = io.read("*all") t = string.gsub(t, ...) -- do the job io.write(t) -- write the
提示:若使用luaEditor编辑器,估计没法在屏幕输入。
彻底模式的核心在于文件句柄(file handle)。该结构相似于C语言中的文件流(FILE*),其呈现了一个打开的文件以及当前存取位置。打开一个文件的函数是io.open。它模仿C语言中的fopen函数,一样须要打开文件的文件名参数,打开模式的字符串参数:
例子:
--读操做 file = io.open("testRead.txt", "r") for line in file:lines() do print(line) end file:close() --写操做 file = io.open("testRead.txt","a+") file:write("\nhello") file:close()
素材:
内容:
运行结果:
文件内容:
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
lua编程中,常常遇到函数的定义和调用,有时候用点号调用,有时候用冒号调用,这里简单的说明一下原理。如:
-- 点号定义和点号调用: girl = {money = 200} function girl.goToMarket(girl ,someMoney) girl.money = girl.money - someMoney end girl.goToMarket(girl ,100) print(girl.money)
-- 参数self指向调用者自身(相似于c++里的this 指向当前类) girl = {money = 200} function girl.goToMarket(self ,someMoney) self.money = self.money - someMoney end girl.goToMarket(girl, 100) print(girl.money)
-- 冒号定义和冒号调用: girl = {money = 200} function girl:goToMarket(someMoney) self.money = self.money - someMoney end girl:goToMarket(100) print(girl.money)
冒号定义和冒号调用其实跟上面的效果同样,只是把第一个隐藏参数省略了,而该参数self指向调用者自身。
总结:冒号只是起了省略第一个参数self的做用,该self指向调用者自己,并无其余特殊的地方。
引用博文:http://www.xuebuyuan.com/1613223.html
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
开发中,大量使用lua,暂时根据当前情况,总结相对而言较好的规范,在多人协做中能够更好的开发、交流。
介绍
该文档旨在为使用lua编写应用程序创建编码指南。
制订编码规范的目的:
切记:善用调试器。
1.全部lua文件命名时使用小写字母、下划线
2.类名、变量名尽量使用有意义的英文,类名使用帕斯卡命名法,变量名使用骆驼式命名法
3.常量、消息号定义时用大写,单词间 _ 分割 eg:KIND_PET_FOOD
4.枚举值定义时 加前缀 enum_
\5. 函数名使用骆驼式命名法
注:
骆驼式命名法:第一个单字以小写字母开始;第二个单字的首字母大写或每个单字的首字母都采用大写字母
帕斯卡命名法:和骆驼命名法很像,只有一点区别,就是首字母要大写。(单字之间不以空格断开或链接号)
\1. 文件开头加上此文件的功能、职责的简要描述;
以下:
--
-- Author: Feng
-- Date: XXXX-XX-XX
-- 功能描述
每一个文件都加module 限定词; 导入的模块都加 local 限定词;或者使用(module(..., package.seeall)),这样便于进行热更新
\2. 全部提供外部函数都加以下格式的注释。
例如:
--此函数检测是否能够从A(oldx, oldy)点走到B点(newx, newy)
--@param oldx 当前所在点x
--@param oldy 当前所在点y
--@param newx 目标点x
--@param newy 目标点y
--@return 若能够到达,返回true;不然返回false
function Object:checkBar(oldx, oldy, newx, newy)
…
end
\3. 函数与函数间、以及一些定义之间加上空行。
\4. 函数内的临时变量、文件内的局部函数都加上 local 限定词
\5. 函数的行数过长(大于100行)时,尽可能拆分为多个子函数;函数中一些晦涩的部分,必定要加上注释。
\6. 短小的注释使用 --; 较长的注释使用 --[[ ]]
\7. assert函数开销不小,请慎用。
\8. Lua类设计时,用元表来实现oop。
不要直接增长函数成员,由于直接增长函数成员会致使内存增长而且在jit下执行效率和用元表方式无差别。
\9. 文件使用UTF8格式
\1. 使用空行
在下述状况下使用单行的空白行来分隔:
1)在方法之间
2)在方法内部代码的逻辑段落小节之间
3)在注释行以前
注释以前增长一行或者多行空行。
2.使用空格符
除正常的成分之间以空格符分隔名(如数据类型和变量名之间),在下述状况下也应使用一个空格符来分隔:
1)运算符和运算符之间,如: c = a + b;
2)在参数列表中的逗号后面,如:
function m1(year, month)
end
3) 在for语句时,如:
for k, v in pairs(t) do
end
4)在下列状况下不要使用空格。
例如:
函数定义时:
function test1(a)
end
不要这样:
function test1( a )
end
函数调用时:
test1(3)
不要这样:
test1( 3 )
不要如此的缘由在于:
a).容易忘记相关空格,致使风格不统一,这样还不如不加;
b).lua解析语法时是采用空格等分割来解析的,某些状况下,若不当心加空格会致使非预期的结果。
\3. 使用换行符
不建议在一行中写多条语句,一条语句的长度通常超过了80个字符时,应该换行
\4. 使用小括号
能够使用小括号来强行规定运算顺序
\5. 使用缩进
在下述状况下应用缩进
1)类中的成分
2)方法体或语句块中的成分
3)换行时的非起始行
缩减量通常为在上一级成分的基础上跑到下一个制表位
1.代码中使用的一些函数尽量在文件开头或者当前局部环境中加local前缀从新定义下。
例如:
local assert = assert
2.尽可能减小表中的成员是另外一个表的引用。 考虑lua的垃圾收集机制、内存泄露等。
3.高级特性尽量不用
4.写代码时尽量写的简单,考虑性能时先作好推断,看看能提高多少,增长的复杂度以及形成的代码晦涩有多严重,而后再决定如何作
5.加载的xml数据表,尽量的作好数据校验,若校验失败,要出发断言,使服务器没法启动;不要等出错时,回过头来检查是数据表问题仍是逻辑问题。
6.出错时,记录好错误日志。
有的函数开销比较大,而调用的频率很低,那么能够不对他作优化;
反之,有的函数开销较小,可是调用的频率很高,从如何下降调用频率以及减小函数开销两个角度去思考,而后定下优化方案
提交代码以前,去掉或者注释掉无关的代码; 测试下保证服务器能够正确启动。
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
Lua能够调用C函数的能力将极大的提升Lua的可扩展性和可用性。
对于有些和操做系统相关的功能,或者是对效率要求较高的模块,咱们彻底能够经过C函数来实现,以后再经过Lua调用指定的C函数。
对于那些可被Lua调用的C函数而言,其接口必须遵循Lua要求的形式,即typedef int (lua_CFunction)(lua_State L)。
简单说明一下,该函数类型仅仅包含一个表示Lua环境的指针做为其惟一的参数,实现者能够经过该指针进一步获取Lua代码中实际传入的参数。返回值是整型,表示该C函数将返回给Lua代码的返回值数量,若是没有返回值,则return 0便可。须要说明的是,C函数没法直接将真正的返回值返回给Lua代码,而是经过虚拟栈来传递Lua代码和C函数之间的调用参数和返回值的。
实例代码:
// testlua.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <string.h> #include <math.h> extern "C" { #include <lua.h> #include <lualib.h> #include <lauxlib.h> } //待Lua调用的C注册函数 static int add2(lua_State* L) { //检查栈中的参数是否合法,1表示Lua调用时的第一个参数(从左到右),依此类推。 //若是Lua代码在调用时传递的参数不为number,该函数将报错并终止程序的执行。 double op1 = luaL_checknumber(L,1); double op2 = luaL_checknumber(L,2); //将函数的结果压入栈中。若是有多个返回值,能够在这里屡次压入栈中。 lua_pushnumber(L,op1 + op2); //返回值用于提示该C函数的返回值数量,即压入栈中的返回值数量。 return 1; } //待Lua调用的C注册函数。 static int sub2(lua_State* L) { double op1 = luaL_checknumber(L,1); double op2 = luaL_checknumber(L,2); lua_pushnumber(L,op1 - op2); return 1; } //待Lua调用的C注册函数。 static int l_sin (lua_State *L) { double d = lua_tonumber(L, 1); /* get argument */ lua_pushnumber(L, sin(d)); /* push result */ return 1; /* number of results */ } int _tmain(int argc, _TCHAR* argv[]) { lua_State *L = luaL_newstate(); luaL_openlibs(L); //将指定的函数注册为Lua的全局函数变量,其中第一个字符串参数为Lua代码 //在调用C函数时使用的全局函数名,第二个参数为实际C函数的指针。 lua_register(L, "add2", add2); lua_register(L, "sub2", sub2); lua_register(L, "l_sin", l_sin); //在注册完全部的C函数以后,便可在Lua的代码块中使用这些已经注册的C函数了。 luaL_dofile(L,"test.lua"); //if (luaL_dostring(L,testfunc)) // printf("Failed to invoke.\n"); //const char *buf = "print('Hello World')"; //luaL_dostring(L,buf); lua_close(L); return 0; }
test.lua
function show() print("helloworld") print(add2(1.0,2.0)) print(sub2(20.1,19)) print(l_sin(1)) end show()
运行结果:
引用博文:http://www.cnblogs.com/stephen-liu74/archive/2012/07/23/2469902.html
由 sf0501 建立,小路依依 最后一次修改 2016-11-16
Lua中的table不是一种简单的数据结构,它能够做为其它数据结构的基础。如数组、记录、线性表、队列和集合等,在Lua中均可以经过table来表示。
在lua中经过整数下标访问表中的元素便可简单的实现数组。而且数组没必要事先指定大小,大小能够随须要动态的增加。
a = {} for i = 1,100 do a[i] = 0 end print("The length of array 'a' is " .. #a) squares = {1, 4, 9, 16, 25} print("The length of array 'a' is " .. #squares)
在Lua中习惯上数组的下表从1开始,Lua的标准库与此习惯保持一致,所以若是你的数组下标也是从1开始你就能够直接使用标准库的函数,不然就没法直接使用。
Lua中主要有两种表示矩阵的方法,第一种是用数组的数组表示。也就是说一个表的元素是另外一个表。
local N = 3 local M = 3 mt = {} for i = 1,N do mt[i] = {} for j = 1,M do mt[i][j] = i * j end end mt = {} for i = 1, N do for j = 1, M do mt[(i - 1) * M + j] = i * j end end
Lua中用tables很容易实现链表,每个节点是一个table,指针是这个表的一个域,而且指向另外一个节点(table)。例如,要实现一个只有两个域:值和指针的基本链表,代码以下:
list = nil for i = 1, 10 do list = { next = list ,value = i} end local l = list while l do --print(l.value) l = l.next end
虽然能够使用Lua的table库提供的insert和remove操做来实现队列,但这种方式实现的队列针对大数据量时效率过低,有效的方式是使用两个索引下标,一个表示第一个元素,另外一个表示最后一个元素。
List = {} --建立 function List.new() return {first = 0,last = -1} end --队列头插入 function List.pushFront(list,value) local first = list.first - 1 list.first = first list[first] = value end --队列尾插入 function List.popFront(list) local first = list.first if first > list.last then error("List is empty") end local value = list[first] list[first] = nil list.first = first + 1 return value end function List.popBack(list) local last = list.last if list.first > last then error("List is empty") end local value = list[last] list[last] = nil list.last = last - 1 return value end --测试代码 local testList = {first = 0,last = -1} local tableTest = 12 List.pushFront(testList,tableTest) print( List.popFront(testList))
简单实现堆栈功能,代码以下:
local stackMng = {} stackMng.__index = stackMng function stackMng:new() local temp = {} setmetatable(temp,stackMng) return temp end function stackMng:init() self.stackList = {} end function stackMng:reset() self:init() end function stackMng:clear() self.stackList = {} end function stackMng:pop() if #self.stackList == 0 then return end if self.stackList[1] then print(self.stackList[1]) end return table.remove(self.stackList,1) end function stackMng:push(t) table.insert(self.stackList,t) end function stackMng:Count() return #self.stackList end --测试代码 object = stackMng:new() object:init() object:push(1) object:pop()
在Lua中用table实现集合是很是简单的,见以下代码:
reserved = { ["while"] = true, ["end"] = true, ["function"] = true, ["local"] = true, } for k,v in pairs(reserved) do print(k,"->",v) end