编译
- lua 是解释语言
- 但 lua 允许在运行源代码前,先将源代码编译为一种中间形式
- 区别解释语言的主要特征并不在于是否能编译它们
- 在于编译器是否是语言运行时库的一部分
- 是否有能力执行动态生成的代码
loadfile 函数
-
dofile
函数是一种内置的操作,用于运行 lua 代码块 -
dofile
仅是做了loadfile
的辅助工作 -
loadfile
会从一个文件加载 lua 代码块 -
但不会运行代码,只是编译代码
-
然后将编译结果作为一个函数返回
-
dofile
会引发错误 -
loadfile
只会抛出错误值,但不处理错误
function dofile(filename)
-- assert 返回错误值
local f = assert(loadfile(filename))
return f()
end
- 发生错误时,
loadfile
会返回nil
及错误消息,可自定义错误消息 - 在需要多次运行一个文件时,只需调用一次
loadfile
,多次调用它的返回结果,也就是那个函数即可 - 而
dofile
开销则相比loadfile
大得多,因为loadfile
只编译一次文件
loadstring 函数
- 从一个字符串中读取代码
- 同样会返回一个函数
- 开销很大,因为在每次调用
loadstring
时都会编译一次 - 而
function
的写法只在编译对于程序块时被编译了一次
i = 0
f = loadstring("i = i + 1") -- 等效于 f = function() i = i + 1 end
f()
print(i) -- 1
f()
print(i) -- 2
-- dostring 完成加载并运行代码
assert(loadstring(s))() -- 语法错误 "attempt to call a nil value"
loadstring
编译时不涉及词法域loadsting
只在全局环境中编译字符串,而非局部环境
i = 32
local i = 0
f = loadstring("i = i + 1; print(i)")
g = function() i = i + 1; print(i) end
f() -- 33 使用了全局变量
g() -- 1 使用了局部变量
- 可以执行外部代码
do
print("enter you expression:")
local l = io.read()
local func = assert(loadstring("return '" .. l .. "'"))
print("the value of your expression is " .. func())
end
do
print("enter function to be plotted(with variable 'x'):")
local l = io.read()
local f = assert(loadstring("return " .. l))
for i = 1, 20 do
x = i
print(x .. ":" .. string.rep("*", f()))
end
end
-
loadfile
和loadstring
,有一个真正的原始函数load
-
loadfile
和loadstring
分别从文件和字符串中读取程序块 -
load
接收一个「读取器函数」,并在内部调用它来获取程序块 -
读取器函数可以分几次返回一个程序块,
load
会反复调用它,直到它返回nil
(表示程序块结束)为止 -
只有当程序块不在文件中,或者程序块过大而无法放入内存时,才会用到
load
-
lua 将所有独立的程序块视为一个匿名函数的函数体,并且该匿名函数还具有可变长实参
-
与其他函数一样,程序块中可以声明局部变量
loadstring("a = 1") -- 等效于 function(...) a = 1 end
f = loadstring("local a = 10; print(a + 10)")
f() -- 20
- 重写读取输入示例,避免使用全局变量 x
print("enter function to be plotted (with variable 'x'):")
local l = io.read()
local f = assert(loadstring("local x = ...; return " .. l))
for i = 1, 20 do
print(string.rep("*", f(i)))
end
- load 函数不会引发错误。在错误发生时,
load
会返回nil
以及一条错误信息
print(loadstring("a a"))
-- nil [string "a a":1 '=' expected nead 'a']
loadfile
和loadstring
不会带来任何副作用- 它们只是将程序块编译为一种中间表示,然后将结果作为一个匿名函数来返回。
- 而并不是加载了一个程序块,或定义了其中的函数
- 函数定义是一种赋值操作,是在运行时才完成的操作。
-- 编写一个 lua 文件,命名为 foo
function foo(x)
print(x)
end
-- 在 cmd 中的 lua 解释器中输入
f = loadfile("你存放 foo 文件的路径")
print(foo()) -- nil
f() -- 定义函数
foo("test") -- test
- 执行外部代码的一些操作
- 处理加载程序块时报告任何错误
- 如果代码不受信任,需要在保护环境(即之前提到的「沙盒」中执行这些代码)