快速掌握Lua 5.3 —— 编译,运行以及错误

来源:互联网 发布:飞飞cms安装ck播放器 编辑:程序博客网 时间:2024/05/21 10:19

Q:load()loadfile()dofile()

A:
1、

-- "a.lua"文件中的内容。--print("Hello Lua!")-------------------------- load()编译指定字符串中的代码,并将代码以一个函数的形式返回。f = load("print(\"Hello Lua!\")")f()    --> Hello Lua!-- loadfile()编译指定文件中的代码,并将代码以一个函数的形式返回。f = loadfile("a.lua")f()    --> Hello Lua!-- dofile()编译指定文件中的代码并执行。dofile("a.lua")    --> Hello Lua!

实际上dofile()只是个辅助函数,它在内部调用了loadfile()

function dofile (filename)  local f = assert(loadfile(filename))  return f()end

2、当指定的代码有语法错误时,load()loadfile()都不会报错,而只是返回错误码以及错误描述,而dofile()会报错。所以load()loadfile()使用更灵活,我们可以自己处理错误,而dofile()使用更方便,一次调用完成所有任务,错误处理也可以托管给Lua。

Q:当load()loadfile()以及dofile()加载一个函数时,与普通的函数创建的区别?

A:load()loadfile()以及dofile()不关心”lexical scoping”,他们总是将自己放在全局环境下。

-- "a.lua"文件中的内容。--i = i + 1------------------------i = 0    -- global i.function print_global_i()    io.write(string.format("Global i is %d\t", i))enddo    local i = 0    f = function () i = i + 1 end    f_load = load("i = i + 1")    f_loadfile = loadfile("a.lua")    f(); print_global_i(); print(string.format("Local i is %d.", i))    f(); print_global_i(); print(string.format("Local i is %d.", i))    f_load(); print_global_i(); print(string.format("Local i is %d.", i))    f_load(); print_global_i(); print(string.format("Local i is %d.", i))    f_loadfile(); print_global_i(); print(string.format("Local i is %d.", i))    f_loadfile(); print_global_i(); print(string.format("Local i is %d.", i))    dofile("a.lua"); print_global_i(); print(string.format("Local i is %d.", i))    dofile("a.lua"); print_global_i(); print(string.format("Local i is %d.", i))end

Global i is 0 Local i is 1.
Global i is 0 Local i is 2.
Global i is 1 Local i is 2.
Global i is 2 Local i is 2.
Global i is 3 Local i is 2.
Global i is 4 Local i is 2.
Global i is 5 Local i is 2.
Global i is 6 Local i is 2.

Q:require()是如何工作的?

A:require()dofile()的功能类似,他们之间的区别在于,
1、require()会在指定的路径(类似于”linux”的”PATH”)中搜索文件。
2、require()会自动识别文件是否被加载过,以防止重复加载。
所以,require()更适合加载库文件(或者说叫“模块”,”modname”)。
require()查找文件所使用的路径类似于如下形式,
?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua
其中;分割每个路径,?代表在查找过程中将被替换为文件名的部分。路径中也可以不加问号,比如:
?;?.lua;/usr/local/default.lua
require()找不到要找的文件时,就会加载这个指定的文件(default.lua)。

require "lili"    -- 如此调用,将尝试打开如下文件(或者说叫“模块”,"modname")。

lili
lili.lua
c:\windows\lili
/usr/local/lua/lili/lili.lua

require()使用package.loaded表存储已加载过的文件,保证同一个文件不会被重复加载,

-- "a.lua"文件中的内容。--a = 5------------------------require "a"    -- 加载"a.lua"。for i, v in pairs(package.loaded) do    print(i, v)end-- resultbit32   table: 0x1338800debug   table: 0x1335d90io  table: 0x13353a0os  table: 0x1335040a   true    <-- "a.lua"已经被加载了_G  table: 0x13329f0coroutine   table: 0x1332770package table: 0x1334470table   table: 0x13350d0utf8    table: 0x1337b70math    table: 0x1334e80string  table: 0x1332df0

Q:如何触发异常?

A:使用error(),第一个参数指定错误信息。

print "enter a number:"n = io.read("*number")    -- 规定输入的是一个数字。if not n then error("invalid input") end

Lua提供一种更优雅的方式,assert()

print "enter a number:"n = assert(io.read("*number"), "invalid input")    -- 如果io.read()的结果为假,则报错,错误信息为指定的信息。

error()第二个参数指定错误信息中报告的出错位置。1代表出错位置指向error()被调用的位置,2代表出错位置指向调用error()的函数被调用的位置。

function foo (str)  if type(str) ~= "string" then    -- 你的函数中有个检查,规定输入参数一定是字符串。    error("string expected")  endend-- call--[[ 如果有这样的调用,     Lua会将出错的地方指向你的判断部分(即foo()中调用error()的部分),     因为error()第二个参数默认值为1。]]foo({x = 1})

这看起来有些不妥,因为并不是函数中对类型判断有问题,实际上是调用的地方出了问题。那么我们可以将error()的第二个参数指定为2

error("string expected", 2)    -- 此时错误信息将指向foo()的部分。

Q:如何控制报错的方式?

