Lua的设计目的是依赖C的高效,并提供C所没有动态结构、高层抽象、内存回收、高阶函数等功能。
Lua本身很小,功能有限,大部分功能来源于其标准库。但本身提供的简单、高效、高扩展、可移植等特性,使得在特定场景中Lua有用武之地。
1 语言基础
1.1 词法约定
Lua语言是一种区分大小写的语言。变量命名和C一样,只能使用字母数字和下划线,数字不能位于开始。以下划线开始的变量默认为Lua自己使用的变量。
Lua中有20+个关键字,不能作为变量使用。诸如for while do end之类的。
Lua中的语句最好以;进行分隔。
1.2 注释
-- 这是行注释
--[[ 这是块
注释
]]
1.3 变量
变量不需要声明,无修饰的变量默认为全局变量,变量未初始化时值为nil。如果需要删除一个变量,将其置为nil即可。
系统初始化的全局变量 : _VERSION lua的版本号;_PROMPT 解释器的提示符样式
局部变量使用local来修饰,如local x = 0; 局部变量的作用域只限于声明它们的块。
lua的变量为词法变量,在局部作用域中,局部变量会覆盖全局作用域中的同名变量。
1.4 lua解析器程序
lua代码有几种方式执行:
lua prog.lua args
lua -e "lua code;"
如果传递了脚本参数,则会创建一个名为arg的表,脚本名位于索引0上,其后的参数索引从1开始,其前的参数则位于负数索引上。
2 类型与值
Lua是动态类型语言,类型不在变量名上绑定,而在值中存储。
Lua中有8种基础类型,nil bool number string function thread table userdata。所有的类型均为FirstClass的。type()可以返回一个变量的类型。
number 表示的为双精度浮点数,取值范围大约在1.79e-308,1.79e+308之间,计算时不会出现四舍五入的错误。
string 表示一个字符序列,采用8位编码,可以包含C的转义序列。其为不可修改的值对象。类似于python的doc string 可以将使用[[]]将一大块字符串包含在其中,而避免其中的特殊字符被转义。 #"hello" = 5
tonumber() 将字符串转换为数
tostring() 将数字转换为字符串
.. 字符串连接符
table 类似于python中的dict,表示关联数组。不仅可用整数来索引,也可用字符串等类型来索引。索引从1开始。
初始化 t = {1,2,3}; t = {x=1,y=2}; t ={['key']=value, };
取值 t[1] = 10; t['key'] = value; t.key = value;
userdata 用于存储C语言传递的数据结构。
3 表达式与语法
3.1 运算符
算法运算符 + - * / ^ %
关系运算符 < <= > >= ~= ==
逻辑运算符 and or not
赋值 x=1; x,y = y,x; t[1] = 0; 如果右侧值过少,则多余的变量赋值为nil
3.2 控制结构
Lua不支持switch,if..else结构会比较常见
if a < 0 then
t = -1;
elseif a = 0 then
t = 0
else
t = 1
end
while结构
while i < 10 do
print(i*2);
i = i + 1;
end
repeat结构
repeat
print(2*i);
i = i + 1;
until
i < 10;
for结构
for i = 1, 10, 1 do
print(2*i)
end
泛型for
for k,v in pairs(t) do
print(k,v)
end
支持break退出当前循环,return用于从当前函数返回。
5 函数
函数调用时需要将参数放在括号中,但如果只有一个参数且参数为字符串或table构造表达式时,可不使用括号。
function foo(str,key)
local start,end = string.find(string,key);
return start,end
end
传递参数时和多重赋值类似,如果形参少,则未能赋值的值为nil,多余的参数将丢弃。此外函数可以返回多重值。
unpack可以将数组中的元素解出来,作为多重值返回。
...可以作为变长参考来进行传递和使用。...的行为类似于一个具有多重返回值的函数,返回所有参数。
function add(...)
local s = 0;
for i,v in ipairs(...)
s = s + v
end
return s
end
利用table,可以使用键值参数,如
fun(arg)
return arg.old,arg.new;
end
fun({old="1",new="2"});
6 闭包
Lua支持词法作用域,同时函数又是第一类的。因此可以实现函数式编程的范式。高阶函数可以使用函数处理逻辑。
这样内部定义的函数可以访问外层函数的局部变量。并将外层的变量闭包在自己的对象中。从而可以脱离外层函数来单独调用。
Lua中的模块即使用在了table结合函数指针。
Lib = {}
Lib.fun = function() ... end
Lib.foo = function() ... end
对于递归调用,支持尾递归。但需要实现尾调用。即函数在调用完另一个函数之后,是否无其他事情需要做了。
只有形式上是return <fun>(args); 这样的调用才算是尾调用。只要args在调用前能求出值即可。
7 迭代器与泛型for
和ipairs等价的迭代器
function values(t)
local i = 0;
return function() i=i+1 return t[i] end
end
for的实现
for var_1,...,var_n in <explist> do
<block>
end
--等价于下面的代码
do
local _func, _state,_var = <explist>
while true do
local var1,...,var_n = _func(_state,_var)
_var = var1
if _var == nil then
break
end
<block>
end
end
8 文件加载与错误处理
8.1 加载文件
loadfile只加载文件,但不执行。dofile()则不仅仅会加载而且会执行。loadstring()则加载一段代码的字符串。需要注意的是对于字符串,其在运行时编译时,会在全局变量环境中查找变量。调用会返回一个函数闭包。
function dofile(file)
local f = assert(loadfile(file))
f()
end
只有执行了f(),文件中的定义才能生效,接下来才能使用文件中定义的函数等。
8.2 加载C库
Lua提供的底层关于动态链接的功能聚集在package.loadlib函数上。
local f = package.loadlib("/usr/lib/socket.so","luaopen_socket").
通常我们并不需要直接使用它,只需要require("socket")即可。
8.3 错误与异常处理
assert(expr,msg) 当expr表达式为true时返回expr,为假时触发错误,并打印msg信息。
error("msg") 抛出异常并输出msg,error可以返回任何类型的数据,如返回table
pcall(func,fun_args) 以try..catch类似的方式,捕获func调用中出现的error异常,其返回两个参数,第一个参数表示是否出现异常,第二个异常或func返回值。
if pcall (func) then
<block> --成功执行func
else then
<block> --异常处理
end
xpcall(func,errHandler) 当出现异常时,在异常现场调用errHandler
debug.traceback() 打印当前的调用栈
9 协同例程(coroutine)
coroutine类似于一个独立的执行线程逻辑,但与线程的区别是,多个coroutine并不是同时执行的,而互相协调,串行执行的。不执行的将让出CPU,以让其他的coroutine来执行。
coroutine.create(func) 创建一个协程,返回协程的id,此时co处于suspended状态
coroutine.resume(co,args) 执行创建的协程,
coroutine.status(co) 返回协程的状态
coroutine.yield(args) 当前运行的协程,通过调用yield挂起自己,使得之前的resume调用可以返回yield传递的参数。只有再次调用resume时,yield()才返回resume调用传 入的参数,使得协程可以向下运行,并交换数据。
运行中协程处于running状态,运行结束则处于dead状态。对于一个死亡状态的协程,调用resume将返回false和一个错误消息。如果协程A在运行中resume了协程B,则A处于normal状态。
示例:使用协程实现生产者和消费者
function producer(consumer)
while(true) do
x=io.read("*number");
send(x,consumer)
end
end
function filter(consumer)
while(true) do
x = receive();
x = x*x ;
send(x,consumer)
end
end
function consumer()
while(true) do
x = receive()
print("In consumer use ",x)
end
end
function send(x,consumer)
coroutine.resume(consumer,x)
end
function receive()
value = coroutine.yield();
return value;
end
coc = coroutine.create(consumer);
cof = coroutine.create(filter);
cop = coroutine.create(producer);
coroutine.resume(coc)
coroutine.resume(cof,coc);
coroutine.resume(cop,cof)
参考文献
Lua程序设计第二版
Lua Reference 翻译 http://www.photoneray.com/Lua-5.2-Reference-Manual-ZH_CN/