目录

Lua初探-声明


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初始化。,因此局部变量可以在任何显式块之外的块中声明。