2.4 - 语句段(Statement)
Lua 支持惯例形式的语句段,它和 Pascal 或是 C 很相象。这个集合包括赋值,控制结构,函数调用,还有变量声明。
2.4.1 - Chunk(语句组)
Lua 的一个执行单元被称作 chunk。一个 chunk 就是一串语句段,它们会被循序的执行。每个语句段可以以一个分号结束:
chunk ::={stat [`;´]}
这儿不允许有空的语句段,所以 ';;' 是非法的。
lua 把一个 chunk 当作一个拥有不定参数的匿名函数(参见 §2.5.9)处理。正是这样,chunk 内可以定义局部变量,接收参数,并且返回值。
chunk 可以被保存在一个文件中,也可以保存在宿主程序的一个字符串中。当一个chunk 被执行,首先它会被预编译成虚拟机中的指令序列,然后被虚拟机解释运行这些指令。
chunk 也可以被预编译成二进制形式;细节参考程序 luac。用源码形式提供的程序和被编译过的二进制形式的程序是可以相互替换的; Lua 会自动识别文件类型并做正确的处理。
2.4.2 - 语句块
语句块是一列语句段;从语法上来说,一个语句块跟一个 chunk 相同:
block ::=chunk
一个语句块可以被显式的写成一个单独的语句段:
stat ::= doblock end
显式的语句块对于控制变量的作用范围很有用。有时候,显式的语句块被用来在另一个语句块中插入return 或是break (参见 §2.4.4)。
2.4.3 - 赋值
Lua 允许多重赋值。因此,赋值的语法定义是等号左边放一系列变量,而等号右边放一系列的表达式。两边的元素都用逗号间开:
stat ::=varlist1 `=´ explist1
varlist1 ::=var {`,´ var}
explist1 ::=exp {`,´ exp}
表达式放在 §2.5 里讨论。
在作赋值操作之前,那一系列的右值会被对齐到左边变量需要的个数。如果右值比需要的更多的话,多余的值就被扔掉。如果右值的数量不够需求,将会按所需扩展若干个nil。如果表达式列表以一个函数调用结束,这个函数所返回的所有值都会在对齐操作之前被置入右值序列中。(除非这个函数调用被用括号括了起来;参见§2.5)。
赋值段首先会做运算完所有的表达式,然后仅仅做赋值操作。因此,下面这段代码
i = 3
i, a[i] = i+1,20
会把 a[3] 设置为 20,而不会影响到a[4] 。这是因为 a[i] 中的 i 在被赋值为 4 之前就被拿出来了(那时是 3 )。简单说 ,这样一行
x, y = y, x
可以用来交换 x 和 y 中的值。
对全局变量以及 table 中的域的赋值操作的含义可以通过 metatable 来改变。对变量下标指向的赋值,即 t[i] = val 等价于 settable_event(t,i,val)。(关于函数 settable_event 的详细说明,参见§2.8。这个函数并没有在 Lua 中定义出来,也不可以被调用。这里我们列出来,仅仅出于方便解释的目的)
对于全局变量的赋值 x = val 等价于 _env.x = val,这个又可以等价于
settable_event(_env, "x", val)
这里,_env 指的是正在运行中的函数的环境。(变量 _env 并没有在 Lua 中定义出来。我们仅仅出于解释的目的在这里写出来。)
2.4.4 - 控制结构
if、 while、以及 repeat 这些控制结构符合通常的意义,而且也有类似的语法:
stat ::= whileexp do blockend
stat ::= repeatblock until exp
stat ::= ifexp then block {elseif expthen block} [else block]end
Lua 也有一个 for 语句,它有两种形式(参见 §2.4.5)。
控制结构中的条件表达式可以返回任何值。false 和 nil 两者都被认为是假条件。所有不同于nil 和 false 的其它值都被认为是真(特别需要注意的是,数字 0 和空字符串也被认为是真)。
在 repeat–until 循环中,内部语句块的结束点不是在until 这个关键字处,它还包括了其后的条件表达式。因此,条件表达式中可以使用循环内部语句块中的定义的局部变量。
return 被用于从函数或是 chunk(其实它就是一个函数)中返回值。 函数和 chunk 可以返回不只一个值,所以return 的语法为
stat ::= return[explist1]
break 被用来结束 while、 repeat、或for 循环,它将忽略掉循环中下面的语句段的运行:
stat ::= break
break 跳出最内层的循环。
return 和 break 只能被写在一个语句块的最后一句。如果你真的需要从语句块的中间return 或是break ,你可以使用显式的声名一个内部语句块。一般写作 do return end 或是 do break end,可以这样写是因为现在return 或break 都成了一个语句块的最后一句了。
2.4.5 - For 语句
for 有两种形式:一种是数字形式,另一种是一般形式。
数字形式的 for 循环,通过一个数学运算不断的运行内部的代码块。下面是它的语法:
stat ::= forName `=´ exp `,´ exp [`,´ exp]do block end
block 将把 name 作循环变量。从第一个 exp 开始起,直到第二个exp 的值为止,其步长为第三个 exp 。更确切的说,一个 for 循环看起来是这个样子
for v = e1,e2, e3 do block end
这等价于代码:
do
local var,limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
if not (varand limit and step) then error() end
while (step> 0 and var <= limit) or (step <= 0 andvar>= limit) do
local v = var
block
var= var + step
end
end
注意下面这几点:
一般形式的 for 通过一个叫作叠代器(iterators)的函数工作。每次叠代,叠代器函数都会被调用以产生一个新的值,当这个值为nil 时,循环停止。一般形式的 for 循环的语法如下:
stat ::= fornamelist in explist1do block end
namelist ::=Name {`,´ Name}
for 语句好似这样
for var_1,···, var_n in explist doblock end
它等价于这样一段代码:
do
local f,s, var = explist
while truedo
local var_1,···, var_n = f(s,var)
var= var_1
if var== nil then break end
block
end
end
注意以下几点:
2.4.6 - 把函数调用作为语句段
为了允许使用可能的副作用,函数调用可以被作为一个语句段执行:
stat ::=functioncall
在这种情况下,所有的返回值都被舍弃。函数调用在§2.5.8中解释。
2.4.7 - 局部变量声名
局部变量可以在语句块中任何地方声名。声名可以包含一个初始化赋值操作:
stat ::= localnamelist [`=´ explist1]
如果有的话,初始化赋值操作的行为等同于赋值操作(参见§2.4.3)。否则,所有的变量将被初始化为nil。
一个 chunk 同时也是一个语句块(参见 §2.4.1),所以局部变量可以放在 chunk 中那些显式注明的语句块之外。这些局部变量的作用范围从声明起一直延伸到chunk 末尾。
局部变量的可见规则在 §2.6 中解释。