类型
1 2 3 4 5 6 7 8 9 10 11 12
| a = nil print(type(a)) a = 1 print(type(a)) a = "hah" print(type(a)) a = true print(type(a)) a = {} print(type(a)) a = function () return 0 end print(type(a))
|
运算符
逻辑运算
and or not
false和nil是false, 其它是true(0也是true)
如果x为false或nil则给x赋初值v
三元运算符a?b:c
在lua里是
连接运算符
1 2 3 4
| .. print("Hello " .. "World") print(0 .. 1)
|
语句
1 2 3 4 5 6 7 8 9 10 11 12
| function show(a) if a > 10 then print("a > 10") elseif a > 0 then print("a = " .. a) else print("a < 0") end end show(15) show(5) show(-5)
|
1 2 3 4 5 6 7 8
| function show(a) while a > 10 do a = a / 2 end print(a) end show(15) show(5)
|
1 2 3 4 5 6 7
| function show(a) repeat a = a * 2 until a > 100 print(a) end show(15) show(5) show(101)
|
- for 语句(若以下三个参数被写成了表达式的形式,表达式只会被调用一次,且调用时间在循环开始前)
1 2 3 4 5 6
| for i = 1, 8, 3 do print("i = " .. i) end
|
- 泛型for
- 初始化,计算in后面表达式的值,表达式应该返回泛型for需要的三个值:迭代函数、状态常量、控制变量;如果返回值个数不够,用nil补足,多出被忽略
- 将状态常量和控制变量作为参数调用迭代函数
- 将迭代函数返回值赋给变量列表
- 如果返回的第一个值为nil,循环结束,否则执行循环体
- 回到2再次调用迭代函数
1 2 3 4 5 6 7 8 9
| a = {1, 2, 3, hello = "lua", world = "world"} for i, v in pairs(a) do print(i .. " " .. v) end
|
- ipairs(t):遇到nil则退出,只能对顺序表访问
1 2 3 4 5 6
| table={[2]=3, hello="hello"} for i,v in ipairs(table) do print(i, v) end
|
- pairs(t):可以遍历表中所有key,可以对dict访问
1 2 3 4 5 6
| table={hello="hello"} for i,v in pairs(table) do print(i, v) end
|
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| s, e = string.find("Hello Lua", "Lua") print(s, e) 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({1, 2, 3, 4, 5}))
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| f = string.find a = {"Hello Lua", "Lua"} print(f(table.unpack(a))) printResult = "" function link(...) local arg = { ... } for i, v in ipairs(arg) do printResult = printResult .. tostring(v) .. "\t" end printResult = printResult .. "\n" end link("a", "b") print(printResult)
|
闭包
当一个函数内部嵌套另一个函数定义时,内部函数体可以访问外部函数体的局部变量,这种特征称为词法定界。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| names = {"a", "b", "c"} grades = {c = 10, b = 7, a = 8} function sortbygrade (names, grades) table.sort(names, function (n1, n2) return grades[n1] > grades[n2] end) end sortbygrade (names, grades) for i, v in ipairs(names) do print(v) end
|
闭包是一个函数加上它可以正确访问的upvalues。闭包指值而不是函数,函数仅仅是闭包的一个原型声明。
1 2 3 4 5 6 7 8 9 10 11
| function newCounter() local i = 0 return function() i = i + 1 return i end end c1 = newCounter() print(c1()) print(c1())
|
闭包的应用
- 作为高阶函数的参数(table.sort的参数)
- 创建其他函数的函数(函数返回一个闭包)
- 回调函数(界面上按钮的回调)
- 创建一个安全的运行环境(比如要限制一个程序访问文件,可以用闭包来重定义io.open)
- 实现迭代器
闭包的实现原理
Lua编译一个函数(编译期概念)时,会为他生成一个原型(prototype),其中包含函数对应的虚拟机指令、函数用到的常量和一些调试信息。
运行时,每当Lua执行一个形如function...end
的表达式时,会创建一个新的数据对象,其中包含相应函数原型的引用及一个由所有upvalue引用组成的数组。这个数据对象称为闭包(运行时概念)。上述数组中的每个元素都是一个对upvalue的引用,可以通过该数据来访问外部局部变量。在Lua5.2之前,闭包中还包括一个对环境(environment)的引用,环境实质就是一个table,函数可以在该表中索引全局变量,从Lua5.2开始取消了闭包中的环境,引入_ENV来设置闭包环境。
upvalue初始存在栈上,由指针指向,当运行时,离开变量作用域时,会复制到upvalue的结构中去,改变指针的位置到upvalue结构中对应位置。Lua维护一条链表,该链表中每个节点对应一个打开的upvalue结构,打开的upvalue是指当前正指向栈局部变量的upvalue。当Lua创建一个新闭包时,会遍历当前函数所有的外部的局部变量,对于每个外部的局部变量,若在上述链表中能找到该变量,则重复使用该打开的upvalue,否则,Lua会创建一个新的打开的upvalue,并插入链表中。当局部变量离开作用域(即超过变量生命周期),这个打开的upvalue就会变成关闭的upvalue,并把它从链表中删除。一旦某个关闭的upvalue不再被任何闭包引用,那么它的存储空间就被回收。
一个函数有可能存取其更外层函数而非直接外层函数的局部变量。在这种情况下,创建闭包时,这个局部变量可能不在栈中。Lua使用flat闭包来处理这种情况。使用flat闭包,无论何时一个函数访问一个外部的局部变量并且该变量不在直接外部函数中,该变量也会进入直接外部函数的闭包中。当一个函数被实例化时,其对应闭包的所有变量要么在直接外部函数的栈中要么在直接外部函数的闭包中。
协程
1 2 3 4 5 6
| co = coroutine.create(function () print("Hello") end) print(coroutine.status(co)) coroutine.resume(co) print(coroutine.status(co))
|
coroutine.create()
创建
coroutine.resume()
继续
coroutine.status()
查看状态:suspend, running, dead
coroutine.yield()
挂起
coroutine.wrap()
创建(与create返回协程本身不同,wrap返回函数,创建完直接调用,无法查看状态)
coroutine.running()
返回当前正在运行的协程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| function receive (prod) local status, value = coroutine.resume(prod) return value end function send (x) coroutine.yield(x) end function producer () return coroutine.create(function () while true do local x = io.read() send(x) end end) end function filter (prod) return coroutine.create(function () local line = 1 while true do local x = receive(prod) x = string.format("%5d %s", line, x) send(x) line = line + 1 end end) end function consumer (prod) while true do local x = receive(prod) io.write(x, "\n") end end p = producer() f = filter(p) consumer(f)
|
数据结构
table是lua中唯一的数据结构,arrays, records, lists, queus, sets都用table实现
1 2 3 4 5 6 7 8
| list = nil list = {next = list, value = 3} local l = list while l do print(l.value) l = l.next end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| function newStack() return {""} end if string.find(_VERSION, "5.3") then table.getn = function(t) if t.n then return t.n else local n = 0 for i in pairs(t) do if type(i) == "number" then n = math.max(n, i) end end return n end end end function addString(stack, s) table.insert(stack, s) for i = table.getn(stack)-1, 1, -1 do if string.len(stack[i]) < string.len(stack[i+1]) then break end stack[i] = stack[i] .. table.remove(stack) end end stack = newStack() addString(stack, "hello ") addString(stack, "world") print(stack[table.getn(stack)])
|
Metatables允许我们改变table的行为,例如可以对两个table进行相加的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| a = {1, 2, 3} b = {7, 8, 9} mt = {} setmetatable(a, mt) setmetatable(b, mt) function mt.__add(c, d) local t = {} for i, v in ipairs(c) do t[#t + 1] = v end for i, v in ipairs(d) do t[#t + 1] = v end return t end a = a + b for i, v in ipairs(a) do print(v) end
|
任何一个表都可以是其他一个表的metatable,一组相关的表可以共享一个metatable(描述他们共同的行为)。一个表也可以是自身的metatable(描述其私有行为)
参考文献
- LUA中文教程
- 深入理解Lua的闭包一:概念、应用和实现原理(CSDN)