Lua初学笔记
Posted corfox_liu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lua初学笔记相关的知识,希望对你有一定的参考价值。
Lua初学笔记
lua动态类型语言,支持过程式编程、面向对象编程、函数式编程与数据驱动编程。
lua -la -lb:命令首先在一个Chunk内先运行脚本文件a,然后运行脚本文件b。(-l参数会调用require,将会在指定的目录下搜索文件)。
lua -i -la -lb:参数-i要求lua运行指定Chunk后进入交互模式。
运行Chunk的另外一个方式是在交互模式下使用dofile
全局变量:全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是nil。
关键字:and break do else elseif end false for function if in local nil not or repeat return then true until while
单行注释:–,多行注释–[[ ]]–
命令行方式:lua [options] [script[args]]
-l: 加载一个文件 -e:直接将命令传入lua -i:进入交互模式 _PROMPT:内置变量作为交互模式的提示符
全局变量arg存放Lua的命令行参数。
Lua是动态类型语言,变量不需要类型定义。Lua中基本类型有8个,分别为nil、boolean、number、string、usrdata、function、thread、table。函数type可以测试给定变量或值的类型。一个全局变量没有赋值以前默认值是nil,给全局变量赋值nil可以删除该变量;Lua中所有值都可以作为条件,在控制结构中,除了false、nil为假,其他值都为真;对于字符串,可以使用[[]]表示多行的字符串。
Lua中的表达式包括数字常量、字符串常量、变量、一元和二元元算符、函数调用,以及非传统的函数定义与表结构。算术运算符+、-、*、/、^
(加减乘除幂),-
(负值);关系运算符< > <= >= == ~=
,Lua通过引用比较tables、userdata、functions,即当两者表示同一个对象时相等;逻辑运算符and or not
,需要注意的是逻辑运算符只认为false与nil为假,其它值都为真(0也是真),另外逻辑比较a and b --如果a为false,则返回a,否则返回b;a or b --如果a为true,则返回a,否则返回b
;连接运算符..
,字符串连接(如果操作数是数字,Lua将数字转换为字符串)。
这些运算符的优先级如下表(优先级由高到底)
优先级 | 运算符 |
---|---|
1 | ^ |
2 | not -(一元) |
3 |
|
4 |
|
5 | .. |
6 | < > <= >= ~= == |
7 | and |
8 | or |
注:其中^
与..
是右连接的,其余操作符都是左连接的。
表的操作:表的构造函数{},构造函数可以使用任何表达式进行初始化,如:t = { math.sin(1), "string", 32}
,表中的元素可以是任意数据类型。下标是从1开始,超过边界的值是nil。同时也可以将一个表作为record使用,类似于Python中的字典,如t = {"one"=1, "two"=2} 等价于 t={}; t.one=1; t.two=2
。不管以任何方式创建table,都可以向表中添加或删除任何类型的域,构造函数仅仅影响表的初始化。更加一般的初始化方式是使用[expression]
,如t = { ["one"] = 1, ["two"] = 2 }
t[“one”]=1 t[“two”]=2,而t = {0, 1} 等价于 t = { [1]=0, [2]=1}; t = { "one"=1, "two"=2}等价于t={["one"]=1, ["two"]=2}
。在构造函数中,可以在构造函数的末尾加上,
方便以后的扩展,另外域分隔符,
可以使用;
代替。
Lua中可以对多个变量同时赋值,变量列表与值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量,如:x, y = 10, "test" 则x=10, y="test"
。遇到赋值语句Lua会先计算右边所有的值,然后再将其赋给左边,因此可以如下操作交换值x, y = y, x
。
可以使用local创建局部变量,与全局变量不同,局部变量只在被声明的那个代码块内有效。代码块指一个控制结构内,一个函数体内,或者一个Chunk(变量被声明的那个文件或者文本串)。应该尽可能的使用局部变量,有两个好处:避免命名冲突;访问局部变量的速度要比全局变量更快。
控制语句:if-else, while, repeat-until。
注意:一个声明在循环体中的局部变量的作用域包括了测试条件,如下示例:
local sqr = x / 2
repeate
sqr = sqr * (2 + sqr)
val = math.abs(sqr^2)
untile val < x / 1000 --the val can be accessed in here.
for循环有两种类型,数值for循环:for var=exp1, exp2, exp3 do loop-part end
,其中exp1是初始值,exp2是终止值,exp3是步长(可选,默认是1)。需要注意:三个表达式只会计算一次,并且在循环开始前;控制变量是局部变量,并且只在循环内有效;循环过程中不要改变控制变量的值,那样做的结果是未知的。泛型for循环for var in intorfunction do loop-par end
。需要注意,控制变量是局部变量,不要修改控制变量的值。
break语句退出当前循环(for repeat while),在循环外不可使用。return语句从函数返回结果,当一个函数自然结束时会有一个默认的return。Lua语法要求break,return只能出现在block的结尾(chunk的最后一句,或者在end之前,或else之前,或until之前),若需要在其它地方使用break或return,使用do break end
。
函数:function func_name (argument list) statements; end
。注意Lua函数的实参与形参的匹配与赋值语句类似,不足nil补齐,多余略去。另外,函数在有多个参数的表达式中不是最后一个参数时仅返回一个值,其它情况都按照多赋值的情况返回值。需要注意的是return f()这种形式的返回,将返回f()的所有值。函数的可变参数使用...
。命名参数可以通过表table来实现。
Lua为面向对象式的调用也提供了一种特殊的语法—冒号操作符。表达式o.f(o, x)
的另一种写法是o:f(x)
,冒号操作符使调用o.f时将o隐含的作为函数的第一个参数。
当只有一个函数调用是一系列表达式中的最后一个值时(或仅有一个元素),才能获得它的所有返回值。函数调用作为函数的参数时也符合上述说明。当函数调用在强加一个圆括号后(test())
只会保留第一个值。
当函数的参数是一个table构造式时,函数调用圆括号是可有可无的。
Lua中的函数时带有词法定界(lexcial scoping)的第一类值(first-class values)。第一类值是指,函数作为基本类型,是和其它基本类型一样,函数可以存放在变量中,也可以存放在表中,可以作为函数的参数,也可以作为函数的返回值。词法定界是指:被嵌套的函数可以访问它外部函数中的变量。另外函数也可以是匿名的。
创建非全局函数的3种语法形式:
Fuc = {}
Fuc.add = function (x, y) return x + y end
Fuc.min = function (x, y) if x < y then return x else return y end end
----------
Fuc = {
add = function (x, y) return x + y end
min = function (x, y) if x < y then return x else return y end end
}
----------
Fuc = {}
function Fuc.add(x, y) return x + y end
function Fuc.min(x, y) if x < y then return x else return y end end
闭包:一个函数加上它可以正确访问的upvalues(upvalues既不是全局变量,也不是局部变量,称为external local variable)。技术上来讲,闭包指值而不是函数,函数仅仅是闭包的一个原型声明。闭包是一个内部函数,它可以访问一个或多个外部函数的外部局部变量。每次闭包的成功调用后,这些外部局部变量都保存它们的值(状态)。
非全局函数,表语函数结合在一起。
正确的尾调用:当一个函数最后一个动作是调用另外一个函数时,称为尾调用。尾调用之后不需要在栈中保留关于调用者的任何信息,当被调用函数返回时,执行控制权可以直接返回到调用f的那个点上。
迭代器:迭代器是一种支持指针类型的结构,它可以遍历集合中的每一个元素。迭代器需要保留上一次成功调用的状态与下一次成功调用的状态。可以利用闭包实现迭代器,一般需要两个函数,一个是闭包自己,另一个是闭包访问的外部局部变量的工厂函数(创建闭包的函数)。
require函数:require和dofile完成同样的功能,但是有两点不同,require会搜索目录加载文件;require会判断是否文件已经加载,以避免加载同一文件。
pcall函数:pcall在保护模式下调用它的第一个参数并运行,因此可以捕获所有的异常和错误。如果没有异常或错误,pcall返回true和调用返回的任何值,否则返回nil和错误信息。
协同程序(coroutine):协同程序与多线程的线程比较类似:有自己的堆栈,自己的局部变量,有自己的指令指针,但是和其它协同程序共享全局变量等很多信息。线程和协同程序的主要不同在于:在多处理器情况下,从概念上讲多线程程序同时运行多个线程,而协同程序是通过协作来完成的,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。
lua通过table提供了所有的协同函数,create函数创建一个新的协同程序,create只有一个参数;协同程序将要运行的代码封装成函数,返回值为thread类型的值表示创建了一个新的协同程序。协同有三个状态:挂起态(suspended)、运行态(funning)、死亡(dead)。注意的是:lua提供的协同是一种不对称的协同,就是说挂起一个正在执行的协同的函数与使一个被挂起的协同再次执行的函数是不同的。
coroutine.create
coroutine.status
coroutine.resume
coroutine.yield
coroutine.warp
table是Lua中唯一的数据结构,其它常见的数据结构都可以通过table来实现。
Metatable与Metamethod:metamethod方法:__add, __mul, __sub, __div, __unm, __pow, __concat, __eq, __lt, __le, __tostring, __metatable, __index(对表进行访问), __newindex(对表进行更新),__mode。setmetatable方法,getmetatable方法。
Lua中的每个值都有一个元表。table和userdata可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表。Lua在创建新的table时不会创建元表,在Lua代码中,只能设置table的元表。若要设置其它类型的值的元表,则必须通过C代码来完成。
元表中,每种算符操作符对应的字段名如下:
操作符 | 字段名 |
---|---|
+ | __add |
- | __sub |
* | __mul |
/ | __div |
% | __mod |
^ | __pow |
- | __unm |
// | __idiv |
& | __band |
| | __bor |
~ | __bxor |
~ | __bnot |
<< | __shl |
> |
__shr |
.. | __concat |
# | __len |
== | __eq |
< | __lt |
> | __le |
table[key] | __index |
table[key]=value | __newindex |
func(args) | __call |
print函数会搜索元表中的字段__tostring
,函数setmetatable与函数getmetatable会用到元表中的字段__metatabel
,用于保护元表。
环境 Lua将所有的全局变量保存在一个常规的table中,这个table称为”环境”,而这个表有保存在全局变量_G
中。可通过函数setfenv改变一个函数的环境。
Pakages通过表来描述Package。
loadfile加载Lua编写的程序库,package.loadlib加载C编写的程序库。
在搜索一个文件时,require所使用的路径与传统的路径有所不同。大部分程序所使用的路径就是一连串的目录,指定了某个文件的具体位置。然而,ANSI C却没有任何关于目录的概念。所以,require采用的路径是一连串的模式,其中每项就是将模块名转换为文件名的方式。require用于搜索Lua文件的路径存放在package.path变量中,搜索C程序库的路径存放在变量package.cpath。当Lua启动后,便以环境变量LUA_PATH的值来初始化这个值。
面向对象程序设计:使用表,使用冒号:
语法将self隐藏起来。冒号的作用是在一个方法定义中添加一个额外的隐藏参数,以及在一个方法调用中添加一个额外的实参。冒号只是一种语法遍历,并没有引入任何新的东西。
Weak表:Lua自动进行内存的管理。虽然有自动垃圾处理机制,但有时候需要自己动手处理,将不要的对象赋值nil。weak表是一种用来告诉Lua一个引用不应该防止对象被回收的机制。表的weak性由他的metatable的__mode域来指定的。在这个域存在的时候,必须是个字符串;如果这个字符串包含小写字母‘k’,这个table中的keys就是weak的;如果这个字符串包含小写字母‘v’,这个table中的values就是weak的。
所谓弱引用(weak reference)就是一种会被垃圾收集器忽视的对象引用。如果一个对象的所有引用都是弱引用,那么Lua就可以回收这个对象了,并且还可以以某种形式来删除这些弱引用本身。
table中有key与value,通常垃圾收集器不会回收一个可访问table中作为key或value的对象。也就是说,这些key与value是强引用,它们会阻止对所应用对象的回收。在一个弱引用table中,key与value是可以回收的。有三种弱引用table:具有弱引用key的table,具有弱引用value的table,同时具有两种引用的table。不论哪种类型的弱引用table,只要有一个key或value被回收了,那么它们所在的整个条目都会从table中删除。
math库
ceil, log, asin, deg(弧度转换为角度), cos, sin, floor, min, modf, max, exp, ult, tointeger, acos, pi, maxinteger, sqrt, frexp, huge, mininteger, randomseed, atan, log10, abs, ldexp, tanh, tan, pow, rad(弧度转换为角度), type, cosh, atan2, fmod, random, sinh
table库
move, sort, remove, concat, unpack, pack, insert
string库
sub, format, find(模式匹配函数), len, unpack, gsub(模式匹配函数,global subtraction), char, dump, packsize, pack, upper, byte, lower, gmatch(模式匹配函数,返回查找到字符串的迭代器), reverse, rep, match(模式匹配函数)
字符串可以从负索引开始。
Lua模式中支持的所有字符分类
字符 | 含义 |
---|---|
. | 任意字符 |
%a | 字母 |
%c | 控制字符 |
%d | 数字 |
%l | 小写字母 |
%p | 标点符号 |
%s | 空白符 |
%u | 大写字母 |
%w | 字母和数字 |
%x | 十六进制数字 |
%z | 代表0的字符 |
字符%
作为这些模式字符的转义字符。因此%.
表示匹配一个点,%%
表示匹配字符%
本身。
在一对方括号内将不同的字符分类或单个字符组合起来,即可创建出属于用户自己的字符分类,这种新的字符分类叫做“字符集(char-set)”,例如字符集[%w_]
表示同时匹配字符、数字和下划线。在字符集中包含一段字符范围的做法是写出字符范围的第一个字符和最后一个字符,并用横线连接它们,例如[0-9]
表示匹配数字。在一个字符集前加一个^
,就可以得到这个字符集的补集。
此外,还可以通过修饰符来描述模式中的重复部分和可选部分。Lua的模式提供4种修饰符,如下:
修饰符 | 含义 |
---|---|
+ | 重复1次或多次 |
* | 重复0次或多次 |
- | 重复0次或多次 |
? | 出现0次或1次 |
注:某些情况下,-
与*
模式匹配效果并不同,-
是以尽可能少的扩展来找到匹配模式,而*
是以尽可能多的扩展来找到匹配模式。
URL编码
URL是HTTP使用的一种编码方式,用于在一个URL中传送各种参数。这种编码方式会讲特殊字符(如:=, &, +
等)编码为%<xx>
的形式,其中<xx>
是字符的十六进制表示。此外,它还会将空格转换为+
,它会将没对参数名及其值用=
连接起来,并将每对结果name=value用&
连接起来,如:
name="al";query="a+b = c";q="yes or no"
会被编码为
"name=al&query=a%2Bb+%3D+c&q=yes+or+no"
I/O库
output, stdout, close, lines, input, read, stderr, stdin, open, write, tmpfile, type, flush, popen
wirte与print有几点不同。一是write在输出时不会添加像制表符或回车这样的额外字符。二是write使用当前输出文件,而print总是使用标准输出。三是print会自动调用其参数的tostring()方法,因此它还能显示table、函数和nil。
read函数从当前输入文件读取串,由它的参数控制读取的内容:
参数 | 含义 |
---|---|
“*all” | 读取整个文件 |
“*line” | 读取下一行 |
“*number” | 从串中转换出一个数值 |
num | 读取num个字符到串 |
open函数打开文件,模式字符’w’, ‘r’, ‘a’, ‘b’。
os库
rename, tmpname, getenv, remove, clock, time, difftime, execute, date, exit, setlocale
debug库
getinfo, traceback, upvaluejoin, gethook, setupvalue, setmetatable, getupvalue, getuservalue, debug, setlocal, getregistry, setuservalue, sethook, getlocal, upvalueid, getmetatable
debug库的一个重要思想是栈级别(stack level)。一个栈级别就是一个指向在当前时刻正在活动的函数的数字,即这个函数正在被调用,但是还没有返回。调用调试库的函数是层1,调用这个函数的函数是层2,以此类推。
getinfo返回一个table,其中包含了与目标函数(getinfo的第一个参数)的相关信息,这些信息有:
currentline, lastlinedefined, func(函数本身), istailcall source(函数定义的位置), namewhat, what(函数的类型,“Lua”普通函数,“C”C函数,“main”Lua程序块的主程序部分), nparams, linedefined(该函数定义在源码中的第一行行号), nups(该函数的upvalue数量), isvararg, short_src(source的短版本)
getinfo的第二个可选参数(该参数是一个字符串,其中每个字符代表一组字段),用于指定希望获取哪些信息。
字母 | 含义 |
---|---|
‘n’ | 选择name和namewhat |
‘f’ | 选择func |
‘S’ | 选择source、short_src、what、linedefined和lastlinedefined |
‘l’ | 选择currentline |
‘L’ | 选择activelines |
‘u’ | 选择nups |
参考
1. 《Lua程序设计》,Roberto Ierusalimschy,译:周惟迪 。
以上是关于Lua初学笔记的主要内容,如果未能解决你的问题,请参考以下文章