Lua初探-声明
- 更多分享:www.catbro.cn
Statements 声明
Lua支持几乎常规的语句集,类似于Pascal或C中的语句。该集包括赋值,控制结构,函数调用和变量声明
一、Blocks 块
-
一个快就是一个声明列表,它是可执行的序列:
block :: = {stat}
-
Lua有空语句,允许您用分号分隔语句,用分号开始一个块或按顺序写两个分号:
stat ::= ‘;’
-
函数的调用和赋值以左括号开始,这样可能会导致Lua语法的歧义,考虑如下代码段:
a = b + c (print or io.write)('done')
-
这语法可以用两种方式去解释:
a = b + c(print or io.write)('done') a = b + c; (print or io.write)('done')
-
当前解析器总是以第一种方式看到这样的结构,将开括号解释为调用参数的开始。 为避免这种歧义,最好始终使用以括号开头的分号语句:
;(print or io.write)('done')
-
可以明确分隔块以生成单个语句:
stat ::= do block end
-
显式块对于控制变量声明的范围很有用。显式块有时也用于在另一个块的中间添加return语句
二、Chunks
Lua的编译单元称为块。从语法上讲,块只是一个块:
chunk ::= block
- Lua将一个块作为一个可变参数的匿名函数进行处理。例如,块可以定义本地变量,接收参数,返回值。此外,这样的匿名函数被编译到一个叫做_ENV的外部本地变量的域里面。这样方式出现了函数总是存在一个_ENV的upvalue,即使他不被使用。
- 一个块也可以被存储在宿主程序内的一个文件中或者一个字符串中。要执行一个块,Lua第一步是加载它,将块代码预编译为虚拟机的指令,然后Lua用虚拟机的解析器执行编译的代码。
- 块也可以被预编译为二进制形式;详细可查看luac和string.dump函数。源代码和编译形式的程序是可以互换的; Lua会自动检测文件类型并采取相应措施
三、Assignment 赋值
Lua允许多赋值。因此,赋值语法定义左侧的变量列表和右侧的表达式列表,两个列表中的元素用逗号分隔:
stat ::= varlist ‘=’ explist
varlist ::= var {‘,’ var}
explist ::= exp {‘,’ exp}
-
赋值前,值列表将调整为变量列表的长度,如果值多余所需值,过多的值将会被丢弃。如果值少于所需的值,则列表将根据需要扩展为nil。在调整之前,如果表达式列表以函数调用形式结束,然后该调用返回的所有值将进入值列表,
-
赋值语句首先执行所有的表达式,然后再赋值,因此如下代码
i = 3 i, a[i] = i+1, 20
-
20赋值给a[3], 会不会是a[4]呢?i+1不是会先执行么?当然不会,虽然i+1先被评估,但是并没有进行赋值操作,最终结果是i=4 a[3]=20;类似的如下这行
x, y = y, x
-
其交换了x和y的值
-
赋值给一个全局变量x=value等效于赋值给_ENV,x=val
-
表字段和全局变量(实际上也是表字段)的赋值的含义可以通过元表更改.
四、Control Structures 控制结构
控制结构if,while和repeat具有通常的含义和熟悉的语法
stat ::= while exp do block end
stat ::= repeat block until exp
stat ::= if exp then block {elseif exp then block} [else block] end
-
一个控制结构的条件表达式可以返回任意的值。只有false和nil被看做是false,其余的都被当做true来处理。注意:0和空字符串也会被作为true进行处理。
-
在repeat-until循环中,内部块不以until关键字结束,而是仅在条件之后结束。因此,条件可以引用循环块内声明的局部变量
-
goto语句将程序控制转移到标签。出于语法原因,Lua中的标签也被视为语句:
stat ::= goto Name stat ::= label label ::= ‘::’ Name ‘::’
-
label在定义它的整个块中是可见的,除了在内部嵌套块中并在内部嵌套函数内定义相同名称的label。只要goto语句没有进入局部变量的范围,他就可以跳转到任何可见的标签中。
-
break语句终止执行while,repeat或for循环,跳转到循环后的下一个语句:中断结束最里面的封闭循环
stat ::= break
-
return语句用于从一个函数或者块(块在lua中被视为匿名函数)中返回值。函数可以返回超过一个值得结果,所以return语句的语法糖如下:
stat ::= return [explist] [‘;’]
-
return语句中可以卸载块的最后一句。 如果确实需要在块的中间返回,那么可以使用显式内部块,如在习语中返回end,因为现在return是其(内部)块中的最后一个语句
五、For Statement for语句
for语句有两种形式:一种是数字形式,一种是通用形式。 数字for循环一个代码块。控制循环的变量通过算术处理。for循环具有以下语法:
stat ::= for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end
-
从第一个exp开始循环该块,直到其通过第二个exp,不步长按第三个exp操作。
for v = e1, e2, e3 do block end
-
上面代码等效于
do local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3) if not (var and limit and step) then error() end var = var - step while true do var = var + step if (step >= 0 and var > limit) or (step < 0 and var < limit) then break end local v = var block end end
-
注意事项如下:
- 在循环开始之前,所有三个控制表达式仅被评估一次。它们的评估结果必须都是数字。
- var,limit,和step 都是不可见的变量,此处列出仅用于说明。
- 如果第三个表达式 step不存在,默认是1。
- 你可以使用break和goto去跳出一个for循环
- 循环变量v是循环体内的一个局部变量。如果你需要在循环后使用该变量,可以在循环退出前将其的值赋值给其他的变量。
-
以函数的形式工作常用for语句我们称之为迭代器。在每次迭代时,调用迭代器方法生成一个新值,当值为nil时循环停止。语法如下:
stat ::= for namelist in explist do block end namelist ::= Name {‘,’ Name}
-
一个for循环语句如下:
for var_1, ···, var_n in explist do block end
-
其等价于如下代码:
do local f, s, var = explist while true do local var_1, ···, var_n = f(s, var) if var_1 == nil then break end var = var_1 block end end
-
注意事项如下:
- explist只会被执行一次,执行结果一个为迭代器函数,一个为状态,还有一个为迭代器的第一个初始值。
- f,s和var是不可见的变量,此处列出只是为了说明。
- 你可以使用braek退出for循环
- 循环变量var_i是循环的局部变量,你不能在循环结束后使用。付过你需要其值,你可以在循环结束前或者break前将其值赋值给其他变量。
六、Function Calls as Statements
函数可以作为声明进行调用
stat ::= functioncall
在这种情况下,所有返回的值都将被丢弃
七、局部声明
局部变量可以在块的任意地方进行声明,声明时可给变量一个初始值:
stat ::= local namelist [‘=’ explist]
- 如果存在赋值语句,则初始赋值具有与多赋值相同的语义。 否则,所有变量都用nil初始化。,因此局部变量可以在任何显式块之外的块中声明。