书本下载地址 http://download.csdn.net/detail/myy2012/5349646
本部分下载地址 http://download.csdn.net/detail/myy2012/5349605
lua程序设计第二版 读书笔记(1-4章)
第一章 开始
第二章 类型与值
第三章 表达式
第四章 语句
http://blog.csdn.net/myy2012/article/details/8900424
lua程序设计第二版 读书笔记(5-8章)
第五章 函数
第六章 深入函数
第七章 迭代器与泛型for
第八章 编译执行与错误
http://blog.csdn.net/myy2012/article/details/8906466
lua程序设计第二版 读书笔记(9-10章)
第九章 协同程序
第十章 完整的实例
http://blog.csdn.net/myy2012/article/details/8911206
lua程序设计第二版 读书笔记(11-14章)
第十一章 数据结构
第十二章 数据文件与持久性
第十三章 元表metatable与元方法meatmethod
第十四章 环境
http://blog.csdn.net/myy2012/article/details/8914457
lua程序设计第二版 读书笔记(15-17章)
第十五章 模块与包
第十六章 面向对象编程
第十七章 弱引用 table
http://blog.csdn.net/myy2012/article/details/8921632
lua程序设计第二版 读书笔记(18-21章)
第十八章 数学库
第十九章 table库
第二十章 字符串库
第二十一章 IO库
http://blog.csdn.net/myy2012/article/details/8925895
lua程序设计第二版 读书笔记(22-23章)
第二十二章 操作系统库
第二十三章 调试库
http://blog.csdn.net/myy2012/article/details/8930181
无论哪种方法调用函数,必须在函数名后跟一个(),如果有参数的话就全部写到()内。
“若实参多余形参,则舍弃多余的实参; 若实参不足,则多余的形参初始化为nil”
5.1多重返回值
Lua允许函数返回多个结果。
s, e = string.find("hello lua users", "lua")
print(s, e)
function foo0()end
function foo1()return "a"end
function foo2()return "a", "b"end
如果一个函数调用不是一系列表达式的最后一个元素,那么只产生一个值:
x , y = foo2(), 20 -- x=”a”, y=20
x, y = foo0(), 20, 30 --x=nil, y=20, 30被舍弃
print(foo2()..x) --ax
也可以将一个函数调用放入一对圆括号中,从而迫使它只返回一个结果
Print( ( foo2()) ) --a
5.2 变长参数(variable number of argument )
Lua中的函数还可以接受不同数量的实参。
function add(...)
local s=0
for i, v in ipairs{...} do
s = s +v
end
return s
end
print(add(3, 4, 5))
参数表中的3个点(...)表示该函数可接受不同数量的实参。
表达式(...)的行为类似于一个具有多重返回值的函数,它返回的是当前函数的所有变长参数。
function foo(...)
local a, b, c = ...
print(a,b,c)
end
function fool(...)
print("calling foo:", ...)
return foo(...)
end
fool(1, 2, 3)
5.3具名实参(named argument)
实参是通过它在参数表中的位置与形参匹配起来的,第一个实参的值与第一个形参相匹配,以此类推。但有时通过名称来指定实参也是很有用的,主要是将所有实参组织到一个table中,并将这个table作为唯一的实参传给函数。
当实参只有一个table构造式时,函数调用中的圆括号是可以省略的:
如 rename{old = “temp.lua”, new = “temp1.lua”}
函数的一种“第一类值”(First-Class Value),它们具有特定的词法域(Lexical Scoping)。
第一类值:这表示在Lua中函数与其他传统类型的值(例如数字、字符串)具有相同的权利。
函数可以存储到变量中或table中,可以作为实参传递给其他函数,还可以作为其他函数的返回值。
词法域:一个函数可以嵌套在另一个函数中,内部的函数可以访问外部的函数中的变量。
函数是“值”:一个函数定义实际上就是一条语句(赋值语句),这条语句创建了一种类型为“函数”的值,并将这个值赋予给一个变量。 结果称为一个“匿名函数”。
function foo(x) return 2*x end
foo = function(x) return 2*x end
次序函数(order function):table中的sort函数并没有提供所有的排序准则,而是提供了一个可选的参数。
例如:network = {
{name = “grauna”,IP=”210.26.30.34”},
{name = “arraial”, IP=”210.26.30.23”},
{name = “lua”, IP=”210.26.23.12”}
}
以name为字段、按反方向的字符顺序来对这个table排序:
table.sort( network, function(a, b) return (a.name>b.name) end)
高阶函数(higher-order function):接受另一个函数作为实参的,像sort这样的函数。
6.1 闭包函数(closure)
--闭包函数
function newCounter()
local i=10 --非局部变量 upvalue
return function() --匿名函数
i=i+10
return i
end
end
c1=newCounter()
c2=newCounter()
print(c1()) --20
print(c1()) --30
print(c1()) --40
print(c2()) --20
因此c1和c2是同一个函数所创建的2个不同closure,他们各自拥有局部变量i的独立实例。
--“沙盒”
print(math.sin(30)) ---0.98803162409286
do
local oldsin=math.sin
local k = math.pi/180
math.sin=function(x)
return oldsin(x*k)
end
end
print(math.sin(30)) --0.5
将原来不安全的版本保存到closure的一个私有变量这两个,从而使得外部再也无法直接访问到原来的版本了,通过这个技术可以在Lua的语言层面上构建出一个安全的运行环境。
6.2 非全局的函数(non-global function)
函数不仅可以存储在全局变量、局部变量中,还可以存储在table的字段中。
如:lib={}
function lib.foo(x, y) return x+y end
function lib.foo(x, y) return x-y end
局部函数:将一个函数存储到一个局部变量中,该函数只能在某个特定的作用域中使用。
Lua是将每个程序块(chunk)作为一个函数来处理的,所以在一个程序块中声明的函数就是局部函数,这些函数只能在该程序块中可见。
--非全局函数
local fact
fact = function(n)
if 0==n then
return 1
else
return n*fact(n-1)
end
end
print(fact(5)) --120
local function fact2(n)
if 0==n then
return 1
else
return n*fact(n-1)
end
end
print(fact2(6)) --720
向前声明(Forward Declaration)
local f ,g --
function g()
<一些代码> f()
end
function f()
<一些代码> g()
end
6.3 正确的尾调用(proper tail call)
尾调用:一个函数调用是另一个函数的最后一个动作。 return
function f(x) return g(x) end
当g返回时,执行控制权可以直接返回到调用f的那个点上。
尾调用不会消耗栈空间。
判断准则:一个函数调用完另一个函数之后,是否就无其他事情需要做。
非尾调用举例:
function f(x) g(x) end
return g(x) +1
return x or g(x)
return (g(x))
尾调用举例:
return x[i].foo( x[j]+a*b, i+j)
尾调用的一大应用就是编写“状态机”,这种程序通常以一个函数来表示一个的状态,改变状态就是goto(或调用)到另一个特定的函数。
7.1迭代器与closure
迭代器:可以遍历(iterate over )一种集合中所有元素的机制。
每个迭代器都需要在每次成功调用之间保持一些状态,这样才能知道它所在的位置及如何到下一个位置。
closure:一个closure结构通常涉及2个函数:closure本身和创建closure的工厂(factory)函数。
--迭代器和closure
function values(t) --工厂函数
local i=0
return function()
i=i+1
return t[i]
end
end
t={101, 202, 303}
iter = values(t)
while true do
local element=iter()
if nil==element then
break
end
print("while "..element)
end
for element in values(t) do
print("for "..element)
end
以下函数可以遍历当前输入文件中所有单词的迭代器---allwords。为了完成这样的遍历,需要保持两个值:当前行内容(变量line)、该行中所处的位置(变量pos)。
function allwords()
local line=io.read() --当前行
local pos=1 --一行的当前位置
return function() --迭代器函数
while line do
local s, e=string.find(line, "%w+", pos)
if s then
pos=e+1
return string.sub(line, s, e)
else
line=io.read()
pos=1
end
end
return nil
end
end
for word in allwords() do
print(word)
end
7.2泛型for的语句
泛型for在循环过程内部保存了迭代器函数:一个迭代器函数f、一个恒定状态s、一个控制变量a。
泛型for的语法: for
end
其中:
for k, v in pairs(t) do print(k, v) end
for line in io.lines() do io.write(line, “\n”) end
说明:1.for做的第一件事就是对in后面的表达式求值。返回3个值供for保存:一个迭代 器函数、一个恒定状态、一个控制变量。(多则弃,少则nil补)
2.在初始化步骤之后,for会以恒定状态和控制变量来调用迭代器函数,然后for将迭 代器函数的返回值赋予变量列表中的变量。如果第一个返回值为nil,那么循环结 束,否则,for执行它的循环体,随后再次调用迭代器函数,并重复这个过程。
假设迭代器函数为f,恒定状态为s,控制变量的初值为a0,那么在循环过程中控制变量的值依次为a1=f(s, a0),a2=f(s, a1),以此类推,直到ai为nil循环结束。
7.3无状态的迭代器
无状态的迭代器:一种自身不保存任何状态的迭代器。
好处:在多个循环中使用同一个无状态的迭代器,避免创建新的closure的开销。
这类迭代器的一个典型例子就是ipairs, 它可以用来迭代一个数组的所有元素:
a={"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end
函数pairs与ipairs类似,也是用于遍历一个table中的所有元素,不同的是,它的迭代器函数是Lua中的一个基本函数next
function pairs(t)
return next, t, nil
end
function iparis(t)
return iter, a, 0
end
7.4 具有复杂状态的迭代器
一个迭代器通过一个table就可以保存任意多的数据,此外在循环过程中还可以改变这些数据(恒定状态总是同一个table,但是table的内容是可以改变的)。
由于这种迭代器可以在恒定状态中保存所有数据,所以它们通常可以忽略泛型for提供的第二个参数(控制变量)。
local iterator
function allwords2()
local state={line=io.read, pos=1}
return iterator, state
end
function iterator(state)
while state.line do
local s,e=string.find(state.line, "%w+", state.pos)
if s then
state.pos=e+1
return string.sub(state.line, s, e)
else
state.line=io.read()
state.pos=1
end
end
return nil
end
尽可能尝试编写无状态的迭代器,那些迭代器将所有状态保存在for变量中,不需要在开始一个循环时创建任何新的对象。如果迭代器无法套用这个模型,那么就应该尝试使用closure
7.5真正的迭代器
“迭代器”这个名称多少有点误导的成分,因为迭代器并没有做实际的迭代,真正做迭代的是for循环。而迭代器只是为每次迭代提供一些成功后的返回值。或许,更准确地称其未“生成器”。
function allwords(f)
for line in io.lines() do
for word in string.gmatch(line, "%w+") do
f(word) -- 调用函数
end
end
end
Lua是一种解释型的语言,有能力(并且轻易地)执行动态生成的代码。
可以说正是因为存在了诸如dofile这样的函数,才可以将Lua称为是一种解释型的语言。
8.1编译
loadstring函数与loadfile函数类似,不同之处在于它是从一个字符串中读取代码,而非从文件中读取。例如,以下代码:
f = loadstring(“i=i+1”)
f就变成了一个函数,每次调用时就执行“i=i+1”:
i=0
f();print(i) -- 1
f();print(i) -- 2
loadstring总是在全局环境中编译它的字符串。
载入lua文件,有文件foo.lua内容如下:
function foo(x)
print(x)
end
执行以下代码:
f= loadfile(“foo.lua”)
这样函数foo就完成编译了,但是还没有定义它。要定义它,就要执行以下程序块:
print(foo) --nil
f() --定义foo
foo(“ok”) --ok
若需要在一个商业品质的程序中执行外部代码,那么还应该处理加载程序块时报告的任何错误。此外,如果代码是不可信任的话,可能还需要在一个保护环境中执行这些代码,以防各种问题的产生。
8.2 C代码
Lua提供的所有关于动态链接的功能都聚集在一个函数中,即package.loadlib(...)。该函数有2个字符串参数:动态库的完整路径、函数名称。
例如:local path = “/usr/local/lib/lua/5.1/socket.so”
local f=package.loadlib(path, “luaopen_socket”)
loadlib 函数加载指定的库,并将其链接入Lua,将C函数作为Lua函数的返回(并没有调用库中的任何函数)。
通常使用require来加载C程序库(这个函数会搜索指定的库),然后用loadlib来加载库,并返回初始化函数。
8.3错误(error)
Lua所遇到的任何未预期条件都会引发一个错误。
Assert函数检查其第一个参数时候为true,若为true,则简单地返回该参数;否则(为false或nil)就引发一个错误。第二个参数是一个可选的信息字符串。
print“enter a number:”
n = io.read(“*number”)
if not n then
error(“invalid input”)
end
当一个函数遭遇了一种未预期的状况(异常),它可以采取2种基本的行为:返回错误代码(通常是nil)、引发一个错误(调用error)。
原则:易于避免的异常应引发一个错误,否则应返回错误代码。
8.4错误处理与异常
对于大多数应用程序而言,无须在Lua代码中作任何错误处理,应用程序本身会负责这类问题。如果执行中发生了错误,此调用会返回一个错误代码,这样应用程序就能采取适当的行动来处理错误。
如果需要在Lua中处理错误,则必须使用方式pcall(protect edcall 受保护的调用)来包装需要执行的代码。
if pcall(foo) then
--执行foo时没有发生错误
<常规代码>
else
--foo引发了一个错误,采取适当的行动
<错误处理代码>
end
8.5 错误消息与追溯(traceback)
Error方式还有第二个附加参数level(层),用于之处应由调用层级中的哪个(层)函数来报告当前的错误,也就是说明了谁应该为这错误负责。
例如:
function foo(str)
if type(str) ~= “string” then
error(“string expected”, 2)
end
<常规代码>
end