1. table:
a = {}; a[x]不等价于a["x"],a["x"]等价于a.x,a[0]不等价于a["0"];
#a取出的是table中连续数组的长度,从下标1开始;如果a[1] = 2; a[10000] = 3; #a是1;如果a[2] = 1; #a是0;
a = {[1]="a", [2]="b", [3]="c", "d", "e"}; 遍历结果是:d e c。因为数组下标默认从1开始,并且d作为1下标的值,e作为2下标的值;
a = {[1]="a", [2]="b", [3]="c",}和a = {[1]="a", [2]="b", [3]="c"; "one", "two", "three"}都是合法的;
2. 类型转换:
lua会自动进行类型转换:"10" + 1结果是11;"1.1"*"2"结果是2.2;"str" + 1运行出错;建议使用强制转换函数tonumber,tostring等;
3. 算数操作符:
==:表示类型和值都相等为true;两个table只有在引用同一个table时才会相等;在比较字符串和数字大小的时候,lua会引发错误,例如"2" < 12;
(a and b) or c 相当于 a ? b : c;
x = x or v 相等于 if (x) x = x; else x = v;
4. 赋值:
a, b, c = 1, 2; 结果a=1,b=2,c=nil;
a, b = 1, 2, 3; 结果a=1,b=2;
a, b, c = 0; 结果是a=0,b=nil,c=nil,特别注意;
x, y = y, x; 交换值,因为lua是先计算右边的表达式再进行复制;
a, b = fun(); 函数返回多个结果;
5. local:
local语句声明一个局部变量,lua的局部作用域主要有函数体和程序块(do-end then-else then-elseif then-end);
尽可能使用局部变量的原因:
(1)避免全局变量污染;
(2)局部变量的访问速度更快,可以优化性能;
(3)局部变量会在作用域结束的时候被垃圾回收器回收,减少不必要的内存浪费;
local foo = foo;暂存全局变量中的值,防止foo被修改,还可以加速对foo的访问;
6. for:
for i = 1, 5, 1 then do; i是局部变量,for循环完成之后会被销毁;
for i, v in ipairs(a) do;迭代数组元素;
for k, v in pairs(a) do;迭代所有元素;
7. 函数:
lua函数可以返回多个值的情况:
(1)x, y = foo(); x, y, z = 10, foo(); 如果函数调用是最后一个表达式,函数会返回尽可能多的返回值;
(2)x, y = foo(), 2; 如果函数调用是第一个表达式,函数只返回一个返回值;
(3)print(foo().."x"); 如果函数调用出现在表达式中,函数只返回一个返回值;
(4)f(g()); g将返回值个数调整和f的参数个数一致;
(5)return f(); 将返回f的所有返回值;
(6)return (f()); ()表达式将强制只返回一个返回值;
(7)t = {f()}; 可以使用数组包含所有返回值;
8. 变成参数:
访问变成参数...的两种方式:
(1)使用{...},想数组一样遍历其中的元素;
(2)select("#", ...)获取长度,select(i, ...)获取i下表的元素;
9. 闭包的使用场景:
单列模式,隐蔽数据;
例如:
local _a = 1; local _b = 2; local _c = 3; local _instance = {}; _instance.setvalue = function (a, b, c) _a = a; _b = b; _c = c; end _instance.print = function() print("instance value:", _a, _b, _c); end local module1 = {}; module1.getInstance = function() return _instance; end return module1;
回调函数;
例如:
function digitButton(digit) return Button{ label = tostring(digit); action = function() print(digit) end } end
重新定义某些函数,并可以通过闭包保持对旧函数的访问;
例如:
do local oldsin = math.sin math.sin = function(x) return oldsin(x * math.pi / 180) end end
10. 函数定义:
local fact = function(n) if n == 0 then return 1 else return n*fact(n-1) end end
上诉递归函数的定义会出现错误,解释器在解释fact(n-1)的时候,会找不到定义,因为fact还没有完成定义。需要修改成:
local fact
fact = function(n)
...
end
或者:
local function fact(n)
...
end
11. 尾调用:
只有在函数调用的最有一个语句是:return <func>(<args>); 才算是尾调用。
以下情况都不算尾调用:
function f() g() end; 因为还要处理g()的临时返回值;
function f() return g() + 1; 还要做一次加法计算;
function f() return (g()); 还要调整返回值;
使用尾调用的好处是:没有函数调用栈的开销;
12. 编译:
loadfile:编译一个lua文件,返回一个可执行的函数,调用函数就等于执行编译好的lua代码。
dofile:编译lua文件,并且会执行,而去会处理错误,代码相当于:
function dofile(filename)
local f = assert(loadfile(filename))
return f()
end
loadfile会返回错误,但是不会处理错误,所有需要用assert;
dofile会在每次调用都重新编译,因此,如果需要一次编译,多次运行,则使用loadfile;
例子1(编译时的运行环境):
module1.lua
gi = gi + 1;
print(gi);
test1.lua
local gi = 4;
local f = assert(loadfile(module1.lua));
f(); // 错误, 因为在编译的时候,没有找到全局变量gi的定义,如果将test1中的gi的local去掉,则可以正常运行。
// 说明loadfile的编译是在当前运行环境中进行编译,而不是在独立的运行环境中进行编译
print(gi); // 4
test2.lua
gi = 4;
local f = assert(loadfile(module1.lua));
f(); // 5 调用f就像调用普通lua函数一样,所以gi会被递增
f(); // 6
test3.lua
gi = 4;
dofile(module1.lua); // 5 每次都会重新编译,所以两次调用结果一样
dofile(module1.lua); // 5
例子2:
module1.lua
function foo()
print("foo");
end
test1.lua
local f = assert(loadfile(module1.lua));
foo(); // 错误,lua中函数的定义其实是一种赋值,是在运行时确定的,因此编译时无法找到foo的定义;
f();
foo(); // 打印foo,在运行f()后,定义就完成了;
上面例子中,foo是全局的函数变量,如果改为局部变量local foo = funtion() print("foo") end,在test1.lua中将访问不了foo,即时是在f()之后;
13. 错误:
assert(bool, message): lua中的断言可以中断程序的执行并打印错误堆栈;
error:抛出一个lua错误,可以传入任意类型,包括字符串,数字或者table来标示错误信息;
pcall:pcall称为保护调用,通常用来捕获异常和处理异常。pcall在调用一个函数的时候,如果函数正常运行,返回true和函数返回值;如果出现错误,返回false和错误消息;
xpcall:同样也是保护调用,和pcall不同的是,xpcall的第二个参数可以提供一个异常处理函数,让用户可以自己处理异常情况。正常运行时候和pcall的机制一样,当出现异常后,返回fasle和nil,并且会调用异常处理函数;
例子:
function foo()
if (s == 1) then return 5;
else error("foo error"); end
end
s = 1;
local status, data = pcall(foo); // status = true, data = 5
s = 2;
local status, error = pcall(foo); // status = false, error = "foo error"
assert和error都可以产生异常,但是error可以自定义错误的信息,更加灵活;
例子:
local status, data = xpcall(foo, function()
print(debug.traceback()); // 打印异常发生时的调用栈信息
end);