# String
string 可支持多行输入,格式为 [[ xxxx ]]
html = [[ | |
<html> | |
<head></head> | |
<body> | |
<a href="http://www.runoob.com/">菜鸟教程</a> | |
</body> | |
</html> | |
]] |
# 迭代器
pairs 迭代 table,会遍历表中所有的 key 和 value 值,不论这个 value 值是否为 nil
ipairs 迭代 table,会按照索引为 1 开始,遇到 nil 时就会停止当前遍历,适用于遍历数组形式的 table
自定义迭代函数,下方自定义了一个返回每次索引平方的迭代函数
-- 迭代函数 | |
function square(cnt,idx) | |
if(cnt>=idx)then | |
return nil | |
else | |
idx=idx+1 | |
return idx*idx | |
end | |
end | |
-- 使用 | |
for i in square,4,0 do | |
print(i) | |
end | |
--[[ 输出结果为 | |
1 | |
4 | |
9 | |
16 | |
]]-- |
# 元表
设置某个表的元表
table = {1,2,3} | |
-- 元表 | |
metatable = {} | |
-- 如果元表中设置了 __metatable 键值,那么就禁止用户访问该元表并修改该元表内的元素 | |
-- 如果元表 (metatable) 中存在 __metatable 键值,setmetatable 会失败 | |
table= setmetatable(table,metatable) |
获取某个表的元表
-- 如果元表中存在 __metatable, 那么返回的是 __metatable 的 value 值,否则返回的是 metatable 的内存地址 | |
metatable = getmetatable(table) |
# __index 元方法
当访问表中的某个索引时,若该表不存在该索引则生效。如果__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 |
# __newindex 元方法
当表添加新的键值对时生效。在对新索引键(newkey)赋值时(mytable.newkey = "新值 2"),会调用元方法,而不进行赋值。而如果对已存在的索引键(key1),则会进行赋值,而不调用元方法__newindex。
mytable = setmetatable({key1 = "value1"}, { | |
__newindex = function(mytable, key, value) | |
rawset(mytable, key, "\""..value.."\"") | |
-- 不能用 mytable [key]=value, 因为这么做会重新出发该方法,造成死循环 | |
end | |
}) | |
mytable.key1 = "new value" | |
mytable.key2 = 4 | |
print(mytable.key1,mytable.key2) | |
-- 输出结果为 new value "4" |
# 为表添加操作符
两个表进行运算时触发
,如 tab3=tab1+tab2
模式 | 描述 |
---|---|
__add | 对应的运算符 '+' |
__sub | 对应的运算符 '-' |
__mul | 对应的运算符 '*' |
__div | 对应的运算符 '/' |
__mod | 对应的运算符 '%' |
__unm | 对应的运算符 '-' |
__concat | 对应的运算符 '..' |
__eq | 对应的运算符 '==' |
__lt | 对应的运算符 '<' |
__le | 对应的运算符 '<=' |
以下演示了 __add
操作
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 | |
]]-- |
# __call 元方法
当表像一个方法一样调用时生效,如 tab ()
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 元方法
__tostring 元方法用于修改表的输出行为。如 print (tab), 默认输出的是 tab 的地址,__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 |
# 面向对象
构建一个继承代码
Person = {name="aaa",age=99} | |
--: 的定义类似于 Person.eat (Person), 这样就可以在内部调用 self | |
Person:eat = function() | |
--self 就是这个表本身 | |
print(self.name.."在吃饭") | |
end | |
-- 这个方法等于构造函数,可传入一个有数据的表 o | |
Person:New = function(o) | |
-- 声明一个空表,用于存新值 | |
-- 要注意设置为 local 防止外部调用 | |
local t = o or {} | |
-- 将 Person 表设置为 t 表的元表,这样 t 表就能访问到元表的数据 | |
setmetatable(t,self) | |
-- 因为返回值为 t,所以当 t 表访问 key 值时,会去判断 self 是否存在该 key,如果不存在,就会设置到 t 表中,不会影响元表数据 | |
self.__index=self | |
return t | |
end | |
-- 生成一个 Student 继承 Person | |
Student = Person:New() | |
-- 会存在新表 t 中生成这个 keyValue | |
Student.grade = 1 | |
-- 生成一个新对象 | |
stu1 = Student:New() | |
stu1:eat() -- 输出 aaa 在吃饭 | |
stu1.grade -- 输出 1 |
# 协程
lua 里的协程拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西
方法 | 描述 |
---|---|
coroutine.create() | 创建 coroutine,返回 coroutine, 参数是一个函数,新创建的协程并不会运行,而是处于挂起状态,当和 resume 配合使用的时候就唤醒函数调用 |
coroutine.resume() | 重启 coroutine,和 create 配合使用 |
coroutine.yield() | 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果 注:yield 的返回值也是 resume 传入的参数 |
coroutine.status() | 查看 coroutine 的状态 注:coroutine 的状态有三种:dead,suspended,running |
coroutine.wrap() | 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复 |
coroutine.running() | 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用 running 的时候,就是返回一个 coroutine 的线程号 |
# 案例
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) -- 第一次执行完会阻塞在这里,这时 r 还未赋值 | |
print("第二次协同程序执行输出", r) -- 这里的 r 值为 coroutine.resume 调用时传入的参数 | |
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,将协同程序唤醒,resume 操作成功返回 true,否则返回 false;
协同程序运行;
运行到 yield 语句;
yield 挂起协同程序,第一次 resume 返回;(注意:此处 yield 返回,参数是 resume 的参数)
第二次 resume,再次唤醒协同程序;(注意:此处 resume 的参数中,除了第一个参数,剩下的参数将作为 yield 的参数)
yield 返回;
协同程序继续运行;
如果使用的协同程序继续运行完成后继续调用 resume 方法则输出:cannot resume dead coroutine
# 注意事项
# Table 相关
lua 中的 table 创建数组时第一个索引 key 为 1,而不是 0
#在获取 table 的长度时实际上获取的是数组最大索引的 key,而不是数量,如
tab = {1,2,3} | |
#tab -- 这时的值为 3 | |
tab = {key1="val1",key2="val2"} | |
#tab -- 这时的值为 0 | |
tab = {} | |
tab[1]=10 | |
tab[2]=20 | |
tab[10]=100 | |
#tab -- 这时的值为 10 |
lua 中数组的索引可以设置为负数,如
tab={} | |
tab[-5] = 10 | |
print(tab[-5]) -- 这是合法的 |
当一个 table 被其他变量也引用时,只有将这些变量都置空才会释放改表的内存,如
tab1 = {1,2,3} | |
tab2 = tab1 | |
tab1 = nil --{1,2,3} 内存并不会释放,tab1 变量释放了 | |
tab2 = nil -- 引用全部清除,{1,2,3} 内存也被释放 |
table 数组的数据移除如果直接让改索引等于 nil,那么该索引后面的值的索引并不会 - 1,而只是该索引值变成 nil,如
tab = {1,2,3} | |
tab[2]=nil -- 这时 tab 实际内容为 {1,nil,3} | |
-- 如果要彻底移除,可以使用 table.remove | |
table.remove(tab,2) -- 这时 tab 实际内容为 {1,3} |
# If 相关
在 if 判断时 0 代表的是 true,除了 false 和 nil 代表 false 外,其他代表的都是 true
# Function 相关
# 可选参数
在获取一个函数的所有可选参数时,可以通过
arg
字段来获取,但这个字段的最后一位会保存当前可选参数的参数总个数,所以遍历的时候会多遍历一次。如果要过滤这个参数数量,那么可以声明一个局部变量对参数进行转存,如local args = {...}
, 如果要获取参数个数的话可以通过cnt = #args
来获得
# 函数中 "." 和 ":" 的区别
在 lua 中使用 “:” 定义的函数会自动传入一个名为 self 的变量,这个变量是隐含的,self 同 c++ 中的 this 一样,表示当前对象的指针:而 “.” 定义的函数中没有 self。
# String 相关
print 输出字符串数字相加会进行算数运算,如
print("2"+"6") -- 输出值为 8 | |
print("2"+6) -- 输出值为 8 | |
print("2+6") -- 输出值为 2+6 |
#在获取包含中文 string 时,一个中文占 2 字节,返回的是字节数,如
#"abcd" -- 长度为 4 | |
#"哈哈ab" -- 长度为 6 |