A:如果你不想让程序受到异常的影响而中断,即使是一些Lua当错误发生时默认会抛出异常的情况(比如字符串与数字比较大小),那么你可以使用pcall()pcall()在安全模式下调用你指定的函数,这意味着被调函数中所有的异常都会被pcall()捕获。pcall()接收被调函数以及被调函数的参数,如果被调函数在执行过程中没有触发异常,则它返回true以及被调函数所有的返回值,否则它返回false以及错误信息。

function foo(var1, var2)    return (var1 > var2)            and var1 .. " is larger than " .. var2            or var1 .. " is smaller than " .. var2endr, s = pcall(foo, 1, 0)print(r, s)    --> true 1 larger than 0r, s = pcall(foo, -1, 0)print(r, s)    --> true -1 smaller than 0r, s = pcall(foo, "1", 0)print(r, s)    --> false attempt to compare number with string

同时pcall()也接受匿名函数,还是上面的例子,改成如下亦可,

r, s = pcall(function (var1, var2)                return (var1 > var2)                and var1 .. " is larger than " .. var2                or var1 .. " is smaller than " .. var2            end, "1", 0)

错误信息不仅可以是字符串,其他任何lua的值均可,

local status, err = pcall(function () error({code=121}) end)print(err.code)    --> 121

附加:

1、Lua虽然是解释型语言(就像”shell”一样),但是它也会将源码编译。这并不奇怪,因为许多解释型语言都是这么做的。解释型语言与编译型语言的差异在于,解释型语言的编译过程是在运行过程中完成的,并且在运行过程中无处不在。
2、注意,对于使用loadfile()加载一个存储函数的文件时,loadfile()只是将创建函数的代码进行了编译,并没有执行创建函数的过程(即没有声明函数),

-- "a.lua"文件中的内容。--function foo(var)    print(var)end------------------------f = loadfile("a.lua")foo(5)    --> attempt to call a nil value (global 'foo')f()    -- 执行创建函数的过程,即声明函数。 -- 其实这里就是在执行"a.lua"中的代码。foo(5)    --> 5

3、如果我们想多次使用同一个文件,那么调用一次loadfile()然后多次使用他的结果,这样比多次调用dofile()要好(这样会多次读取文件,因为其内部多次loadfile())。
4、”Q & A”中提到的load()loadfile()以及dofile()的使用方法均是使用他们加载语句(加载的结果不能赋值给其他变量或函数),而如果你想加载表达式(加载的结果可以赋值给其他变量或函数),需要在代码前面加上一个return

print "enter function to be plotted (with variable 'x'):"local l = io.read()local f = assert(load("return " .. l))    -- 返回表达式。for i = 1, 10 do    x = i   -- global 'x' (to be visible from the chunk)    print(string.rep("*", f()))    -- "f()"将被替换为用户输入的代码。end-- result> 2*x**************************************************************************************************************

5、当一个函数出现异常时,它有两种方式可以选择:
(1) 返回错误码(经典的情况是返回nil)。
(2) 报错。
没有固定的规则说明当错误发生时该选择哪一种方式,但是有一条指导方针:如果一个错误很容易被避免(而且是没有合理的理由证明它应该出现的),那么当这种错误发生时就报错;否则,将会返回错误码。
举个例子,当math.sin()的参数是一个”table”时我们该怎么做?假设我们返回一个错误码,那么如果我们想判断是否出错也许应该这么写,

local res = math.sin(x)if not res then     -- error  ...

或者更简单的,我们可以在调用math.sin()之前检查参数,

if not tonumber(x) then     -- error: x is not a number  ...

但是通常我们既不检查参数,也不检查函数返回结果。如果参数不是数字,说明我们的程序在哪里出了问题。如果是那样的话,触发异常是最简单,最直接的方法,这类错误属于很容易被避免的,而且是没有合理的理由证明它应该出现的。
接下来再考虑io.open()打开一个不存在文件的情况。在调用io.open()之前没有简单的方法可以检测出文件是否存在,并且在很多系统上,判断一个文件是否存在的方法就是去尝试打开它。所以当io.open()无法打开文件时,它返回一个nil加上一段错误描述,这样你就可以合理的处理这些错误。
6、当错误发生的时候,我们常常希望了解更详细的信息(比如函数调用栈信息),而不仅是错误发生的位置。但pcall()返回错误信息时,已经释放了保存错误发生情况的栈信息。所以如果我们想获取,需要在pcall()被调用之前获取。然而,Lua提供了xpcall(),它比pcall()多接收一个错误处理函数。当错误发生时,在函数调用栈信息被释放之前,Lua会调用指定的错误处理函数,这样就可以在该错误处理函数中使用debug库获得更多的信息。debug库中两个常用的函数,

debug.debug()    -- 进入交互模式(类似于Lua的交互模式)。输入命令查看信息。-- lua交互模式报错时也是通过此函数打印的函数调用栈信息。debug.traceback()    -- 打印函数调用栈信息。

7、Lua在package.loadlib()中提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:

local path = "/usr/local/lua/lib/libluasocket.so"local f = package.loadlib(path, "luaopen_socket")

package.loadlib()加载指定的库并且连接到Lua,然而它并不打开库(也就是说没有调用初始化函数)。他返回初始化函数作为Lua的一个函数,这样我们就可以直接在Lua中调用他。如果加载动态库或者查找初始化函数时出错,package.loadlib()将返回nil和错误信息。

0 0
原创粉丝点击