Lua程序设计第二版(笔记) 第八章编译、执行与错误

来源:互联网 发布:光辉岁月 知乎 编辑:程序博客网 时间:2024/05/24 03:56

Lua是一种解释型的语言,但Lua确实允许在运行代码之前,先将代码预编译为一种中间形式。

区别解释型语言的主要特质是:不在于是否编译它们,而是在于编译器是否是语言运行库的一部分,即是否有能力执行动态生成的代码。

dofile是一种内存的操作,用于运行Lua代码块。实际上dofile是一个辅助函数,loadfild才做了真正核心的工作。类似dofile,loadfile会从一个文件加载Lua代码块,但它不会运行代码,只是编译代码,然后将编译结果作为一个函数返回。此外与dofile不同还有就是loadfile不会引发错误,它只是返回错误值并不处理错误。

一般dofile定义:

function dofile(filenme)

local f = assert(loadfile(filename))

return f()

end

对于简单的任务而言,dofile非常便捷,它在一次调用中做完了整件事情。然而loadfile更灵活,在发生错误的情况中,loadfile会返回nil及错误消息,这便可以按自定义的方式来处理错误。还有就是如果要多次执行一个文件,loadfile值需要调用一次后,多次调用它的返回结果就行了。相对于多次调用dofile,开销就小得多。

函数loadstring与loadfile类似,不同之处是一个从字符串中读取代码,而非从文件中读取。

例如:

f = loadstring("i = i + 1")

f变成了一个函数,每次调用就执行"i = i + 1"

i = 0

f() print(i) -- 1

f() print(i) -- 2

loadstring优点功能非常强大,缺点是开销较大。

一般将loadstring用于字面字符串是没有意义的。

例如:

f = loadstring("i = i + 1")

基本上就等价于:

f = function() i = i + 1 end

第二段代码要快得多,因为它只在编译对应程序块时被编译了一次。而第一段代码却在每次调用 loadstring时都被重新编译。

loadstring在编译时不涉及词法域,所以上述两段代码并不等价。

i = 32
local i = 0
f = loadstring("i = i+1; print(i)")
g = function () i = i + 1 ;print(i) end
f() -- 33
g() -- 1

函数g如预期地操作了局部的 i ,但是f 操作的确实全局i ,这是因为loadstring总是在全局环境中编译它的字符串。

loadstring最典型的用处是执行外部代码,也就是那些位于程序之外的代码。

Lua将所有独立的程序块视为一个匿名函数的函数体,并且该匿名函数还是具有可变长实参。

例如:

function (...) a = 1 end

与其他函数一样,程序块中可以声明局部变量:

 f = loadstring("local a = 10; print( a+ 20 )")

f() -- 30


C代码

与Lua代码不同的是,C代码需要在使用前先链接入一个应用程序。

Lua通常不会包含任何无法通过ANSI C来实现的机制。

Lua提供的所有关于动态链接的功能都聚集在一个函数中,即package.loadlib。该函数有两个字符串参数:动态库的完整路径和一个函数名称。

例如:

local path = "/user/local/lib/lua/5.1/socket.so"

local f = package.loadlib( path, "luaopen_socket")

laodlib函数加载指定的库,并将链接入Lua。它并没有调用库中的任何函数。相反,他将一个C函数作为Lua函数返回。如果在加载库或者查找初始化函数时发生任何错误,loadlib返回nil及一条错误消息。

loadlib 是一个非常底层的函数。必须提供库的完整路径及正确的函数名称。通常使用require来加载C程序库,这个函数会搜索指定的库,然后用loadlib来加载库,并返回初始化函数。


错误

一旦发生错误,Lua应该结束当前程序块

当一个函数遭遇了一种未预期的状况(即“异常”),它可以采取两种基本的行为:返回错误代码(通常是nil)或引发一个错误(调用error )。在这两种选择之间并没有固定的法则,但通常的指导原则而是:易于避免的异常应引发一个错误,否则应返回错误代码。

获知一个文件是否存在的例子:

local fil, msg

repeat

print "enter a file name :"

local name = io.read()

if not name then return end -- 无输入

file, msg = io.open( name "r" )

if not file then print(msg) end

until file

如果不想处理这种情况,但仍想安全运行程序,只需要使用assert来检测操作即可。

file = assert( io.open(name, "r") )

这是一种典型的Lua技巧,如果io.open失败了,assert 就引发一个错误。

file = assert( io.open("no-file", "r" ) ) -- stdin:l:no-file:No such file or directory


错误处理与异常

如果需要在Lua中处理错误,必须使用函数pcall来包装需要执行的代码。

例如;

function foo()

<一些代码>

if 未预期的条件 then error() end

<一些代码>

print(a[i]) --潜在的错误:a可能不是一个table

<一些代码>

end

使用pcall来调用foo:

if pcall( foo )then

--执行foo时没有发生错误

<常见代码>

else

--foo引发了一个错误,采取适当的行动

<错误处理代码>

end

在调用pcall可以传入一个匿名函数:

if pcall ( function() 

<受保护代码>

end) then

<常规代码>

else<错误处理代码>

end

pcall函数会以一种“保护模式”来调用它的第一个参数,因此pcall可以捕获函数执行中的任何错误。入股没有发生错误,pcall会返回true 及函数的返回值;否则,返回false及错误消息。

通过这些机制可以在Lua中完成所有的异常处理了。可以使用error来抛出一个异常或使用pcall来捕获异常,而错误消息则可以标识出错误的类型和内容。

















原创粉丝点击