若使用命令行参数 -i 来启动Lua解释器,那么解释器就会在运行完指定程序块后进入交互模式。例如在命令行中输入:
% lua -i prog
这样会先运行文件prog中的程序块,然后再进入交互模式。参数 -i 对于调试和手工测试尤为有用。
Lua中的标识符可以是由任意字母、数字和下划线构成的字符串,但不能以数字开头。
应该避免使用以一个下划线开头并跟着一个或多个大写字母(例如“_VERSION”)的标识符,Lua将这类标识符保留用作特殊用途。通常保留标识符“_”(一个下划线)作为“哑变量(Dummy Variable)”使用。
可以在任何地方以两个连字符(--)开始一个“行注释”,该注释一直延伸到一行的结尾。Lua也提供了“块注释”,以“--[[”开始,直至“]]”。
Lua中有8种基础类型:nil(空)、boolean(布尔)、number(数字)、string(字符串)、userdata(自定义类型)、function(函数)、thread(线程)和table(表)。
Lua中的字符串是不可变的值。不能像在C语言中那样直接修改字符串的某个字符,而是应该根据修改要求来创建一个新的字符串。
可以用一对匹配的双方括号来界定一个字母字符串,以这种形式书写的字符串可以延伸多行,Lua不会解释其中的转义序列。此外,如果字符串的第一个字符是一个换行字符串,那么Lua会忽略它。这种书写对于书写那种含有程序代码的字符串尤为有用。如下例:
page = [[
An HTML Page
lua
]]
在table类型中,a.x和a[x]是不一样的。前者表示a["x"],表示以字符串“x”来索引table。而后者是以变量x的值来索引table。
虽然可以用任何值作为一个table的索引,也可以用任何数字作为数组索引的起始值。但就Lua的习惯而言,数组通常以1作为索引的起始值。并且有不少机制依赖于这个惯例。
在Lua5.1中,长度操作符“#”用于返回一个数组或线性表的最后一个索引值(或为其大小)。例如:
for i=1, #a do
print(a[i])
end
注意:所有未初始化的元素的索引结果都是nil。Lua将nil作为界定数组结尾的标志。当一个数组有“空隙”时,即中间含有nil时,长度操作符会认为这些nil元素就是结尾标志。可以肯定我们不会想要这种不可预期性。因此应该避免对那些含有“空隙”的数组使用长度操作符。大多数数组不会包含“空隙”,因此大多数时候使用长度操作符是安全的。如果真的需要处理那些含有“空隙”的数组,可以使用函数table.maxn,它将返回一个table的最大正索引数。例如:
a = {}
a[1000] = 1
print(table.maxn(a)) --> 1000
在Lua中,函数是作为“第一类值”来看待的。这表示函数可以存储在变量中,可以通过参数传递给其他函数,还可以作为其他函数的返回值。
逻辑操作符有 and、or 和 not。与条件控制语句一样,所有的逻辑操作符将 false 和 nil 视为假,而将其它的任何东西视为真。对于操作符 and 来说,如果它的第一个操作数为假,就返回第一个操作数;不然返回第二个操作数。对于操作符 or 来说,如果它的第一个操作数为真,就返回第一个操作数;不然返回第二个操作数。
print(4 and 5) -->5
print(nil and 13) -->nil
print(false and 13) -->false
print(4 or 5) -->4
print(false or 5) -->5
有一种常用的Lua习惯写法“x=x or v”,它等价于:
if not x then
x = v
end
它可用在没有设置 x 的时候,将其设置为一个默认值 v。
另一种习惯写法是“(a and b)or c”,这类似与C语言中的表达式 a?b:c,但前提是 b 不为假。例如,为了选出数字 x 和 y 中的较大者,可以使用以下语句:
max = (x>y) and x or y
要在Lua中连接两个字符串,可以使用操作符“..”,如果其任意一个操作数是数字的话,Lua会将这个数字转换成一个字符串:
print("Hello" .. "world") -->Hello World
print(0 .. 1) -->01
每当Lua评估一个构造式时,都会先创建一个新table,然后初始化它。这样,就能用table写出以下的链表代码:
list = nil
for line in io.lines() do
list = {next = list , value = line}
end
这段代码从标准输入中读取每行的内容,然后将每行按相反的次序存储到一个链表中。链表的每个结点都是一个table,table中含有两个字段:value(每行的内容)和next(指向下一个结点的引用)。但在真实的Lua程序中很少会用到链表。
在多重赋值中,Lua先对等号右边的所有元素求值,然后才执行赋值。这样便可以用一句多重赋值来交互两个变量。如下所示:
x,y = y,x -- 交换x与y
在Lua中,有一种习惯写法是:
local foo = foo
这句代码创建了一个局部变量foo,并将用全局变量foo的值初始化它。
Lua将0和空字符串也视为真。
因为那些位于return或break之后的语句将无法执行到,所以通常只能在上述几个位置使用这些语句。然而有时可能希望在一个块的中间插入一句return或break。例如,准备调试一个函数,但又不想执行该函数的内容。在这种情况中,可以使用一个显示的do块来包住一条return语句:
function foo()
return --<<语法错误
-- 在下一个块中return就是最后一条语句
do return end--OK
<其它语句>
end
Lua会调整一个函数的返回值数量以适应不同的调用情况。若将函数调用作为一条单独语句时,Lua会丢弃函数的所有返回值。若将函数作为表达式的一部分来调用时,Lua只保留函数的第一个返回值。只有当一个函数调用是一系列表达式中的最后一个元素(或仅有一个元素)时,才能获得它的所有返回值。
在Lua中,函数是一种“第一类值”,它们具有特定的词法域。
“第一类值”:表示在Lua中函数与其他传统类型的值(例如数字和字符串)具有相同的权利。函数可以存储到变量中(无论全局变量还是局部变量)或table中,可以作为实参传递给其它函数,还可以作为其它函数的返回值。
“词法域”:指一个函数可以嵌套在另一个函数中,内部的函数可以访问外部函数中的变量。
所谓“迭代器”就是一种可以遍历一种集合中所有元素的机制。在Lua中,通常将迭代器表示为函数。每调用一次函数,即返回集合中的“下一个”元素。
每个迭代器都需要在每次成功调用之间保持一些状态,这样才能知道它所在的位置及如何步入下一个位置。
泛型for在循环过程内部保存了迭代器函数。实际上它保存着3个值:一个迭代器函数、一个恒定状态(invariant state)和一个控制变量(control variable)。
泛型for的语法如下:
for in do
end
其中,
for k,v in pairs(t) do print (k,v) end
其中变量列表是“k,v”,表达式列表只有一个元素pairs(t)。一般来说变量列表中也只有一个变量,例如下面这个循环:
for line in io.lines() do
io.write(line,"\n")
end
变量列表的第一个元素称为”控制变量”。在循环过程中该值决不会为nil,因为当它为nil时循环就结束了。
for做的第一件事情是对in后面的表达式求值。这些表达式应该返回3个值供for保存:迭代器函数、恒定状态和控制变量的初值。这里有点类似于多重赋值,即只有最后一个表达式才会产生多个结果,并且只会保留前3个值,多余的值会被丢弃;而不足的话,将以nil补足。
在初始化步骤之后,for会恒定状态和控制变量来调用迭代器函数。然后for 将迭代器函数的返回值赋予变量列表中的变量。如果第一个返回值为nil,那么循环终止。否则,for执行它的循环体,随后再次调用迭代器函数,并重复这个过程。
更 明确地说,以下语句:
for var_1,...,var_n in do end
等价于以下代码:
do
local _f,_s,_var =
while true do
local var_1, ..., var_n = _f(_s, _var)
_var = var_1
if _var == nil then break end
end
end
因此,假设迭代器函数为f,恒定状态为s,控制变量的初值为a0。那么在循环过程中控制变量的值依次为a1 = f(s,a0)、a2 = f(s,a1),依此类推,直至ai为nil结束循环。如果for还有其他变量,那么它们也会在每次调用f后获得额外的值。
尽管将Lua称为是一种解释型的语言,但Lua确实允许在运行源代码前,先将源代码预编译为一种中间形式。听上去“编译”似乎不应该在一种解释型语言的范畴之列。其实,区别解释型语言的主要特征并不在于是否能编译它们,而是在于编译器是否是语言运行是库的一部分,即是否有能力(并且轻易地)执行动态生成的代码。可以说正是因为存在了诸如dofile这样的函数,才可以将Lua称为是一种解释型的语言。
当一个函数遭遇了一种未预期的状况(即“异常”),它可以采取两种基本的行为:返回错误代码(通常是nil)或引发一个错误(调用error)。在这两种选择之间并没有固定的法则,但通常的指导原则是:易于避免的异常应引发一个错误,否则应返回错误代码。
协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈、局部变量和指令指针,同时又与其他协同程序共享全局变量和其他大部分东西。从概念上讲线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作地运行。就是说,一个具有多个协同程序的程序在任意时刻只能运行一个协同程序,并且正在运行的协同程序只会在其显式地要求挂起时,它的执行才会暂停。
一个协同程序可以处于4种不同的状态:挂起、运行、死亡和正常。当创建一个协同程序时,它处于挂起状态。