Lua支持的常规语句基本上和C语言所支持的那些语句差不多。这些语句包括赋值、控制结构和过程调用。另外Lua还支持一些不太常见的语句,例如多重赋值和局部变量声明。
1、赋值
赋值的基本含义是修改一个变量或一个table中字段的值:
Lua允许"多重赋值",也就是一下子将多个值赋予多个变量。每个值或每个变量之间以逗号分隔。例如:
在多重赋值中,Lua先对等号右边的所有元素求值,然后才执行赋值。这样便可以用一句多重赋值来交互两个变量了,例如:
Lua总是会将等号右边值的个数调整到与左边变量的个数相一致。规则是:若值的个数少于变量的个数,那么多余的变量会被赋值为nil;若值的个数更多的话,那么多余的值会被“悄悄地”丢弃掉。
所以若要初始化一组变量,应为每个变量提供一个值;
2、局部变量与块
相对于全局变量,Lua还提供了局部变量。通过local语句来创建局部变量:
与全局变量不同的是,局部变量的作用于仅限于声明它们的那个块。一个块(block)是一个控制结构的执行体、或者是一个函数的执行体,再或者是一个程序块(chunk)。
如果在交互模式下,输入一条指令,Lua就会马上运行这句话,并未下一行的运行开启一个新的程序块。为了让解释器一起执行程序块,可以通过do-end
关键字显式地声明程序块,这样Lua就不会单独地执行后面每行的内容,而是直至遇到一个相应的end时,才会执行整个块的内容,例如:
尽可能地使用局部变量是一种良好的编程风格,例如如下:
- 局部变量可以避免将一些无用的名称引入全局环境,避免搞乱了全局环境;
- 访问局部变量比访问全局变量更快;
- 一个局部变量通常会随着其作用域的结束而消失,这样便使垃圾收集器可以释放其值;
Lua将局部变量的声明当做语句来处理,因此可以在任何允许书写语句的地方书写局部变量的声明。所声明的局部变量的作用于从声明语句开始,直至所在块的结尾。声明语句中还可以包含初始化赋值,其规则与普通的赋值语句完全一样:额外的值会被丢弃;额外的变量会被赋值为nil。如果一条声明语句没有初始化赋值,那么它声明的所有变量都会初始化为nil。
在Lua中,有一种习惯用法是:
这句代码创建了一个局部变量foo,并将用全局变量foo的值初始化它。如果后续其他函数改变了全局foo的值,那么可以在这里先将它的值保存起来。这种方式还可以加速在当前作用域中对foo的的访问。
由于许多语言都强制程序员在一个块(或一个过程)起始处声明所有的局部变量,所以某些人就认为在一个块的中间使用声明语句是一种不好的习惯。但事实恰恰相反,在需要时才声明变量,可以使这个变量在初始化时刻就拥有一个有意义的初值。此外,缩短变量的作用域有助于提高代码的可读性。
3、控制结构
Lua提供了一组传统的、小巧的控制结构,包括用于条件执行的if
,用于迭代的while
、repeat
和for
。所有的结构都有一个显式的终止符:if
、for
和while
以end作为结尾,repeat
以until作为结尾。
控制结构中的条件表达式可以是任何值,Lua将所有不是false和nil的值视为“真”。
3.1、if then else
if语句先测试其条件,然后根据测试结果执行then部分或else部分,else部分是可选的;
若要编写嵌套的if,可以使用elseif,它类似于else后面紧跟着一个if,它还可以避免在这样的嵌套中出现多个end:
由于Lua不支持switch语句,所以这种一连串的if-elseif代码是很常见的。
3.2、while
与其它语言中的while循环一样,Lua先测试while的条件。如果条件为假,那么循环结束;不然,Lua执行循环体,并重复这一过程。
3.3、repeat
正如这个名字所暗示的那样,一条repeat-until
语句重复执行其循环体直到条件为假时结束。测试是在循环体之后做的,因此循环体至少会执行一次。
与其它大多数语言不同的是,在Lua中,一个声明在循环体中的局部变量的作用于包括了条件测试:
3.4、数字型 for
for语句有两种形式:数字型for和泛型for
数字型for的语法如下:
var从Init变化到Term,每次变化都以Step作为步长递增var,并执行一个“执行体”。第三个表示式Step是可选的,若不指定的话,Lua会将步长默认为1,示例如下:
如果不想给循环设置上限的话,可以使用常量math.huge
:
为了更好地使用for循环,还需要了解一些小细节。首先,for的3个表达式是在循环开始前一次性求值的。例如,上例中的f(x)只会执行一次。其次,控制变量会被自动声明为for语句的局部变量,并且仅在循环体内可见,因此,控制变量在循环结束后就不存在的。
如果需要在循环结束后访问控制变量的值(通常是在break循环时),必须将该值保存到另一个变量中,例如:
最后一点,不要在循环过程中修改控制变量的值,否则会导致不可预知的效果。如果想在for循环正常结束前终止循环,可以使用break
语句。
3.5、泛型 for
泛型for循环通过一个迭代器(iterator)函数来遍历所有值:
Lua的基础库提供了ipairs
,这是一个用于遍历数组的迭代器函数。在每次循环中,i会被赋予一个索引值,同时v被赋予一个对应于该索引的数组元素值。
从外观上看泛型for比较简单,但其实它是非常强大的。通过不同的迭代器,几乎可以遍历所有的东西,而且写出的diam极具可读性。标准库提供了几种迭代器,例如:
迭代器 | 作用 |
io.lines | 迭代文件中的每行 |
pairs | 迭代table元素 |
ipairs | 迭代数组元素 |
string.gmatch | 迭代字符串中单词 |
当然,也可以编写自己的迭代器。
泛型for循环与数字型for循环有两个相同点:
- 循环变量是循环体的局部变量;
- 决不应该对循环变量作任何赋值;
对于泛型for的使用,再举一个例子。假设有这样一个table,它的内容是一周中每天的名称:
现在要将一个名称转换成它在一周的的位置,。为此,需要根据给定的名称来搜索这个table。然后在Lua中,通常更有效的方法是创建一个“逆向table”。例如这个逆向table叫revDays,它以一周中每天的名称作为索引,位置数字作为值:
接下来,要找出一个名称所对应的序号,只需用名字来索引这个reverse table即可:
当然,不必手动声明这个逆向table,而是通过原来的table自动地构造出这个逆向table:
这个循环会为每个元素赋值,其中变量k为key(1、2、…),变量v为value(“Sunday”、“Monday”、…)。
4、break与return
break
和return
语句用于跳出当前的块。
break
语句用于结束一个循环,它只会跳出包含它的那个内部循环(for、repeat和while),而不会改变外层的循环。在执行了break后,程序会在那个被跳出的循环之外继续执行。
return
语句用于从一个函数中返回结果,或者用于简单地结束一个函数的执行。任何函数的结尾处都有一句隐式的return。所以如果有一个函数,它么有值需要返回,那么就无须在其结尾处添加return语句。
由于语法构造的原因,break和return只能是一个块的最后一条语句。