07-补充知识
争取两三周入门Lua!
本文简要介绍了Lua的“局部变量和代码块”,“控制结构等”基础语法部分。
局部变量和代码块
Lua语言中的变量在默认情况下是 全局变量,所有的局部变量在使用前必须用local
声明。局部变量的生效范围仅限于声明它的代码块。在交互模式中,每一行代码就是一个代码段(除非这行代码不完整)。
可以用do-end
显式区分一个代码块:
do
local a2 = 2 * a
end
运行时动态替换
也称猴子补丁(Monkey patching):
local foo = foo
这段代码声明了一个局部变量foo
,使用全局变量foo
对其初始化。这个用法在需要提高对foo
的访问速度时很有用;当其他函数改变了全局foo
的值,而代码段又需要全局foo
的原始值时,这个用法也很有用。
除了可以替换变量外,也能替换函数,例如local print = print
,在此之前用的都是默认print
,在此之后用的是自定义print
。
尽可能地使用局部变量是一种良好的编程风格:
- 局部变量可以避免由于不必要的命名而造成全局变量的混乱;
- 局部变量还能避免同一程序中不同代码部分中的命名冲突;
- 访问局部变量比访问全局变量更快;
- 局部变量会随着其作用域的结束而消失,使得GC能够将其释放。
控制结构
Lua提供了一组精简常用的控制结构:
- 用于条件执行的
if
; - 用于循环的
while
,repeat
,for
;
所有控制结构语法上都有一个显式的终结符:
end
用于终结if
,for
和while
结构;until
用于终结repeat
结构。
if then else
if op == "+" then
r = a + b
elseif op == "-" then
r = a - b
end
Lua语言不支持switch
语句。
while
先测试条件是否为真,是的话就循环,否则循环结束。
local i = 1
while a[i] do
print(a[i])
i = i + 1
end
repeat
重复执行循环体直到条件为真时结束,由于条件测试在循环体之后执行,所以循环体至少会执行一次。
-- 牛顿迭代法求平方根
local sqr = x / 2
repeat
sqr = (sqr + x / sqr) / 2
local error = math.abs(sqr^2 - x)
until error < x / 10000
可以发现,循环体内声明的局部变量作用域包括测试条件。
for
数值型
for var = st, ed [, step] do
sth
end
在循环中,var
的值从st
以步长(可选,默认为1)step
增长至ed
,每步执行一次sth
。如果不想设置上限,可以让ed = math.huge
。
要注意的是,循环变量var
的作用域仅限于循环体内。
泛型
泛型for
遍历迭代函数返回的所有值,例如pairs
,ipairs
,io.lines()
等,利用好可以遍历几乎所有数据结构。
function add(...)
local sum = 0
for _, v in ipairs{...} do
sum = sum + v
end
return sum
end
泛型for
可以使用多个变量,每次循环时会更新这些变量。当第一个变量为nil
时,循环终止。
break, return和goto
break
和return
语句用于从当前的循环结构中跳出;goto
语句则允许跳转到函数中的几乎任何地方。
这里看一下goto
,它用于将当前程序跳转到相应的标签处继续执行。在使用goto
跳转时,Lua语言设置了一些限制条件:
- 标签遵循常见的可见性规则,因此不能直接跳转到一个代码块中的标签(该标签对外不可见)
goto
不能跳转到函数外goto
不能跳转到局部变量的作用域
goto
语句在编写状态机时也很有用,如下代码用于检验输入是否包含偶数个0:
-- 标签语法: ::标签名::
::s1:: do
local c = io.read(1)
if c == '0' then goto s2
elseif c == nil then print 'ok'; return
else goto s1
end
end
::s2:: do
local c = io.read(1)
if c == '0' then goto s1
else if c == nil then print 'not ok'; return
else goto s2
end
end
goto s1