《LUA程序设计》学习笔记

第一章   开始

若使用命令行参数 -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中,可以作为实参传递给其它函数,还可以作为其它函数的返回值。

“词法域”:指一个函数可以嵌套在另一个函数中,内部的函数可以访问外部函数中的变量。


第七章 迭代器与泛型for

所谓“迭代器”就是一种可以遍历一种集合中所有元素的机制。在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种不同的状态:挂起、运行、死亡和正常。当创建一个协同程序时,它处于挂起状态。

你可能感兴趣的:(阅读书籍笔记)