虽然我们把Lua当作解释型语言,但是Lua会首先把代码预编译成中间码然后再执行(很多解释型语言都是这么做的)。在解释型语言中存在编译阶段听起来不合适,然而,解释型语言的特征不在于他们是否被编译,而是编译器是语言运行时的一部分,所以,执行编译产生的中间码速度会更快。
--我们可以这样定义dofile:
function dofile (filename)
local f = assert(loadfile(filename))
return f()
end
--完成简单的功能dofile比较方便,他读入文件编译并且执行。
加载文件,这个函数相当于从文件里读出string,然后再调用loadstring(file_text)实现加载功能。
loadstring与loadfile相似,只不过它不是从文件里读入chunk,而是从一个串中读入。例如:
f = loadstring("i = i + 1")
f将是一个函数,调用时执行i=i+1。
i = 0
f(); print(i) --> 1
f(); print(i) --> 2
loadstring函数功能强大,但使用时需多加小心。确认没有其它简单的解决问题的方法再使用。
Lua把每一个chunk都作为一个匿名函数处理。例如:chunk "a = 1",loadstring返回与其等价的function () a = 1 end
与其他函数一样,chunks可以定义局部变量也可以返回值:
f = loadstring("local a = 10; return a + 20")
print(f()) --> 30
loadfile和loadstring都不会抛出错误,如果发生错误他们将返回nil加上错误信息:
print(loadstring("i i"))
--> nil [string "i i"]:1: '=' expected near 'i'
另外,loadfile和loadstring都不会有边界效应产生,他们仅仅编译chunk成为自己内部实现的一个匿名函数。通常对他们的误解是他们定义了函数。Lua中的函数定义是发生在运行时的赋值而不是发生在编译时。假如我们有一个文件foo.lua:
-- file `foo.lua'
function foo (x)
print(x)
end
当我们执行命令f = loadfile("foo.lua")后,foo被编译了但还没有被定义,如果要定义他必须运行chunk:
f() -- defines `foo'
foo("ok") --> ok
如果你想快捷的调用dostring(比如加载并运行):可以这样loadstring(s)()
调用loadstring返回的结果,然而如果加载的内容存在语法错误的话,loadstring返回nil和错误信息(attempt to call a nil value);为了返回更清楚的错误信息可以使用assert: assert(loadstring(s))()
还有一个重要区别:loadstring编译的时候不关心词法范围,该函数总是在全局环境中编译它的字符串,因此它将无法访问文件局部变量,而是只能访问全局变量,如:
i = 322
local i = 0
f = loadstring("i = i + 1; print(i)")
g = function() i = i + 1; print(i) end
f() --f函数中的i为全局变量i,因此输出33
g() --g函数中的i为局部变量i,因此输出1
对于loadstring返回的函数,如果需要对一个表达式求值,则必须在其之前添加return,这样才能构成一条语句,返回表达式的值,如:
i = 32
f = loadstring("i = i + 1; return i * 2")
print(f()) --输出66
print(f()) --输出68。由于loadstring返回的就是正规的函数,因此可以被反复调用。
这个函数是通过调用dofile()来实现的。不同的是,每次加载执行一个文件时,require()都会记录,保存在table package.loaded中,避免重复加载。另外,如果给定的路径找不到文件,require()会到指定的路径下去找输到加载的文件。文件名称可以省去.lua后缀。
Lua提供高级的require函数来加载运行库。粗略的说require和dofile完成同样的功能但有两点不同:
require使用的路径和普通我们看到的路径还有些区别,我们一般见到的路径都是一个目录列表。require的路径是一个模式列表,每一个模式指明一种由虚文件名(require的参数)转成实文件名的方法。更明确地说,每一个模式是一个包含可选的问号的文件名。匹配的时候Lua会首先将问号用虚文件名替换,然后看是否有这样的文件存在。如果不存在继续用同样的方法用第二个模式匹配。例如:?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua 调用require "lili"时会试着打开这些文件:
lili
lili.lua
c:\windows\lili
/usr/local/lua/lili/lili.lua
require是如何加载的呢?
require关注的问题只有分号(模式之间的分隔符)和问号,其他的信息(目录分隔符,文件扩展名)在路径中定义。
为了确定路径,Lua首先检查全局变量LUA_PATH是否为一个字符串,如果是则认为这个串就是路径;否则require检查环境变量LUA_PATH的值,如果两个都失败require使用固定的路径(典型的"?;?.lua")
require的另一个功能是避免重复加载同一个文件两次。Lua保留一张所有已经加载的文件的列表(使用table保存)。如果一个加载的文件在表中存在require简单的返回;表中保留加载的文件的虚名,而不是实文件名。所以如果你使用不同的虚文件名require同一个文件两次,将会加载两次该文件。比如require "foo"和require "foo.lua",路径为"?;?.lua"将会加载foo.lua两次。我们也可以通过全局变量_LOADED访问文件名列表,这样我们就可以判断文件是否被加载过;同样我们也可以使用一点小技巧让require加载一个文件两次。比如,require "foo"之后_LOADED["foo"]将不为nil,我们可以将其赋值为nil,require "foo.lua"将会再次加载该文件。
一个路径中的模式也可以不包含问号而只是一个固定的路径,比如:?;?.lua;/usr/local/default.lua
这种情况下,require没有匹配的时候就会使用这个固定的文件(当然这个固定的路径必须放在模式列表的最后才有意义)。在require运行一个chunk以前,它定义了一个全局变量_REQUIREDNAME用来保存被required的虚文件的文件名。我们可以通过使用这个技巧扩展require的功能。举个极端的例子,我们可以把路径设为"/usr/local/lua/newrequire.lua",这样以后每次调用require都会运行newrequire.lua,这种情况下可以通过使用_REQUIREDNAME的值去实际加载required的文件。
local path = "/usr/local/lua/lib/libluasocket.so" --由于loadlib是非常底层的函数,因为在调用时必须提供完整的路径名和函数名称
local f = package.loadlib(path, "luaopen_socket")
loadlib函数
加载指定的库并且连接到Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为Lua的一个函数,这样我们就可以直接在Lua中调用他。如果加载动态库或者查找初始化函数时出错,loadlib将返回nil和错误信息。我们可以修改前面一段代码,使其检测错误然后调用初始化函数:
local path = "/usr/local/lua/lib/libluasocket.so"
-- or path = "C:\\windows\\luasocket.dll"
local f = assert(loadlib(path, "luaopen_socket"))
f() -- actually open the library
一般情况下
我们期望二进制的发布库包含一个与前面代码段相似的stub文件,安装二进制库的时候可以随便放在某个目录,只需要修改stub文件对应二进制库的实际路径即可。将stub文件所在的目录加入到LUA_PATH,这样设定后就可以使用require函数加载C库了。
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")
assert函数将检查其第一个参数是否为true,如果是,则简单的返回该参数,否则就引发一个错误。
第二个参数是可选字符串。
function foo()
local a = 10
print(a[2])
end
r,msg = pcall(foo)
if r then
print("This is ok.")
else
print("This is error.")
print(msg)
end
--This is error.
--E:\workplace\CB1.lua:21: attempt to index local 'a' (a number value)
错误信息不一定仅为字符串(下面的例子是一个table),传递给error的任何信息都会被pcall返回:
r, msg = pcall(function() error({code = 121}) end)
if r then
print("This is ok.")
else
print("This is error.")
print(msg.code)
end
--This is error.
--121
function errorFunc()
local a = 20
print(a[10])
end
function errorHandle()
print(debug.traceback())
end
if xpcall(errorFunc,errorHandle) then
print("This is OK.")
else
print("This is error.")
end
--输出结果为:
--[[stack traceback:
d:/test.lua:7: in function
d:/test.lua:3: in function
[C]: in function 'xpcall'
d:/test.lua:10: in main chunk
[C]: ?
This is error.
--]]