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
  • 用于循环的whilerepeatfor

所有控制结构语法上都有一个显式的终结符:

  • end用于终结ifforwhile结构;
  • 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遍历迭代函数返回的所有值,例如pairsipairsio.lines()等,利用好可以遍历几乎所有数据结构。

function add(...)
    local sum = 0
    for _, v in ipairs{...} do
        sum = sum + v
    end
    return sum
end

泛型for可以使用多个变量,每次循环时会更新这些变量。当第一个变量为nil时,循环终止。

break, return和goto

breakreturn语句用于从当前的循环结构中跳出;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