lua
Table of Contents
- 1 lua的特殊之处
- 2 术语
- 3 编译器lua
- 3.1 参数说明
- 4 运算符
- 5 变量
- 5.1 变量类型
- 5.2 特殊变量
- 6 语法
- 6.1 注释
- 6.2 赋值
- 6.3 控制结构语句
- 6.4 函数
- 7 高级主题
- 7.1 面向对象编程
- 7.1.1 简单的对象实现方式
- 7.1.2 简单继承
- 7.1.3 实现对外隐藏属性
- 7.1.4 使用元表实现继承
- 7.2 包
- 7.3 协作线程
- 7.1 面向对象编程
1 lua的特殊之处
- ..为字符串连接运算符(可用于字符串与数字之间的连接)
- ~=为不等号
- 默认数组下标从1开始,不推荐数组下标从0开始,否则很多标准库无法使用 ,这点很不一样
- io.write()不输出换行,而print自动输出换行,print()输出一个换行符
- lua中的table,类似于javascript中的Array(数组,同时也是Object,可以当Map用)
address={1,2,3} address.Street="Wyman Street" address.Contry="USA" print(address.Street,address["Contry"]
- if语句
- if 条件 then 语句 end
- if 条件 then 语句 else 语句 end
- if 条件 then 语句 elseif 条件 then 语句 else 语句 end
- util语句
- repeat 语句 until 条件
- while语句
- while 条件 do 语句 end
- for语句
- for from语句,to值,by值
-- Count from 1 to 4 by 1. for a=1,4 do io.write(a) end print() -- Count from 1 to 6 by 3. for a=1,6,3 do io.write(a) end
- for 变量 in 列表
for key,value in pairs({1,2,3,4}) do print(key, value) end -------- Output ------ 1 1 2 2 3 3 4 4
- for from语句,to值,by值
- 定义函数
- 可以返回多个返回值
function 函数名(parm1,parm2,parm3…) 语句 return value1,value2,value3… end
function myFirstLuaFunctionWithMultipleReturnValues(a,b,c) return a,b,c,"My first lua function with multiple return values", 1, true end a,b,c,d,e,f = myFirstLuaFunctionWithMultipleReturnValues(1,2,"three") print(a,b,c,d,e,f) -------- Output ------ 1 2 three My first lua function with multiple return values 1 true
- 与javascript类似,所有的变量默认为全局变量. lua中的local与javascript中的var作用一样,使得变量成为局部变量
-- All variables are global in scope by default. b="global" -- To make local variables you must put the keyword 'local' in front. function myfunc() local b=" local variable" a="global variable" print(a,b) end myfunc() print(a,b) -------- Output ------ global variable local variable global variable global
- 使用requre语句导入库
require("iuplua")
2 术语
- Chunk: Chunk 是一系列语句,Lua 执行的每一块语句,比如一个文件或者交互模式下的每一行都是一个 Chunk。
- Upvalue: 一个函数所使用的定义在它的函数体之外的局部变量(external local variable)称为这个函数的upvalue。
3 编译器lua
- 键入文件结束符可以退出交互模式,活着调用os.exit()函数也可以退出
- Lua 通常把每一个行当作一个 Chunk,但如果 Lua 一行不是一个完整的 Chunk时,他会等待继续输入直到得到一个完整的 Chunk.在 Lua 等待续行时,显示不同的提示符(一般是>>).
3.1 参数说明
- -l 让lua执行一系列文件
- -i 要求 Lua 运行指定Chunk 后进入交互模式.
- -e 直接将命令传入Lua
4 运算符
- 关系运算符:< > <= >= == ~=
- lua中不等号用~=表示
- 如果两个值类型不同,Lua认为两者不同;nil只跟自己相等
- lua通过引用比较tables、userdata、functions
- 为了避免不一致的结果,混合比较数字和字符串,lua会报错,例如2<'15'
- 逻辑运算符:and or not
- 逻辑运算符认为false和nil是假,其他为真,0和""也为真
- and 和 or的运算结果不是true和false,而是操作数的结果,这一点跟javascript类似
- and的优先级比or高
- C语言的a?b:c在lua中可以用(a and b) or c代替
- 连接运算符: ..
- 如果操作数为数字,则自动转换成字符串
- 表构造符:{}
- 初始化表a={v1,v2…}
- 初始化表a={k1=v1,k2=v2…}
- 通过a.x=nil来删除表中的项目
- 每次调用{},Lua都会创建一个新的table
-- 可以使用table构造一个list list=nil for line in io.lines() do list={next=list,value=line} end
- 在同一个构造中,可以混用列表和record风格进行初始化
polyline={k=v,k1,{a=b,c}}
- 上面的初始化方式有几个限制,你不能使用负索引,也不能使用类似+=*/这样的索引。 更一般的初始化方式是tb={["+"]="add",["-"="SUB",[ 1 ]="one",[ 2 ]="two"]}
- 构造函数中域分隔符都好(,)可以用分号(;)代替,通常我们使用分号来分割不同类型的表元素
{x=10,y=45;"one","two"}
5 变量
5.1 变量类型
- number
- string 总是以\0结尾,但可以包含任意字符,包括\0
- boolean
- function 对lua来说,函数是一种基本数据类型――代表一种可执行对象,可以有自己的状态。 可以用setfenv()来对函数设置函数环境
- table 任何类型的变量,除了nil,都可以做为表项的键。从简单的数值、字符串到复杂的函数、表等等都可以;同样,任何类型的变量,除了nil,都可以作为表项的值。给一个表项的值赋nil意味着从表中删除这一项
- userdata 用户(非脚本用户)定义的C数据结构。脚本用户只能使用它,不能定义。
- thread Lua协作线程,与一般操作系统的抢占式线程不一样。 yield的传入参数是另一个进程resume的返回值 resume的传入参数是另一个进程yield的返回值
- nil
5.2 特殊变量
- 全局变量arg存放Lua的命令行参数: 脚本名索引为0,脚本参数从1开始增加,脚本前面的参数从-1开始减少
prompt> lua -e "sin=math.sin" script a b arg 表如下: arg[-3] = "lua" arg[-2] = "-e" arg[-1] = "sin=math.sin" arg[0] = "script" arg[1] = "a" arg[2] = "b"
- LUAINIT: Lua 会查找环境变量 LUAINIT 的值,如果变量存在并且值为@filename,Lua 将加载指定文件。如果变量存在但不是以@开头,Lua假定 filename 为 Lua 代码文件并且运行他。
6 语法
6.1 注释
- 单行注释 --
- 多行注释 �C[ [line1\nline2… �C] ]
6.2 赋值
- Lua可以对多个变量同时赋值,这点类似Python
- 若变量个数与值个数不同则
- 变量个数>值得个数,变量个数补足nil
- 变量个数<值得个数,多余的值会被忽略
- 局部变量与代码块
- 与javascript类似,所有的变量默认为全局变量.
- lua中的local与javascript中的var作用一样,使得变量成为局部变量
-- All variables are global in scope by default. b="global" -- To make local variables you must put the keyword 'local' in front. function myfunc() local b=" local variable" a="global variable" print(a,b) end myfunc() print(a,b) -------- Output ------ global variable local variable global variable global
- 使用do…end定义一个block
6.3 控制结构语句
- if语句
- if 条件 then 语句 end
- if 条件 then 语句 else 语句 end
- if 条件 then 语句 elseif 条件 then 语句 else 语句 end
- util语句
- repeat 语句 until 条件
- while语句
- while 条件 do 语句 end
- for语句
- for var=from值,to值,by值
-- Count from 1 to 4 by 1. for a=1,4 do io.write(a) end print() -- Count from 1 to 6 by 3. for a=1,6,3 do io.write(a) end
需要注意的是:from、to、by值仅会在循环开始前执行一次;控制变量var是局部变量自动被声明,并且只在循环内有效. 因此如果需要保留控制变量的值,需要在循环中将其保存。
- for 变量 in 列表
for key,value in pairs({1,2,3,4}) do print(key, value) end -------- Output ------ 1 1 2 2 3 3 4 4
需要注意的是:控制变量var是局部变量自动被声明,并且只在循环内有效. 因此如果需要保留控制变量的值,需要在循环中将其保存。
- for var=from值,to值,by值
- Lua语法要求break和return职能出现在block的结尾一句,所以若是为了调试或其他需要在block中间使用return或break,需要显式使用do…end来实现
6.4 函数
- 定义函数
- 可以返回多个返回值
function 函数名(parm1,parm2,parm3…) 语句 return value1,value2,value3… end
function myFirstLuaFunctionWithMultipleReturnValues(a,b,c) return a,b,c,"My first lua function with multiple return values", 1, true end a,b,c,d,e,f = myFirstLuaFunctionWithMultipleReturnValues(1,2,"three") print(a,b,c,d,e,f) -------- Output ------ 1 2 three My first lua function with multiple return values 1 true
- Lua的函数可以接受可变数据的参数,类似C语言在函数参数列表中使用三点…表示函数有可变的参数。Lua将函数放在一个叫arg特殊变量内,这个变量是一个表表。除了参数以外,arg表还有一个域n表示参数的个数。
printResult="" function print(...) for i,v in ipairs(arg) do printResult=printResult..tostring(v).."\t" end printResult=printResult.."\n" end
- 命名参数:Lua可以通过将所有的参数放在一个表内,把表作为函数的唯一参数来实现类似命名参数的功能。
function rename(args) return os.rename(args.old,args.new) end rename{old="old.lua",new="new.lua"} --当函数参数仅一个,且为表或字符串时,可以省略()
- 定义匿名函数 function (x) 语句 end
- 类似python,Lua将函数看成一个普通那个变量,也支持闭包
function newCounter() local i = 0 return function() -- anonymous function i = i + 1 return i end end c1 = newCounter() print(c1()) --> 1 print(c1()) --> 2
- 可以返回多个返回值
- 调用函数
- 当函数只有一个参数并且这个参数是字符串或者表构造时,()是可选的:
print "abc" -- print("abc")
- Lua也提供面向对象方式调用函数的语法: obj:function(). 这里object与function之间用:分隔开,而模块与方法之间用.分隔开!!
o:foo(x) --> o.foo(o,x)
- Lua函数的参数与javascript类似,,多余的部分被省略,缺少部分用nil补足
- 如果没有返回,则值为nil
- 函数调用作为函数参数或是表的构造函数中,或是在return语句之后时,与多值赋值时相同
print(f()) -->print(a,b,c) a={f()} -->a={a,b,c} return f() -->return a,b,c
- 可以使用圆括号强制使调用返回一个值 print((f())) �C>print(a)
- 可以用unpack函数来讲一个数组转换成多个元素
- 当函数只有一个参数并且这个参数是字符串或者表构造时,()是可选的:
7 高级主题
7.1 面向对象编程
与javascript类似,lua是基于对象的,其可以用setmetatable()设置一个元表(metatable),这个元表类似于javascript的prototype
7.1.1 简单的对象实现方式
- 对象工厂模式 如前面代码的create函数用表来表示对象,把对象的数据和方法都放在一张表内,虽然没有隐藏私有成员,但对于简单脚本来说完全可以接受。
- 成员方法的定义 function obj:method(a1, a2, …) … end 等价于 function obj.method(self, a1, a2, …) … end 等价于 obj.method = function (self, a1, a2, …) … end
- 成员方法的调用 obj:method(a1, a2, …) 等价于 obj.method(obj, a1, a2, …)
function create(name, id) local obj = { name = name, id = id } function obj:SetName(name) self.name = name end function obj:GetName() return self.name end function obj:SetId(id) self.id = id end function obj:GetId() return self.id end return obj end
7.1.2 简单继承
- 先调用基类的工厂函数产生一个基类的对象
- 在这个基类的对象的基础上增加子类的属性和方法
- 返回这个对象
function createBase() local obj={attr="base"} function obj:setName(name) self.name=name end end function createSub() local obj=createBase() obj.attr="Sub" function obj:setId(id) self.id=id end end
7.1.3 实现对外隐藏属性
* 实现方式 把需要隐藏的成员放在一张表里,把该表作为成员函数的upvalue。
- 局限性 基于对象的实现不涉及继承及多态。但另一方面,脚本编程是否需要继承和多态要视情况而定。
7.1.4 使用元表实现继承
- 实现方法 将基类设置为子类的元表、并将子类的元表(即基类)设置为自身(也是基类,也就是代码self._index=self)
Robot = { name = "Sam", id = 001 } function Robot:New(extension) local t = setmetatable(extension or { }, self) self.__index = self return t end FootballRobot = Robot:New( {position = "right back"}) fr=FootballRobot:New()
- prototype模式 一个对象既是一个普通的对象,同时也可以作为创建其他对象的原型的对象(即类对象,class object);动态的改变原型对象的属性就可以动态的影响所有基于此原型的对象;另外,基于一个原型被创建出来的对象可以重载任何属于这个原型对象的方法、属性而不影响原型对象;同时,基于原型被创建出来的对象还可以作为原型来创建其他对象。
7.2 包
- 定义 包是一种组织代码的方式。
- 实现方式 一般在一个Lua文件内以module函数开始定义一个包。module同时定义了一个新的包的函数环境,以使在此包中定义的全局变量都在这个环境中,而非使用包的函数的环境中。理解这一点非常关键。以前面的代码为例, “module(…, package.seeall)”的意思是定义一个包,包的名字与定义包的文件的名字相同(除去文件名后缀,在前面的代码中,就是“mypack”),并且在包的函数环境里可以访问使用包的函数环境(比如,包的实现使用了print,这个变量没有在包里定义,而是定义在使用包的外部环境中)。
- 2使用方式 一般用require函数来导入一个包,要导入的包必须被置于包路径(package path)上。包路径可以通过package.path或者环境变量来设定。一般来说,当前工作路径总是在包路径中。
7.3 协作线程
- 创建协作线程 通过coroutine.create可以创建一个协作线程,该函数接收一个函数类型的参数作为线程的执行体,返回一个线程对象。
- 启动线程 通过coroutine.resume可以启动一个线程或者继续一个挂起的线程。该函数接收一个线程对象以及其他需要传递给该线程的参数。线程可以通过线程函数的参数或者coroutine.yield调用的返回值来获取这些参数。当线程初次执行时,resume传递的参数通过线程函数的参数传递给线程,线程从线程函数开始执行;当线程由挂起转为执行时,resume传递的参数以yield调用返回值的形式传递给线程,线程从yield调用后继续执行。
- 线程放弃调度 线程调用coroutine.yield暂停自己的执行,并把执行权返回给启动/继续它的线程;线程还可利用yield返回一些值给后者,这些值以resume调用的返回值的形式返回
- 另一种迭代方式 协作线程可以作为for循环迭代器的另一种实现方式。虽然对于简单的数组遍历来说,没有必要这么做,但是考虑一下,如果需要遍历的数据集合是一个复杂数据结构,比如一棵树,那么协作线程在简化实现上就大有用武之地了。
function enum(array) return coroutine.wrap(function() local len = #array for i = 1, len do coroutine.yield(array[i]) end end) end function foreach(array, action) for element in enum(array) do action(element) end end foreach({1, 2, 3}, print)