Lua 语法

一、注释

行注释 —

块注释 —[[

]]

Lua语句之间并不需要分隔符。因为解释器会自动识别分隔语句。也可使用';'分隔语句。

二、类型与值

Lua的8种基础类型:

nil
全局变量未赋值前就为nil,将nil赋予全局变量等于删除它;
boolean
将false和nil视为假,其它全为真,包括0和空字符串;
number
双精度浮点数;
string
不可改变值,可将二进制数据存储字符串中,可用[[ ... ] ]延伸多行字符串;
table
关联数组,可用出了nil以外的类型来索引。table是一个对象,持有它的变量只有对它的引用。
function
函数也是第一类型;
userdata
表示由应用程序或C语言库所创建的新类型;
thread
协同程序

三、表达式

算术操作符: + - * / ^ % ;

关系操作符: < > <= >= == ~= ; 返回true或false; 对于table、userdata和function,Lua做的是引用比较。

逻辑操作符: and、or和not ; 返回操作数; 短路求值。

字符串连接: ... ;连接成新的字符串或数字转化连接成的新字符串,所以会有效率问题;

优先级问题查手册。

四、语句

1、多重赋值,对等号右边的值全部求完后在赋值,常用于变量交换,额外的值会被丢弃、额外的变量会被赋予nil

2、局部变量 local ... , 优点是访问快和便于回收

3、块 do ... end

4、控制结构

  • if then else
  • while
  • repeat , 测试条件的语句众仍可以访问error
  • 数字型for , 所有条件表达式会在开始求一次值,控制变量会自动的声明为locale
  • 泛型for , 标准库提供的迭代器:文件每行(io.lines)、table元素(pairs)、数组元素(ipairs)、字符串中的单词(string.gmatch)
  • break
  • return

五、函数

当参数是字符串或者table构造式时,函数后面的括号可有可无。

o.foo(o,x)可写成o:foo(x),更符合面向对象编程。

Lua对实参也进行类似多重赋值的操作。

Lua的多重返回值(multipile results)功能。Lua会调整一个函数的返回值数量以适应不同的调用情况:
1. 将函数调用作为一条单独语句时,Lua会丢弃所有返回值;
2. 将函数调用作为表达式一部分时,Lua只保留函数的第一个返回值;
3. 将函数调用作为(1.多重赋值、2.函数参数调用时传入的实参列表、3.table构造式、4.return语句)时,才能获得它所有返回值;

泛型调用: C语言中没法写泛型调用编码,最多是声明一个能接受变长参数的函数(通过stdarg.h),或者使用函数指针调用不同的函数。 就是说每次必须传入固定数量的参数,并且每个参数都具有确定的类型。 Lua就可以做到泛型调用,ex: f(unpack(a))。

变长参数:... 作为函数参数时表示该函数可接受不同数量的实参。

具名实参: ex: rename{old="temp.lua", mew="temp1.lua"}

六、深入函数

1. 匿名函数

函数作为“第一类值”会使Lua能应用函数式语言中的编程技术。 Lua中函数是匿名的,没有名称。当讨论一个函数名时(如print),实际是在讨论一个持有某函数的变量。

Lua中实际创建函数只是一个魔法糖而已: function foo (x) return 2*x end 只是下式的简化: foo = function (x) return 2*x end

匿名函数也有很大用处,如: table.sort(network, function (a,b) return (a.name > b.name) end)

2. 非局部变量

非局部变量:既非全局变量,又非局部变量,是在函数内部定义的函数 使用的外部函数的局部变量 Lua中,只有closure,函数是一种特殊的closure。closure可以使得超出函数作用范围的变量仍可以被closure访问。 这种技术可以实现很多强大的功能,比如方便重定义库函数,创建“沙盒”。

3. 非全局的函数

只要将一个函数存储到一个变量中,即得到一个“局部函数”,也就是说该函数只能在特定的作用域中使用。 这对于“程序包(packet)”而言,这种函数定义非常有用。因为Lua是将每个程序块(chunk)作为一个函数来处理的。 所以在一个程序块中声明的函数就是局部函数,这些局部函数只在程序块中可见。 "词法域"确保了程序包中其他函数可以使用这些局部函数。 定义局部函数: local fact fact = function (n) ... end 语法糖: local function face (n) ... end

4. 尾调用消除

当一个函数调用是另外一个函数的最后一个动作时,该调用称作一个尾调用。 在Lua中,只有"return ()这样调用形式才算作一条尾调用。 Lua支持尾调用消除。如当function f(x) return g(x) end,当f调用完g就无事可做了,也就不需要保存f的函数栈, 因此当g返回时,会直接将控制权交给调用f的那个点上。如果这样做递归嵌套,就不会耗费栈空间。 应正确区分调用形式是否为尾调用。 这种技术一大应用就是编写“状态机”。

七、 迭代器与泛型for

“迭代器”就是一种可以遍历一种集合中所有元素的机制。 在Lua中,通常将迭代器表示为函数,与泛型for组合用。

泛型for的语法如下:
for in do

end

是变量名列表,变量列表的第一个元素称为“控制变量”,当为nil时循环就结束了。
是表达式列表,for对表达式列表求值应该返回3个值供for保存:
迭代器函数
可能返回多个变量对进行多重赋值,第一个变量会赋给控制变量,同样检查其如果为nil循环就结束
恒定状态
如需遍历的数组
控制变量
如需遍历数组的索引值 Lua会自动将的结果调整为3个。

如果需要保存的状态需要有多个,优先使用closure方法解决(?),其次还可将迭代器所需的所有状态打包成table。

八、 编译、执行与错误

区别解释性语言与编译性语言的一个主要特征是能否有能力(并且轻易地)执行动态生成的代码(如Lua的dofile之类函数)。

dofile —> loadfile —> loadstring - —-> load

Lua将所有独立的程序块视为一个匿名函数的函数体。

如果需要在Lua中处理错误,则必须使用pcall函数来包装需要执行的代码。

九、 协同程序

协同程序和线程差不多,也是一条指令序列,拥有自己独立的栈、局部变量和指令指针, 同时又与其它协同程序共享全局变量和其它大部分东西。

概念上讲协同程序需要彼此协作的运行,任意时刻只能运行一个协同程序,别且正在运行的协同程序只会在其显示 地要求挂起(suspend)时,执行才会暂停。而多线程程序可以同时运行几个线程。

十、TIPs

dofile()
立即执行一个文件
type()
返回变量的类型名称string
tonumber()
把变量转换成string
#
返回数组或线性表(table)的最后一个索引值
table.maxn()
返回一个table的最大正索引值
io.lines()
从文件读入每行
math.huge
unpack()
接受一个数组作为参数,并从下标为1开始返回数组,重要用途就是“泛型调用”

参考书籍

1. Lua程序设计 第二版 Roberto Ierusalimschy

发表评论