类型和变量
Booleans
两个取值 false 和 true。但要注意 Lua 中所有的值都可以作为条件。在控制结构的条 件中除了 false 和 nil 为假,其他值都为真。所以 Lua 认为 0 和空串都是真。
Numbers
表示实数,Lua 中没有整数。一般有个错误的看法 CPU 运算浮点数比整数慢。事实 不是如此,用实数代替整数不会有什么误差(除非数字大于 100,000,000,000,000)。Lua 的 numbers 可以处理任何长整数不用担心误差。你也可以在编译 Lua 的时候使用长整型 或者单精度浮点型代替 numbers,在一些平台硬件不支持浮点数的情况下这个特性是非 常有用的,具体的情况请参考 Lua 发布版所附的详细说明。和其他语言类似,数字常量 的小数部分和指数部分都是可选的,数字常量的例子: 4 0.4 4.57e-3 0.3e12 5e 20
Strings
指字符的序列。lua 是 8 位字节,所以字符串可以包含任何数值字符,包括嵌入的 0。 这意味着你可以存储任意的二进制数据在一个字符串里。Lua 中字符串是不可以修改的, 你可以创建一个新的变量存放你要的字符串,如下:
代码语言:javascript复制a = "one string"
b = string.gsub(a, "one", "another") -- change string parts
print(a)
--> one string
print(b)
--> another string
string 和其他对象一样,Lua 自动进行内存分配和释放,一个 string 可以只包含一个 字母也可以包含一本书,Lua 可以高效的处理长字符串,1M 的 string 在 Lua 中是很常见 的。可以使用单引号或者双引号表示字符串 a = “a line” b = ‘another line’ 运行时,Lua 会自动在 string 和 numbers 之间自动进行类型转换,当一个字符串使 用算术操作符时,string 就会被转成数字。
代码语言:javascript复制print("10" 1)
--> 11
print("10 1")
--> 10 1
print("-5.3e - 10" * "2")
--> -1.06e-09
print("hello" 1)
-- ERROR (cannot convert "hello")
反过来,当 Lua 期望一个 string 而碰到数字时,会将数字转成 string。 print(10 … 20) --> 1020 …在 Lua 中是字符串连接符,当在一个数字后面写…时,必须加上空格以防止被解释 错 尽管字符串和数字可以自动转换,但两者是不同的,像 10 == "10"这样的比较永远 都是错的。如果需要显式将 string 转成数字可以使用函数 tonumber(),如果 string 不是正 确的数字该函数将返回 nil。
代码语言:javascript复制line = io.read()
-- read a line
n = tonumber(line)
-- try to convert it to a number
if n == nil then
error(line .. " is not a valid number")
else
print(n*2)
end
反之,可以调用 tostring()将数字转成字符串,这种转换一直有效:
代码语言:javascript复制print(tostring(10) == "10")
--> true
print(10 .. "" == "10")
--> true
想要输出正确数字后面要接空格,再接…
表达式
关系运算
< > <= >= == ~= 不等~=
逻辑运算符
and or not 逻辑运算符认为 false 和 nil 是假(false),其他为真,0 也是 true. and 和 or 的运算结果不是 true 和 false,而是和它的两个操作数相关。 a and b – 如果 a 为 false,则返回 a,否则返回 b a or b – 如果 a 为 true,则返回 a,否则返回 b –例如:
代码语言:javascript复制print(4 and 5)
--> 5
print(nil and 13)
--> nil
print(false and 13)
--> false
print(4 or 5)
--> 4
print(false or 5)
--> 5
一个很实用的技巧:如果 x 为 false 或者 nil 则给 x 赋初始值 v x = x or v
C 语言中的三元运算符
a ? b : c 在 Lua 中可以这样实现: (a and b) or c
基本语法
赋值语句
遇到赋值语句 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. 变量个数 < 值的个数 多余的值会被忽略
控制结构语句
控制结构的条件表达式结果可以是任何值,Lua 认为 false 和 nil 为假,其他值为真。 if 语句,有三种形式: if conditions then then-part end; if conditions then then-part else else-part end; if conditions then then-part elseif conditions then elseif-part … —>多个 elseif else else-part end; while 语句: while condition do statements; end; repeat-until 语句 repeat statements; until conditions; for 语句有两大类: 第一,数值 for 循环: for var=exp1,exp2,exp3 do loop-part end for 将用 exp3 作为 step 从 exp1(初始值)到 exp2(终止值),执行 loop-part。其中 exp3 可以省略,默认 step=1 如果想颠倒来
代码语言:javascript复制for i=10,1,-1 do
print(i)
end
-- print all values of array 'a'
for i,v in ipairs(a) do print(v) end
范型 for 遍历迭代子函数返回的每一个值。 再看一个遍历表 key 的例子: – print all keys of table ‘t’ for k in pairs(t) do print(k) end
函数
多返回值
函数多值返回的特殊函数 unpack,接受一个数组作为输入参数,返回数组的所有元 素。unpack 被用来实现范型调用机制,在 C 语言中可以使用函数指针调用可变的函数, 可以声明参数可变的函数,但不能两者同时可变。在 Lua 中如果你想调用可变参数的可 变函数只需要这样: f(unpack(a)) unpack 返回 a 所有的元素作为 f()的参数
代码语言:javascript复制f = string.find
a = {"hello", "ll"}
print(f(unpack(a)))
--> 3 4
string.find 默认情况下返回两个值, 即查找到的子串的 起下标标和止下标 预定义的 unpack 函数是用 C 语言实现的,我们也可以用 Lua 来完成:
代码语言:javascript复制function unpack(t, i)
i = i or 1
if t[i] then
return t[i], unpack(t, i 1)
end
end
相当于把表的每一个item都作为参数依次传入
可变参数
Lua 函数可以接受可变数目的参数,和 C 语言类似在函数参数列表中使用三点(…) 表示函数有可变的参数。Lua 将函数的参数放在一个叫 arg 的表中,除了参数以外,arg 表中还有一个域 n 表示参数的个数。 例如,我们可以重写 print 函数:
代码语言:javascript复制printResult = ""
function print(...)
for i,v in ipairs(arg) do
printResult = printResult .. tostring(v) .. "t"
end
printResult = printResult .. "n"
end
如果是传入…,在函数内使用arg表
代码语言:javascript复制function g (a, b, ...) end
g(3) a=3, b=nil, arg={n=0}
g(3, 4) a=3, b=4, arg={n=0}
g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2}
域 n 表示参数的个数 举个具体的例子,如果我们只想要 string.find 返回的第二个值。一个典型的方法是 使用哑元(dummy variable,下划线): local _, x = string.find(s, p) – now use `x’
再论函数
a = {p = print}
a.p(“Hello World”)
–> Hello World
print = math.sin – print' now refers to the sine function a.p(print(1)) --> 0.841470 sin = a.p --
sin’ now refers to the print function
sin(10, 20)
–> 10 20
table.sort
内部是快速排序法实现 table 标准库提供一个排序函数,接受一个表作为输入参数并且排序表中的元素。这 个函数必须能够对不同类型的值(字符串或者数值)按升序或者降序进行排序。Lua 不 是尽可能多地提供参数来满足这些情况的需要,而是接受一个排序函数作为参数(类似 C 的函数对象),排序函数接受两个排序元素作为输入参数,并且返回两者的大小关系, 例如:
代码语言:javascript复制network = {
{name = "grauna", IP = "210.26.30.34"},
{name = "arraial", IP = "210.26.30.23"},
{name = "lua", IP = "210.26.23.12"},
{name = "derain", IP = "210.26.23.20"},
}
for i,v in ipairs(network) do print(v.name) end
table.sort(network, function (a,b)
return (a.name > b.name)
end)
for i,v in ipairs(network) do print(v.name) end
输出 grauna arraial lua derain
lua grauna derain arraial
- table中不能有nil table.sort是排序函数,它要求要排序的目标table的必须是从1到n连续的,即中间不能有nil。
- 重写的比较函数,两个值相等时不能return true 此外,当比较函数没有写的时候,table.sort默认按照lua里面的排序规则升序排序; 当额外写了比较函数时,相当于用你额外写的比较函数重载了lua中自带的“<”操作符。 这就有一个特别要注意的问题,当两个数相等的时候,比较函数一定要返回false! 如果两个值相等都, 排序函数返回true时则会报错 invalid order function for sorting
table.sort(tmpQueue, function(a, b)
if (a == nil or b == nil) then
return (a.endTime < b.endTime) --此处千万不能用小于等于,不然顺序错乱
end)
代码语言:javascript复制network = {
{name = "grauna", IP = "210.26.30.34"},
{name = "arraial", IP = "210.26.30.23"},
{name = "lua", IP = "210.26.23.12"},
{name = "grauna", IP = "210.26.23.20"},
}
for i,v in ipairs(network) do print(v.name) end
table.sort(network, function (a,b)
return (a.name >= b.name)
end)
for i,v in ipairs(network) do print(v.name) end
输出 grauna arraial lua grauna
lua grauna arraial grauna
闭包
函数内部有函数 在匿名函数内部 grades 不是全局变量也不是局部变量,我们称作外部的局部变 量(external local variable)或者 upvalue。
代码语言:javascript复制function newCounter()
local i = 0
return function() -- anonymous function
i = i 1
return i
end
end
c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2
c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2
匿名函数使用 upvalue i 保存他的计数,当我们调用匿名函数的时候 i 已经超出了作 用范围,因为创建 i 的函数 newCounter 已经返回了。然而 Lua 用闭包的思想正确处理了 这种情况。简单的说,闭包是一个函数以及它的 upvalues。如果我们再次调用 newCounter, 将创建一个新的局部变量 i,因此我们得到了一个作用在新的变量 i 上的新闭包。 闭包在完全不同的上下文中也是很有用途的。因为函数被存储在普通的变量内我们 可以很方便的重定义或者预定义函数。通常当你需要原始函数有一个新的实现时可以重 定义函数。例如你可以重定义 sin 使其接受一个度数而不是弧度作为参数:
代码语言:javascript复制do
local oldSin = math.sin
local k = math.pi/180
math.sin = function (x)
return oldSin(x*k)
end
end
利用同样的特征我们可以创建一个安全的环境(也称作沙箱,和 java 里的沙箱一样), 当我们运行一段不信任的代码(比如我们运行网络服务器上获取的代码)时安全的环境 是需要的,比如我们可以使用闭包重定义 io 库的 open 函数来限制程序打开的文件。
代码语言:javascript复制do
local oldOpen = io.open
io.open = function (filename, mode)
if access_OK(filename, mode) then
return oldOpen(filename, mode)
else
return nil, "access denied"
end
end
end
非全局函数
递归函数先声明 上面这种方式导致 Lua 编译时遇到 fact(n-1)并不知道他是局部函数 fact,Lua 会去查 找是否有这样的全局函数 fact。为了解决这个问题我们必须在定义函数以前先声明:
代码语言:javascript复制local fact
fact = function (n)
if n == 0 then
return 1
else
return n*fact(n-1)
end
end
迭代器与泛型for
迭代器与闭包
迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素 举一个简单的例子,我们为一个 list 写一个简单的迭代器,与 ipairs()不同的是我们 实现的这个迭代器返回元素的值而不是索引下标:
代码语言:javascript复制function list_iter (t)
local i = 0
local n = table.getn(t)
return function ()
i = i 1
if i <= n then return t[i] end
end
end
t = {10, 20, 30}
for element in list_iter(t) do
print(element)
end
可以自己写些遍历的条件,例如取table里满足条件的item
范性for
ipairs与pairs区别
ipairs 仅仅遍历值,按照索引升序遍历,索引中断停止遍历。即不能返回 nil,只能返回数字 0,如果遇到 nil 则退出。它只能遍历到集合中出现的第一个不是整数的 key。必须是连续的,从1开始,只要中间为nil,即断开 pairs 能遍历集合的所有元素。即 pairs 可以遍历集合中所有的 key,并且除了迭代器本身以及遍历表本身还可以返回 nil。
代码语言:javascript复制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
编译,运行,错误信息
assert断言
local f = assert(loadstring("return " … l))
require 函数
Lua 提供高级的 require 函数来加载运行库。粗略的说 require 和 dofile 完成同样的功 能但有两点不同:
- require 会搜索目录加载文件
- require 会判断是否文件已经加载避免重复加载同一文件。由于上述特征,require 在 Lua 中是加载库的更好的函数。 require 的另一个功能是避免重复加载同一个文件两次。Lua 保留一张所有已经加载 的文件的列表(使用 table 保存)。如果一个加载的文件在表中存在 require 简单的返回; 表中保留加载的文件的虚名,而不是实文件名。所以如果你使用不同的虚文件名 require 同一个文件两次,将会加载两次该文件。比如 require "foo"和 require “foo.lua”,路径为 "?;?.lua"将会加载 foo.lua 两次。
C Package
local path = “/usr/local/lua/lib/libluasocket.so” – or path = “C:windowsluasocket.dll” local f = assert(loadlib(path, “luaopen_socket”)) f() – actually open the library
异常与错误处理
lua实现try catch
当我们的Lua程序遇到有需要保护的代码或者方法时(即使程序异常,也只是抛出异常信息,而不是让程序崩溃),Lua为我们提供了两种解决的办法,这两种方法可以让我们捕获异常,因此封装自己的tryCatch函数。 1.pcall调用 2.xpcall调用 相同点: 当程序正常时,返回true,被执行函数的返回值 不同点: 1.参数不同 pcall(fun) ,参数只有一个被调用函数 xpcall(fun,errHandleFun),参数是被调用函数,错误函数处理 2.执行结果 pcall:返回错误信息时,已经释放了保存错误发生情况的栈信息。 xpcall:会在栈信息释放之前调用错误处理程序(可以使用debug库收集错误信息) 3.返回结果 pcall 返回 nil , 错误信息 xpcall返回nil , 无错误信息
代码语言:javascript复制local fun=function ( b)
local a=1;
print(a b);
return a b;
end
tryCatch=function(fun)
local ret,errMessage=pcall(fun);
print("ret:" .. (ret and "true" or "false" ) .. " nerrMessage:" .. (errMessage or "null"));
end
xTryCatchGetErrorInfo=function()
print(debug.traceback());
end
xTryCatch=function(fun)
local ret,errMessage=xpcall(fun,xTryCatchGetErrorInfo);
print("ret:" .. (ret and "true" or "false" ) .. " nerrMessage:" .. (errMessage or "null"));
end
print("n------A------n")
tryCatch(fun("1"));
print("n------B------n")
xTryCatch(fun("1"));
print("n------C------n")
输出 ------A------
2 ret:false errMessage:attempt to call a number value
------B------
2 stack traceback: PInLua.lua:13: in function PInLua.lua:12 [C]: in function ‘xpcall’ PInLua.lua:16: in function ‘xTryCatch’ PInLua.lua:25: in main chunk [C]: ? ret:false errMessage:null
------C------
协同程序
Lua中的协程和unity协程的区别,最大的就是其不是抢占式的执行,也就是说不会被主动执行类似MoveNext这样的操作,而是需要我们去主动激发执行,就像上一个例子一样,自己去tick这样的操作。 Lua中协程关键的三个API: coroutine.create()/wrap: 构建一个协程, wrap构建结果为函数,create为thread类型对象 coroutine.resume(): 执行一次类似MoveNext的操作 coroutine.yield(): 将协程挂起 比较简易,可以写也给例子测试一下:
代码语言:javascript复制local func = function(a, b)
for i= 1, 3 do
print(i, a, b)
end
end
local func1 = function(a, b)
for i = 1, 3 do
print(i, a, b)
coroutine.yield()
end
end
co = coroutine.create(func)
coroutine.resume(co, 1, 2)
--此时会输出 1 ,1, 2/ 2,1,2/ 3, 1,2
co1 = coroutine.create(func1)
coroutine.resume(co1, 1, 2)
--此时会输出 1, 1,2 然后挂起
coroutine.resume(co1, 3, 4)
--此时将上次挂起的协程恢复执行一次,输出: 2, 1, 2 所以新传入的参数3,4是无效的
coroutine.resume(co1, 3, 4)
coroutine.resume(co1, 3, 4)
coroutine.resume(co1, 3, 4)
coroutine.resume(co1, 3, 4)
输出 1 1 2 2 1 2 3 1 2 1 1 2 2 1 2 3 1 2