lua程序设计第二版 读书笔记(5-8章)

来源:互联网 发布:医学数据库检索方法 编辑:程序博客网 时间:2024/05/22 00:15

 

书本下载地址                       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

因此c1c2是同一个函数所创建的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  <func> (<args>)

 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(或调用)到另一个特定的函数。

第七章 迭代器与泛型for

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 <var-list> in <exp-list> do

<body>

end

其中:<var-list>为一个或多个变量名的列表

  <exp-list>为一个或多个表达式的列表(通常只有一个元素,即对迭代器工厂的调用)

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),以此类推,直到ainil循环结束。

7.3无状态的迭代器

无状态的迭代器:一种自身不保存任何状态的迭代器。

好处:在多个循环中使用同一个无状态的迭代器,避免创建新的closure的开销。

这类迭代器的一个典型例子就是ipairs, 它可以用来迭代一个数组的所有元素:

a={"one", "two", "three"}

for i, v in ipairs(a) do

print(i, v)

end

函数pairsipairs类似,也是用于遍历一个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,则简单地返回该参数;否则(为falsenil)就引发一个错误。第二个参数是一个可选的信息字符串。

print“enter a number:”

n = io.read(“*number”)

if not n then 

error(“invalid input”)

end

当一个函数遭遇了一种未预期的状况(异常),它可以采取2种基本的行为:返回错误代码(通常是nil)、引发一个错误(调用error)。

原则:易于避免的异常应引发一个错误,否则应返回错误代码。

8.4错误处理与异常

对于大多数应用程序而言,无须在Lua代码中作任何错误处理,应用程序本身会负责这类问题。如果执行中发生了错误,此调用会返回一个错误代码,这样应用程序就能采取适当的行动来处理错误。

如果需要在Lua中处理错误,则必须使用方式pcallprotect 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

 

原创粉丝点击