自从接触了 JerryScript(见开源库jerryscript使用备忘 ),本以为再也不会用到 Lua 了,关于 Lua 的记忆已经逐渐在我的脑海中删除,但最近维护的几个老项目又都使用了 Lua,真是命运的捉弄啊。
来吧,刷新记忆!
基础
Lua区分大小写
注释格式
局部变量使用local声明, 其他的全部为全局变量,变量的默认值为nil
只有false和nil为假,其他的全部为真
Lua中字符串都是不可变的常量
使用..可以连接字符串
Lua没有整形,都是实数
可以在字符串前放置操作符#来获取字符串长度,某些情况下对table也适用
关系操作符: < > == >= <=,不等于使用~=,而不是!=
and, or, not 逻辑操作符返回的不一定是true和false,这点与C++不同,它返回的是对应表达式的运行结果
支持多重赋值
1 2 3 4 5 6 7 if exp then elseif exp then else end
1 2 3 while <exp is true > do end
1 2 3 repeat until <exp is true >
1 2 3 for var=exp1, exp2, exp3 do end
1 2 3 for i,v in ipairs (a) do end
1 2 3 for i in pairs (a) do end
函数 在Lua中函数也是值。
多重返回值 <原文出自: jiangxueqiao.com,请尊重原创> Lua中函数可以返回多个值,但有下面的特殊情况:
函数作为单独的语句时,会丢弃所有返回值
函数作为表达式的一部分时,只保留第一个返回值
在多重赋值中,函数作为最后一个表达式时,会保留尽可能多的返回值
将函数调用放在一对括号中,会迫使其只返回一个值
变长参数 1 2 3 4 5 6 7 8 9 function add (...) local ret = 0 for i,v in ipairs ({...}) do ret = ret + v end return ret end print (add(1 ,2 ,9 ))
1 2 3 function foo (...) local a,b,c = ... end
1 2 3 4 5 function FooCallLog (...) print ("Call foo:" , ...) return foo(...) end
1 2 3 4 5 6 7 8 9 10 11 12 function add (...) local ret = 0 for i = 1 , select ("#" , ...) do local arg = select (i, ...) ret = ret + arg end return ret end print (add(1 ,3 ,5 ))
闭合(Closure)函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function newCounter () local i = 0 return function () i = i + 1 return i end end c1 = newCounter() print (c1()) print (c1()) c2 = newCounter() print (c2())
非全局函数 将一个函数存储到一个局部变量中,即得到了一个“局部函数”。<原文出自: jiangxueqiao.com,请尊重原创>
1 2 3 4 5 6 7 8 local lib={foo = function (x,y) return x + y end ,goo = function (x,y) return x - y end }
1 2 3 local lib={}function lib.foo (x,y) return x + y end function lib.goo (x,y) return x - y end
对与递归程序等,为了防止局部函数为尚定义完毕,就要被编译,可以使用前置定义的方式:
1 2 3 4 5 6 7 8 local factfact = function (n) if n == 0 then return 1 else return fact(n-1 ) end end
编译、执行与错误 loadstring 1 2 3 a = 20 f = loadstring ("local a = 10; print(a)" ) f()
pcall 1 2 3 4 5 6 7 8 9 10 11 12 13 function foo (n) if n > 0 then print (n) else error ("n need > 0" ) end end if pcall (foo, -1 ) then print ("ok" ) else print ("error" ) end
pcall也可以执行匿名函数。
协同程序 Lua将所有和协同程序相关的函数存储在名为“coroutine”的table中。一个协同程序可以有4种状态:suspended, running, dead, normal。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 co = coroutine .create (function () coroutine .yield () for i = 0 , 2 do if i == 2 then error ("oh No." ) end print (i) end end ) print (coroutine .resume (co)) print (coroutine .resume (co)) print (coroutine .resume (co))
元表与元方法 Lua中每个值都有一个元表,table和userdata可以有各自独立的元表,而其他类型的值则共享其类型所属的单一元表。 Lua在创建新的table时不会创建元表。除了标准的字符串程序库外,其他的类型在默认情况下都没有元表。
1 2 3 4 5 t={} print (getmetatable (t)) print (getmetatable ("hi" )) print (getmetatable (10 ))
可以在元表中定义的原方法(或字段)如下:
__add 加
__sub 减
__mul 乘
__div 除
__unm 相反数
__mod 取模
__pow 乘幂
__eq 等于
__lt 小于
__le 大于
__concat 连接
__tostring 字符串转化
__metatable 保护元表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 local mt = {}mt.__tostring = function (t) local l = {} for e in pairs (t) do l[#l + 1 ] = e end return "{" .. table .concat (l, ", " ) .. "}" end ages = {Jeff = 18 , Jim = 19 , Lucy = 20 } print (ages) setmetatable (ages, mt)print (ages) mt.__metatable = "not your business" print (getmetatable (ages))
__index Table访问
__newindex Table更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 local index = {}local mt={__index = function (t, k) print (string .format ("*access to element %s, value %s" , k, t[index][k])) return t[index][k] end ,__newindex = function (t, k, v) print ("*update of element " .. tostring (k) .. "to " .. tostring (v)) t[index][k] = v end } function track (t) local proxy = {} proxy[index] = t setmetatable (proxy, mt) return proxy end ages = {Jeff = 18 , Jim = 19 , Lucy = 20 } ages = track(ages) print (ages.Jeff)address = {Jeff = "WuHan" , Jim = "Beijin" , Lucy = "Taiyuan" } address = track(address); print (address.Jim)
模块与包 模块搜索 对于require(“foo”)语句,Lua会先在预定路径中搜索foo.lua,如果没有搜索到才会搜索foo.DLL。
require在搜索Lua文件时,会在变量package.path存放的路径中进行搜索,Lua启动后便以环境变量LUA_PATH的值来初始化该变量; 而搜索DLL文件的路径则存放在package.cpath中,同理,Lua启动后会用LUA_CPATH环境变量的值来初始化该变量。
require会用模块名来替换每个“?”,依次匹配搜索,直到搜索成功。
基本模块编写 编译一个模块最简单的方法就是:创建一个table,并将所有需要导出的函数放入其中,最后返回这个table。
1 2 3 4 5 6 7 8 9 10 basicmod = {} basicmod.name = "basic mod" function basicmod.func1 () print ("func1" ) end return basicmod
调用该模块:
1 2 3 require ("basic_mod" )print (basicmod.name) print (basicmod.func1())
但是上面的写法会导致模块中的每个函数或成员前面都带有该模块的名称,更改模块名称会牵一发而动全身,针对这个问题,可以进行如下改进:
1 2 3 4 5 6 7 8 9 10 11 local M = {} basicmod = M M.name = "basic mod" function M.func1 () print ("func1" ) end return basicmod
实际上,可以完全避免写模块名,因为require会将模块名作为参数传递给模块:
1 2 3 4 5 6 7 8 9 10 11 local modname = ...local M = {}_G [modname] = Mpackage .loaded [modname] = MM.name = "basic mod" function M.func1 () print ("func1" ) end
1 2 3 require ("basic_mod" ) print (basic_mod.name)print (basic_mod.func1())
在Lua5.1中,提供了一个新的函数module,简化模块的编写:
1 2 3 4 5 6 7 module (..., package .seeall )name = "basic mod" function func1 () print ("func1" ) end
面向对象 使用this(或self)参数是所有面向对象语言的核心。大多数面向对象语言都能对程序员隐藏this参数,从而使得程序员不必显示的声明这个参数。
Lua只需要使用冒号就能隐藏该参数,冒号的作用就是在一个方法的定义中添加一个额外的隐藏参数,以及在一个方法的调用中添加一个额外的实参,冒号只是一种语法便利,并没有引入任何新的东西。
1 2 3 4 5 6 7 8 9 10 Account = {balance = 0 } function Account.cost (v) Account.balance = Account.balance - v end Account.cost(100 ) a = Account; Account = nil a.cost(100 )
改进如下:
1 2 3 4 5 6 7 8 9 Account = {balance = 0 } function Account.cost (self, v) self .balance = self .balance - v end Account.cost(Account, 100 ) a = Account; Account = nil a.cost(a, 100 )
使用冒号改进:
1 2 3 4 5 6 7 8 9 Account = {balance = 0 } function Account:cost (v) self .balance = self .balance - v end Account:cost(100 ) a = Account; Account = nil a:cost(100 )