Lua学习
- 注释代码:
--[[
print(10)
--]]
- 重新启用已注释的代码:
在第一行行首多添加一个"-"
---[[
print(10)
--]]
-
Lua默认将一个没有声明的变量视为全局变量
如果想删除一个全局变量,只需要将其赋值为nil -
type(X) 返回的值永远是string,无论X取值如何,因为type函数总是返回字符串
-
Lua语言中整数没有类型,用number泛指数字类型
-
修改部分字符串函数
gsub(a,"1","2")
意思是将a字符串中的1改为2
- Lua中声明一个字符串可以用" "也可以用’ ’
a="line"
b='line'
- /加最多3个十进制数字表示以ASCII码为索引的字符
如
a 和 /97 所表示的内容一致
-
可以在两个方括号之间加入任意个等号作为呼应
如
[=[
]=]
为一组字符串 -
两个字符串连接用. .(注意前加空格),在Lua中,字符串是不可改变的值,连接字符串只会创建一个新的字符串
print(10 ..20)
输出为1020
- tonumber()将一个字符串转换成数字(显式转换)
a={}
i=10 j=11
a[j]="abc"
print(a[tonumber(i)+1])
输出结果为abc
- 在字符串前加#来获得字符串长度
print(#a)
- table没有固定的大小,动态分配内存
a={}
创建了一个table表
注意:在Lua中,索引值通常习惯用1开始,并不是0
但可以手动设置table索引为0(不推荐)
a={[0]="zero","one"}
这样zero的索引就是0,one的索引就是1了
但不推荐!!!
- for循环
for i=1,1000 do a[i]=1*2 end
表示数组a中1-1000每个元素做i*2运算
-
如果table索引为空,则会返回nil
-
可以用语法糖 a.x来索引table,a.x表示用字符串x来索引table,也可以用运算符来作为索引符,但需要显式表达["+"]
a={["+"]=10}
x="y"
a[x]=10
s="+"
print(a[x])
print(a.y)
print(a[s])
返回值都为10
- 长度操作符#用于返回一个数组或线性表中最后一个索引值
for i=1,#a do
print(a[i])
end
输出所有行
a[#a]=nil
删除最后一个值
a[#a+1]=v
将v添加至列表末尾
-
当数组中包含空隙时,Lua默认该数组以空隙位置结束.如果要返回该数组的最大正索引数,应使用table.manx(a)
-
x%1的结果是x的小数部分,x%0.1的结果是x精确到小数点后两位的结果,x#0.01是x精确到小数点后两位的结果
x=math.pi
print(x%1)
结果为0.14159265358979
x=math.pi
print(x%0.01)
结果为0.001592653589793
-
在Lua中,~=相当于!=
-
在Lua中,只能对两个数字或两个字符串做大小性比较,而数字和字符串之外的其他类型只能进行相等性或不等性比较
-
2<15是true,但"2"<"15"是false,因为是按照字母次序来比较的
-
在Lua中,对于and操作符来说,如果第一个操作数为假,则返回第一个操作数,否则返回第二个操作数.对于or操作符来说,如果第一个操作数为真,则返回第一个操作数,否则返回第二个操作数
-
and和or和C语言中的||,&&一样,都是用"短路求值",也就是说,只会在需要的时候才回去评估第二个操作数,如果第一个为真,则不判断第二个操作数.
if not x then x=v end
上保险
表示如果x为假时,将x设为v.
max=(x>y) and x or y
等价于C语言中的(前提是x不为false)
max=(x>y)?x:y
a={x=0,"r"}
等价于
a={["x"]=0,[2]="r"}
- 在table中分号视为逗号,用于分割不同类的字符
a={x=10;"one"}
- Lua允许多重赋值
a,b=10,20
则a等于10,b等于20
x,y=y,x
表示x与y互换
- Lua总是会将等号右边值的个数调整到与左边个数相等,如果不够则多余的变量赋值为nil,如果超出,则删除超出部分.
a,b,c=0
print(a,b,c)
结果为 0 nil nil
-
在Lua中,用local声明局部变量,在开发中尽可能的多使用局部变量是一种好习惯
local foo=foo
这句代码创建了一个foo,并用全局变量的值初始化它
这句代码的作用是,如果后续操作改变了全局变量foo,那么这里的局部变量就保存了它本来的值
-
if,for,while以end为终止符,repeat以until为终止符
-
if then else语句
if a<b then return a else return b end
如果a小于b取a,否则取b
-
在Lua中,不支持switch语句
-
在Lua中,for有两种形式:数字型和泛型,38-41讲的是数字型
for i=exp1,exp2,exp3 do <执行体> end
表示i从exp1变化到exp2,每次变化都以exp3为步长,这里exp3可以写,如果不写默认为1
for i=10,1,-1 do print(i) end
输出结果为 10,9,8,7,6,5,4,3,2,1
- 如果不想给循环次数设置上限的话,可以使用常量 math.huge
for i=1,math.huge do <执行体> end
- 在for循环中的三个表达式都是在循环开始时一次性求值的
for i=1,f(x) do print(i) end
这里f(x)只会运算一次
- 在for循环中,控制变量都会被自动转化为局部变量,因此在end之后就不存在了
for i=1,10 do print(i) end
print(i)
输出为1,2,3,4,5,6,7,8,9,10,nil
- Lua的基础库提供了ipairs,是用于遍历数组的迭代器函数
for i,v in ipairs(a) do print(v) end
打印数组a的所有值
for k in pairs(t) do print(k) end
注意这里是pairs,迭代table元素用pairs
打印table t 中的所有key
- 标准库提供了几种迭代器:迭代文件中每行的(io.lines),迭代table元素的(pairs),迭代数组元素的(ipairs),迭代字符串中单词的(string,gmatch)等等
- 泛型for和数字型for一样,for中的循环变量也是局部变量
- 在Lua中,break和return只能是一个块的最后一条语句
o.foo(0.x)
等价于
o:foo(x)
这里的冒号操作符使调用o.foo的同时将o隐含地作为函数的第一个参数
- 函数的实参与形参数量也遵循多重赋值原则,参考31
- Lua中的函数允许返回多个结果
s,e=string.find("hello Lua users","Lua")
print(s,e)
查找字符串函数
返回值为 7 9
- 如果一个函数调用不是一系列表达式的最后一个元素,那么只会产生一个值
假设有以下定义
function foo2() return "a","b" end
x,y=foo2(),20
这里输出x=“a”,y=20
50.当函数出现在一个表达式中,Lua会将其返回值数量调整为1
假设有以下定义
function foo2() return "a","b" end
print(foo2(),1)
输出为a 1
- table构造式可以完整地接受一个函数调用的所有结果
假设有以下定义
function foo2() return "a","b" end
t={foo2()}
在这里t={“a”,“b”}
注意: 这种行为只有当一个函数调用作为最后一个元素时才会发生,而在其他位置上的函数调用总是只产生一个结果值
t={foo0(),foo2()}
在这里 t[1]=nil,t[2]=“a”
- return 语句可以返回函数f的所有返回值
- 也可以将一个函数放入一堆圆括号中,从而迫使它只返回一个结果
print((foo2()))
这里输出 a
- unpack函数.它接受一个数组作为参数,并从下标1开始返回该数组的所有元素
print(unpack{10,20,30})
输出 10 20 30
- … (变长参数) 表示该函数可接受不同数量的实参
function add(...)
local s=0
for i,v in ipairs{...} do
s=s+v
end
return s
end
print(add(3,4,12,13,12))
输出为44
function foo(a,b,c)
等同于
function foo(..)
local a,b,c=...
end
- 遍历变长参数只需要使用表达式{…},但无法遍历包含nil的变长参数.调用select时,必须传入一个固定参数selector,如果selector为数字n,那么select返回它的第n个可变实参,否则,selector只能为字符串#,这样会返回变长参数的总数.
for i=1,select('#', ...) do
local arg=select(i, ...)
<循环体>
end
这样会得到第i个参数
select会返回所以变长参数的总数,其中包括nil
- 在Lua中,函数是一种"第一类值",也就是说可以把一个函数存储到一个变量中,也可以存储在table字段中.
a={p=print}
a.p("hello world")
输出hello world
- 在Lua中,函数具有特定的"词法域",这是指一个函数可以嵌套在另一个函数里,内部函数可以访问外部函数的变量.
a={p=print}
print=math.sin
a.p(print(1))
输出0.8414709848079
- sort排序函数
假设有一个table如下
network={
{name="a",IP="1"},
{name="b",IP="2"},
{name="c",IP="3"},
{name="d",IP="4"},
}
如果想以name为字段,按反向的字符顺序来对这个table排序的话
table.sort(network,function(a,b) return (a.name>b.name) end)
定义递归的局部函数时要注意!
local fact=function(n)
if n==0 then teturn 1
else teturn n*fact(n-1)
end
end
错误!!!
local fact
fact=function(n)
if n==0 then return 1
else return n*fact(n-1)
end
end
正确!!!
一般先定义一个局部变量,再定义函数本身
- 尾调用不耗费栈空间
这里注意尾调用的判断:只有当一个函数在调用完另一个函数之后,没有事情做才是尾调用.(可以是非常复杂的函数)
function f(x) return g(x) end
所以一个程序可以拥有无限个尾调用,在调用以下函数时,传入任何数字作为参数都不会造成栈溢出
- 一个迭代器结构通常涉及到两个函数:迭代器本身和一个用于创建该迭代器的工厂函数
function values(t)
local i=0
return function() i=i+1 return t[i] end
end
在该例中,values就是一个工厂
- 泛型for的语法如下:
for <var-list> in <exp-list> do
<body>
end
其中, var-list是一个或多个变量名的列表,以逗号分离:exp-list是一个或多个表达式,以逗号分离
下面是一个例子
for k,v in pairs(t) do print(k,v) end
一般来说,变量列表也只有一个变量
-
变量列表的第一个值成为"控制变量",该值绝不会为nil,因为当它为nil时,循环就结束了
-
在Lua中,数字,字符串都是可以连接的,但是对于两个table类型则不能直接相加.但是可以通过元表来修改一个值的行为.比如,现在有两个table类型的变量a和b,可以通过metatable定义表达式a+b.
按照以下步骤进行:
1)先判断a和b两者之一是否有元表
2)检查该元表中是否有一个叫__add的字段(注意是两个下划线
3)如果找到该字段,则调用,这个值对应的是一个metamethod
4)调用__add对应的metamethod计算a+b的值 -
使用setmetatable来设置一个table或userdata类型变量的元表,
t = {}
local t1 = {}
setmetatable(t, t1)
print(getmetatable(t))
print(t1)
将t1设为t的元表 可以发现输出结果一样
输出为
table: 001F9558
table: 001F9558
- 在默认情况下字符串库为所有的字符串都设置了一个元表,而 其他类型在默认情况下则没有
print(getmetatable("Hello World"))
print(getmetatable(10))
输出为
table: 00B29918
nil
- 函数print总是调用tostring来格式化其输出
print({})
输出为
table: 00CF93A0
- 当访问一个table中不存在的字段时,这些访问会促使解析器去查找一个叫__index的元方法.如果没有__index这个元方法,那么返回值为nil.否则就由这个元方法来提供结果.__index元方法还可以是一个table.
Windows = {}
Windows.default = { width = 100}
Windows.mt = {}
function Windows.new(o)
setmetatable(o, Windows.mt)
return o
end
Windows.mt.__index = function (table, key)
return Windows.default[key]
end
local win = Windows.new({x = 10, y = 10})
print(win.x)
print(win.width)
输出结果为
10
100
因为第二个执行了__index元方法
- __newindex元方法用于table的更新,而__indexx用于table的查询.调用rawset(t,k,v)可以绕过元方法来直接设置table t中key为k的value v.