从0开始学lua,给自己记一个笔记,使用书籍 lua程序设计(第二版),电子工业大学出版社
此篇包含的内容有
-
类型
-
表达式
-
语句
-
函数
下面开始正式内容,有错误欢迎指出,小菜一个
1类型
-
table(表)
具有哈希结构的关联数组
可以通过除了nil外仍和类型的值来索引它,当需要创建新条目(entry)时,table会自动增长
lua中主要的、仅有的数据结构机制
lua中用table来表示模块(module)、包(packge)、对象(object)例如 io.read 实际是 io模块中的read函数,通过key read在io表中进行索引
lua中table既不是“值”也不是“变量”而是“对象”,可以当做一种动态分配的对象,程序仅持有一个对他们的引用(||指针)lua不会暗中产生table的副本或者创建新的table
table永远是匿名的anonymous,一个持有table的变量与table自身没有固定的关联性
当一个程序再也没有对一个table的引用时,lua的垃圾收集器(garbage collector)最终会删除该table并复用他的内存当table中的某个元素没有被初始化时,它的内容就为nil。通过将nil赋予table某个元素来删除该元素,类似于全局变量。因为lua将全局变量存储在一个普通的table中
数组通常你以1作为索引的起始值,并且有很多机制依赖于此惯例
长度操作符 # 用于返回一个数组或线性表的最后一个索引值(或为其大小 ) 如
for i=1 #a do
print(a[i])
end
- lua将nil作为界定数组结尾的标志,当一个数组有空隙(hole)即中间有值为nil时,长度操作符会认为这些nil元素就是结尾。如果需要处理此类数据,使用table.maxn,他会返回table的最大正索引数
-
userdata(自定义类型)
可以将任意C语言类型的数据存储到lua变量中 lua中没有太多的预定义操作 只能惊醒赋值和相等性测试
-
boolean(布尔)
true和false lua中的任何一个值都可以表示一个条件。不同于有些语言,lua中只有false和nil为假,其他任何值都为真
-
nil(空)
只有一个值,主要用于和其他任何值。一个全局变量在第一次赋值前的默认值就是nil
-
thread(线程)
具体细节之后再讲内容较多
-
funtion(函数)
lua中作为第一类值来看待,函数可以存储在变量中 并可以当做参数传递,(不同于c++,函数是第二类值,只能用过指向函数地址的函数指针来传递) 可以通过参数传递给其他函数 也可以作为其他函数的返回值
lua所有的标准库都是用C语言写的,标准库中包括对字符串的操作、table的操作、I/O、操作系统的功能调用、数学函数和调试函数
-
string(字符串)
- 不能像c一样直接修改字符串的值,每一次修改需要重新创建一个新的字符串
string a="11"; a+="2"; --错误
string a="11" a="112" --正确
string a="11".."2" --正确,lua中 .. 为字符串连接符
享元模式,相同字符串指向同一片内存空间
有自动内存管理机制所托管(gc)
转义字符表
转义字符:
\a 响铃
\b 退格
\f 提供表格
\n 换行
\r 回车
\t 水平制表
\v 垂直制表
\\ 反斜杠
\" 双引号
\' 单引号
-
number(数字)
用于表示实数,lua中没有整数类型 lua中的任何数字都可以表示任何32位整数 使用双精度来表示一个 整数
可以使用科学计数法,如 0.4 4.37e6
2 表达式
-
算术操作符
二元:
+ 加法
- 减法
* 乘法
/ 除法
^ 指数,右结合
% 取模
一元:
- 负号
--关于%运算,规则如下:
a % b == a - floor(a/b) * b
--对于实数来说:
x%1 --> x的小数部分
x-x%1 --> x的整数部分
x-x%0.01 --> x精确到小数点后两位
-
关系操作符
>
<
<=
>=
==
~=(!=)
对于table、userdata 和函数,lua是做引用比较的。只有当他们引用同一个对象是,才认为相等
只能对于两个数字或两个字符串做大小型比较 对于 "0"与0,是不相同的
-
逻辑操作符
and ( && )
or ( || )
not ( ! )
- 与条件控制语句相同,所有的逻辑操作将false和nil视为假,而其他任何东西都为真。
- 对于 and 和 or 来说,都是用 短路求值,也就是说他们只会在需要时哦按段第二个操作数(如and前为假,lua变不判断之后)
常用操作:
x=x or v (若x为空赋默认值v)
max=(x>y)and x or y (返回最大值)
-
字符串连接
- ".." (字符串连接符) "hello ".."world" -->"hello world"
- 在操作中如果任意一个操作室是数字,lua会将其转换成一个字符串
- lua中字符串是不可变值,连接操作只会创建 一个新的字符串
- 右结合
-
table构造式
- 用于创建和初始化table
- 最简单的构造式为{}空构造式,创建一个空的table
初始化数组:
words={"a","b","c","d","e"} printa(a[3]) --> c
a={x=10,y=20} ==> a=[]; a.x=10; a.y=20;
构造式{x=0,y=0} ==> {["x"]=0,["y"]=0}
--在一个构造式中,除了逗号还可以用分号。一般用分号用于分隔构造式中不同的成分,例如
{x=10,y=45;"one","two","three"}
3.语句
-
赋值
普通赋值 与c++相似差异在于多重赋值
lua 中允许多重赋值,例如 a,b = 10 ,2x 赋值后,a为10,b为2x
在多重赋值中,lua先对等号右边所有元素求知,然后才执行赋值 如 x,y=y,x 交换x与y的值
lua总会将有编制的个数调整到与左边变量的个数一致。 规则是 若右边值数量少,则多语的变量会被赋值为nil ;若右边值多,则多于的值会被丢弃
注意: a,b,c=0 赋值结果为 0 nil nil多重赋值一般用于交换两个变量或接收函数的多个返回值(lua中函数允许返回多个返回值)
-
局部变量与块
通过local语句来创建局部变量
局部变量的作用域仅限于生命他们的那个块。一个块(block)是一个控制结构的执行体,或者是一个函数的执行体再或是一个程序块
x=10
local i =1 -- 程序块中的局部变量
while i<=x do
local x=i*2 --while循环中的局部变量
print(x)
end
if i>20 then
local x -- 新局变量x
x=20
print(x+2) --成功输出22,局部x
else
print(x) --成功输出10,全局
end
print(x) --输出10 全局
--如果是交互模式,可能不是此结果。
--交互模式中 每行输入的内容自身就形成了一个程序块
--避免此情况需要使用关键字 do-end
do
local a2=2*a
local d=(b^2-4*a*c)^{1/2}
x1=(-b+d)/a2
x2=(-b-d)/a2
end --a2和d的作用域至此结束
print(x1,x2)
在编程中尽可能的使用局部变量,这是一种良好的编程风格 可避免一些无用的名称引入全局环境,而且访问局部变量比访问全局变量更快 切在作用域结束时,便会消失 垃圾回收器便可释放该值
在lua中有一种习惯写法时
local foo=foo
这句代码创建了一个局部变量foo,并用全局变量foo的值初始化它,如果后续其它函数改变了全局foo的值,那么可以在这里先将他的值保存起来。这种方法还可以加速在当前作用域中对foo的访问
- 在需要时才声明局部变量,使得其在初始化时就拥有一个有意义的初值,并且缩短变量的作用域 这样有助于提高代码的可读性
-
控制结构
1. 语法
if then else
if a<0 then a=0 end
if a<b then return a else return b end
if line>MAXLINES then
showpage()
line=0
end
-- =============================
多个嵌套
if a==1 then
a=2
elseif a==2 then
a=3
else a=4
end
-- =============================
while
local i=1
while a[i] do
print(a[i])
i=i+1
end
repeat 一条repeat-until 语句重复执行其循环知道条件为真是结束
--打印一行不为空的内容
repeat
local num=io.read()
until num>0
2. for循环
2.1 数字型for
for var=exp1,exp2,exp3 do
<执行体>
end
--表示var从exp1变化到exp2,每次以exp3为步长(默认为1)
for i=1,10 do print(i) end --正序输出1-10(包含10)
for i=10,1,-1,do print(i) end --倒叙输出1-10(包含1)
不要在循环过程中修改控制变量(i)的值,跳出直接break
2.2 泛型for(foreach)
泛型for循环通过一个迭代器(iterator)函数来遍历所有值
for i ,v in ipairs(a) do print(v) end --打印数组a的所有值
Lua基础库 提供了ipairs,这是一个用于遍历数组的迭代器函数。在每次循环中,i会被赋予一个新的索引值,同事c被赋予一个对应于该索引的数组元素值
标准库提供的几种迭代器:
io.lines --迭代文件中的每行
pairs --迭代table元素
ipairs --迭代数组元素
string.gmatch --迭代字符串中带刺
同时,也可以自行编写迭代器
3. break/return
与其他语言基本相同,不同是break与return 只能是一个举哀的最后一条语句,或者是end else until前的一条语句
4.函数
-
基本属性
lua中函数可没有返回值(return 语句)此时返回的是nil
function testPrint()
print "test print"
end
local t=testPrint()
print(type(t)) -- nil
正常情况后面必须跟随圆括号,除以下特例:一个函数只有一种参数,且改参数是字面字符串或table构造式
print "hello world" ==>print("hello world")
dofile 'a.lua' <==> dofile('a.lua')
为面向对象式的调用提供了一种特殊语法: 冒号操作符 例如:
o.foo(o,x) ==> o:foo(x) --隐含将o作为函数的第一个参数
lua中形参数量与实参数量可以不同,会自动调整数量(规则和多重赋值相同)
function f(a,b) return a or b end
f(3)a=3,b=nil
f(3,4) a=3,b=4
f(3,4,5) a=3,b=4 5被舍弃
-
多重返回值
lua具有一项与其他语言不同的特征,允许函数返回多个结果 例如
s,e=string.find("hello lua user","lua") --如果找到返回匹配的起始字符和结尾字符的索引
编写具有多返回值的函数:
function testReturn(n)
if n>0 then
return 1,2
else
return 3
end
end -- n>0返回1,2 否则返回3
lua会调整一个函数的返回值类型数量以适应不同的调用情况,如下。
- 若将函数调用作为一条单独语句时,lua会丢弃所有的返回值
- 若将函数作为表达式的一部分调用时,lua只保留函数的第一个返回值
- 当函数是一系列表达式中的最后一个元素时,才能获得 他的所有返回值(多重赋值,函数调用时传入的参数列表。table的构造式、return语句)
function foo() return 1 ,2 end
x,y=foo() --x=1 y=2
x,y=foo(),3 --x=1 y=3
- 当一个函数调用作为另一个函数调用的最后一个实参时,第一个函数的所有返回值豆浆作为实参传入第二个参数
print(foo()) -- 1 2
print(foo(),3) --1 3
- 当foo2出现在一个表达式中时,lua会将其返回值数量调整为1 如
print(foo().."x") --1x
- table表达式可以完整的接收一个函数调用的所有结果(只有当一个函数调用作为最后一个元素时发生)
t={foo()} --t[1]=1 t[2]=2
t={foo(),3} --t[1]=1,t[2]=3
t={0,foo()} --t[1]=0 t[2]=1 t[3]=2
- 也可以将一个函数调用放入一对圆括号中,从而只返回一个结果如
print((foo())) --输出1
- 关于多重返回还有一个特殊函数 upack 他接受一个数组作为参数,并从1开始返回所有元素
print(unpack ({10,20,30}) ) --10 20 30
a,b=unpack(10,20,30) --a=10,b=20 30被丢弃
- unpack的一项重要用途体现在泛型调用(generic call) 机制中 ASNI C 中(c的标准)是无法编写泛型调用的代码 最多是声明一个可以接受长参数的函数 或者通过一个函数指针来调用不同的函数 并且无法在同一次函数调用中传入动态数量的参数,每次在调用时必须传入固定数量的参数 并且每个都要具有确定的类型 而lua中可以做到
unpack函数等价于 return list[1],list[2].....list[j] (j=#list)
-
变长参数
lua中的函数还可以接受不同数量的实参,例如
function Add(...)
local s=0
for i,v in ipairs{...} do
s=s+v
end
return s
end
print(add(3,4,10,25,12)) --> 54
参数中的3个点 (...) 表示该函数可接受不同数量的实参,一个函数要访问它的变长参数时,仍需用到三个点(...)此时这三个点是作为一个表达式来使用的 表达式{...}表示一个由所有变长参数构成的数组
表达式 "..." 的行为类似于一个具有多重返回值的函数,他返回的是当前函数的所有变长参数
具有变长参数的函数同样也可以拥有任意数量的固定参数,但固定参数必须放在变长参数之前。
在遍历一个变长参数时通常只需用到{...]如同访问一个table一样。然而如果变长参数中有我们刻意传入的nil值,则需 用到函数select,规则如下
如果 index 是个数字, 那么返回参数中第 index 个之后的部分; 负的数字会从后向前索引(-1 指最后一个参数)。 否则,index 必须是字符串 "#", 此时 select 返回参数的个数。如
for i=1,select('#',...) do
print(select('i',...))
end
-
具名实参
类似于c/c++中具有默认值的参数 int GetAge(int age=0) 但lua中不支持这种语法,我们可以通过一些操作达到相同的效果,如下示例
function Window(options)
--检查必要参数
if type(options.title)~="string" then error("no title") end
if type(options.width)~=“number" then error("no width") end
if type(options.height)~= "number" then error("no height") end
--其他参数都是可选的
_Window(options.title, -- _Window 真正的创建窗口函数
options.x or 0 --默认值
options.y or 0 --默认值
options.width,options.height
options.background or "white" --默认值
)
end