Lua特色:
1.可扩展性,可通过Lua代码或者C代码扩展
简单:简单
高效率:平均效率最高
与平台无关:不是通过使用条件编译实现平台无关,而是完全使用ANSI C。所以只要有ANSI C编译器就可以编译并使用Lua。
2.Lua大部分强大的功能来自于它的类库。可通过新类型和函数来扩展其功能。动态类型检查最大限度允许多台出现,并自动简化调用内存管理的额接口,因为
这样不需要关心谁来分配内存谁来释放内存, 也不必担心数据溢出。高级函数和匿名函数均可以接受高级参数,使函数更为通用。
3.Lua的使用者
a.嵌入在应用程序,比如CGILua(搭建动态网页),LuaOrb(访问COBRA对象)。这些类型用Lua-API注册新函数,创建新类型。通过配置 Lua就可以
改变应用宿主语言的行为。
b.作为一种独立运行的语言,Lua 也是很有用的,主要用于文本处理或者只运
行一次的小程序。这种应用 Lua 主要使用它的标准库来实现,标准库提供模式匹配和其
它一些字串处理的功能。我们可以这样认为:Lua 是文本处理领域的嵌入式语言。
c.还有一些使用者使用其他语言开发,把 Lua 当作库使用。这些人大多使用 C语言开发,但使用 Lua建立简单灵活易于使用的接口。
如果你真得想学一门语言,参考手册是必备的。本书和 Lua 参考手册互为补充,手
册仅仅描述语言本身,因此他既不会告诉你语言的数据结构也不会举例说明,但手册是
Lua 的权威性文档,http://www.lua.org可以得到手册的内容。
-- Lua 用户社区,提供了一些第三方包和文档
http://lua-users.org
-- 本书的更新勘误表,代码和例子
http://www.inf.puc-rio.br/~roberto/book/
另外本书仅针对 Lua 5.0,如果你的版本不同,请查阅 Lua 手册或者比较版本间的差
异。
第一章
1.1Chunks
Chunk 是一系列语句,Lua 执行的每一块语句,比如一个文件或者交互模式下的每一行都是一个 Chunk。
每个语句结尾的分号(;)是可选的,但如果同一行有多个语句最好用;分开
一个 Chunk 可以是一个语句,也可以是一系列语句的组合,还可以是函数,Chunk
可以很大,在 Lua 中几个 MByte的Chunk 是很常见的。
你还可以以交互模式运行 Lua,不带参数运行 Lua:
你键入的每个命令(比如:"Hello World")在你键入回车之后立即被执行,键入文
件结束符可以退出交互模式(Ctrl-D in Unix, Ctrl-Z in DOS/Windows),或者调用 OS库
的 os.exit()函数也可以退出。
在交互模式下,Lua 通常把每一个行当作一个 Chunk,但如果 Lua 一行不是一个完
整的 Chunk时,他会等待继续输入直到得到一个完整的 Chunk.在 Lua 等待续行时,显示
不同的提示符(一般是>>).
可以通过指定参数让 Lua 执行一系列 Chunk。例如:假定一个文件 a 内有单个语句
x=1;另一个文件 b 有语句 print(x)
prompt> lua -la -lb
命令首先在一个 Chunk内先运行 a然后运行 b。(注意:-l 选项会调用 require,将会
在指定的目录下搜索文件,如果环境变量没有设好,上面的命令可能不能正确运行。
-i 选项要求 Lua 运行指定Chunk 后进入交互模式.
prompt> lua -i -la -lb
将在一个 Chunk 内先运行 a 然后运行 b,最后直接进入交互模式。
另一个连接外部 Chunk 的方式是使用 dofile 函数,dofile 函数加载文件并执行它.假
设有一个文件:
-- file 'lib1.lua'
function norm (x, y)
local n2 = x^2 + y^2
return math.sqrt(n2)
end
function twice (x)
return 2*x
end
在交互模式下:
> dofile("lib1.lua") -- load your library
> n = norm(3.4, 1.0)
> print(twice(n)) --> 7.0880180586677
-i 和 dofile 在调试或者测试 Lua 代码时是很方便的。
1.2全局变量
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初
始化的全局变量也不会出错,只不过得到的结果是:nil.
print(b); nil
如果你想删除一个全局变量,只需要将变量负值为 nil
b = nil
print(b) --> nil
这样变量 b 就好像从没被使用过一样.换句话说, 当且仅当一个变量不等于 nil 时,
这个变量存在。
1.3 词法约定
标示符:字母(letter)或者下划线开头的字母、下划线、数字序列.最好不要使用下划
线加大写字母的标示符,因为 Lua的保留字也是这样的。Lua 中,letter的含义是依赖于
本地环境的。
保留字:以下字符为 Lua 的保留字,不能当作标识符。
and break do else elseif
end false for function if
in local nil not or
repeat return then true until
while
注意:Lua 是大小写敏感的.
注释:单行注释:--
多行注释:--[[ --]]
--[[
print(10) -- no action (comment)
--]]
1.4 命令行方式
-e:直接将命令传入 Lua
prompt> lua -e "print(math.sin(12))" --> -0.53657291800043
-l:加载一个文件.
-i:进入交互模式.
_PROMPT 内置变量作为交互模式的提示符
prompt> lua -i -e "_PROMPT=' lua> '"
lua>
Lua 的运行过程,在运行参数之前,Lua 会查找环境变量 LUA_INIT 的值,如果变
量存在并且值为@filename,Lua 将加载指定文件。如果变量存在但不是以@开头,Lua
假定 filename 为 Lua 代码文件并且运行他。利用这个特性,我们可以通过配置,灵活的
设置交互模式的环境。可以加载包,修改提示符和路径,定义自己的函数,修改或者重
名名函数等。
全局变量 arg存放 Lua 的命令行参数。
prompt> lua script a b c
在运行以前,Lua 使用所有参数构造 arg 表。脚本名索引为 0,脚本的参数从 1 开始
增加。脚本前面的参数从-1 开始减少。
prompt> lua -e "sin=math.sin" script a b
arg 表如下:
arg[-3] = "lua"
arg[-2] = "-e"
arg[-1] = "sin=math.sin"
arg[0] = "script"
arg[1] = "a"
arg[2] = "b"
第二章 类型和值
Lua是动态类型语言,变量不要类型定义。 Lua中有8个基本类型分别为: nil、 boolean、
number、string、userdata、function、thread和 table。函数 type 可以测试给定变量或者值
的类型。
print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(X))) --> string
变量没有预定义的类型,每一个变量都可能包含任一种类型的值。
print(type(a)) --> nil ('a' is not initialized)
a = 10
print(type(a)) --> number
a = "a string!!"
print(type(a)) --> string
a = print -- yes, this is valid!
a(type(a)) --> function
注意上面最后两行,我们可以使用 function 像使用其他值一样使用(更多的介绍参
考第六章)。一般情况下同一变量代表不同类型的值会造成混乱,最好不要用,但是特殊
情况下可以带来便利,比如 nil。
2.1 Nil
Lua 中特殊的类型,他只有一个值:nil;一个全局变量没有被赋值以前默认值为 nil;
给全局变量负 nil 可以删除该变量。
2.2 Booleans
两个取值 false 和 true。但要注意 Lua中所有的值都可以作为条件。在控制结构的条
件中除了 false 和 nil 为假,其他值都为真。所以 Lua 认为0 和空串都是真。
2.3 Numbbers
表示实数,Lua 中没有整数。一般有个错误的看法 CPU运算浮点数比整数慢。事实
不是如此,用实数代替整数不会有什么误差(除非数字大于 100,000,000,000,000)。Lua
的 numbers 可以处理任何长整数不用担心误差。你也可以在编译 Lua的时候使用长整型
或者单精度浮点型代替 numbers,在一些平台硬件不支持浮点数的情况下这个特性是非
常有用的,具体的情况请参考 Lua发布版所附的详细说明。和其他语言类似,数字常量
的小数部分和指数部分都是可选的,数字常量的例子:
4 0.4 4.57e-3 0.3e12 5e+20
2 2.4 Strings
指字符的序列。lua是 8位字节,所以字符串可以包含任何数值字符,包括嵌入的 0。
这意味着你可以存储任意的二进制数据在一个字符串里。 Lua 中字符串是不可以修改的,
你可以创建一个新的变量存放你要的字符串,如下:
a = "one string"
b = string.gsub(a, "one", "another") -- change string parts
print(a) --> one string
print(b) --> another string
string 和其他对象一样,Lua 自动进行内存分配和释放,一个 string 可以只包含一个
字母也可以包含一本书,Lua 可以高效的处理长字符串,1M的 string 在 Lua 中是很常见
的。可以使用单引号或者双引号表示字符串
a = "a line"
b = 'another line'
为了风格统一,最好使用一种,除非两种引号嵌套情况。对于字符串中含有引号的
情况还可以使用转义符\来表示。Lua中的转义序列有:
\a bell
Copyright ? 2005, Translation Team, www.luac
\b back space -- 后退
\f form feed -- 换页
\n newline -- 换行
\r carriage return -- 回车
\t horizontal tab -- 制表
\v vertical tab
\\ backslash -- "\"
\" double quote -- 双引号
\' single quote -- 单引号
\[ left square bracket -- 左中括号
\] right square bracket -- 右中括号
例子:
> print("one line\nnext line\n\"in quotes\", 'in quotes'")
one line
next line
"in quotes", 'in quotes'
> print('a backslash inside quotes: \'\\\'')
a backslash inside quotes: '\'
> print("a simpler way: '\\'")
a simpler way: '\'
还可以在字符串中使用\ddd(ddd 为三位十进制数字)方式表示字母。
"alo\n123\""和'\97lo\10\04923"'是相同的。
还可以使用[[...]]表示字符串。这种形式的字符串可以包含多行也,可以嵌套且不会
解释转义序列,如果第一个字符是换行符会被自动忽略掉。这种形式的字符串用来包含
一段代码是非常方便的。
page = [[
<HTML>
<HEAD>
<TITLE>An HTML Page</TITLE>
</HEAD>
<BODY>
Lua
[[a text between double brackets]]
</BODY>
</HTML>
]]
io.write(page)
运行时,Lua 会自动在 string 和 numbers 之间自动进行类型转换,当一个字符串使
用算术操作符时,string 就会被转成数字。
print("10" + 1) --> 11
print("10 + 1") --> 10 + 1
print("-5.3e - 10" * "2") --> -1.06e-09
print("hello" + 1) -- ERROR (cannot convert "hello")
反过来,当 Lua 期望一个 string 而碰到数字时,会将数字转成 string。
print(10 .. 20) --> 1020
..在 Lua 中是字符串连接符,当在一个数字后面写..时,必须加上空格以防止被解释
错。
尽管字符串和数字可以自动转换,但两者是不同的,像 10 == "10"这样的比较永远
都是错的。如果需要显式将 string 转成数字可以使用函数 tonumber(),如果 string 不是正
确的数字该函数将返回 nil。
line = io.read() -- read a line
n = tonumber(line) -- try to convert it to a number
if n == nil then
error(line .. " is not a valid number")
else
print(n*2)
end
反之,可以调用 tostring()将数字转成字符串,这种转换一直有效:
print(tostring(10) == "10") --> true
print(10 .. "" == "10") --> true
2.5 Functions
函数是第一类值(和其他变量相同),意味着函数可以存储在变量中,可以作为函数
的参数,也可以作为函数的返回值。这个特性给了语言很大的灵活性:一个程序可以重
新定义函数增加新的功能或者为了避免运行不可靠代码创建安全运行环境而隐藏函数,
此外这特性在 Lua 实现面向对象中也起了重要作用(在第 16 章详细讲述)。
Lua 可以调用 lua 或者 C 实现的函数,Lua 所有标准库都是用 C 实现的。标准库包
括 string 库、table 库、I/O库、OS库、算术库、debug 库。
2.6 Userdata and Threads
userdata 可以将 C 数据存放在 Lua 变量中, userdata 在 Lua 中除了赋值和相等比较外
没有预定义的操作。userdata 用来描述应用程序或者使用 C 实现的库创建的新类型。例
如:用标准 I/O库来描述文件。下面在 C API章节中我们将详细讨论。
在第九章讨论协同操作的时候,我们介绍线程。
第 3 章 表达式
Lua 中的表达式包括数字常量、字符串常量、变量、一元和二元运算符、函数调用。
还可以是非传统的函数定义和表构造。
3.1 算数运算符
二元运算符:+ - * / ^ (加减乘除幂)
一元运算符:- (负值)
这些运算符的操作数都是实数。
3.2 关系运算符
< > <= >= == ~=
这些操作符返回结果为 false 或者true;==和~=比较两个值,如果两个值类型不同,
Lua 认为两者不同;nil 只和自己相等。Lua 通过引用比较 tables、userdata、functions。
也就是说当且仅当两者表示同一个对象时相等。
a = {}; a.x = 1; a.y = 0
b = {}; b.x = 1; b.y = 0
c = a
a==c but a~=b
Lua 比较数字按传统的数字大小进行,比较字符串按字母的顺序进行,但是字母顺
序依赖于本地环境。
当比较不同类型的值的时候要特别注意:
"0" == 0 -- false
2 < 15 -- true
"2" < "15" -- false (alphabetical order!)
为了避免不一致的结果,混合比较数字和字符串,Lua 会报错,比如:2 < "15"
3.3逻辑运算符
and or not
逻辑运算符认为 false 和nil 是假(false),其他为真,0 也是 true.
and 和 or的运算结果不是 true和 false,而是和它的两个操作数相关。
a and b -- 如果 a为false,则返回 a,否则返回 b
a or b -- 如果 a为true,则返回 a,否则返回 b
例如:
print(4 and 5) --> 5
print(nil and 13) --> nil
print(false and 13) --> false
print(4 or 5) --> 4
print(false or 5) --> 5
一个很实用的技巧:如果 x 为 false或者 nil 则给x 赋初始值v
x = x or v
等价于
if not x then
x = v
end
and 的优先级比 or高。
C 语言中的三元运算符
a ? b : c
在 Lua 中可以这样实现:
(a and b) or c
not 的结果一直返回 false或者 true
print(not nil) --> true
print(not false) --> true
print(not 0) --> false
print(not not nil) --> false
3.4连接运算符
.. --两个点
字符串连接,如果操作数为数字,Lua 将数字转成字符串。
print("Hello " .. "World") --> Hello World
print(0 .. 1) --> 01
3.5优先级
从高到低的顺序:
^
not - (unary)
* /
+ -
..
< > <= >= ~= ==
and
or
除了^和..外所有的二元运算符都是左连接的。
a+i < b/2+1 <--> (a+i) < ((b/2)+1)
5+x^2*8 <--> 5+((x^2)*8)
a < y and y <= z <--> (a < y) and (y <= z)
-x^2 <--> -(x^2)
x^y^z <--> x^(y^z)
3.6表的构造
构造器是创建和初始化表的表达式。表是 Lua特有的功能强大的东西。最简单的构
造函数是{},用来创建一个空表。可以直接初始化数组:
days = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
Lua 将"Sunday"初始化 days[1](第一个元素索引为 1) ,用"Monday"初始化 days[2].
print(days[4]) --> Wednesday
构造函数可以使用任何表达式初始化:
tab = {sin(1), sin(2), sin(3), sin(4),
sin(5),sin(6), sin(7), sin(8)}
如果想初始化一个表作为 record 使用可以这样:
a = {x=0, y=0} <--> a = {}; a.x=0; a.y=0
不管用何种方式创建 table,我们都可以向表中添加或者删除任何类型的域,构造函
数仅仅影响表的初始化。
w = {x=0, y=0, label="console"}
x = {sin(0), sin(1), sin(2)}
w[1] = "another field"
x.f = w
print(w["x"]) --> 0
print(w[1]) --> another field
print(x.f[1]) --> another field
w.x = nil -- remove field "x"
每次调用构造函数,Lua都会创建一个新的 table,可以使用 table 构造一个 list:
list = nil
for line in io.lines() do
list = {next=list, value=line}
end
这段代码从标准输入读进每行,然后反序形成链表。下面的代码打印链表的内容:
l = list
while l do
print (l.value)
l = l.next
end
在同一个构造函数中可以混合列表风格和 record 风格进行初始化,如:
polyline = {color="blue", thickness=2, npoints=4,
{x=0, y=0},
{x=-10, y=0},
{x=-10, y=1},
{x=0, y=1}
}
这个例子也表明我们可以嵌套构造函数来表示复杂的数据结构
print(polyline[2].x) --> -10
上面两种构造函数的初始化方式还有限制,比如你不能使用负索引初始化一个表中
元素,字符串索引也不能被恰当的表示。下面介绍一种更一般的初始化方式,我们用
[expression]显示的表示将被初始化的索引:
opnames = {["+"] = "add", ["-"] = "sub", ["*"] = "mul", ["/"] = "div"}
i = 20; s = "-"
a = {[i+0] = s, [i+1] = s .. s, [i+2] = s .. s .. s}
print(opnames[s]) -->sub
print(a[22]) --> ---
list 风格初始化和 record风格初始化是这种一般初始化的特例:
{x=0, y=0} <--> {["x"]=0, ["y"]=0}
{"red", "green", "blue"} <-->
{[1]="red", [2]="green", [3]="blue"}
如果真的想要数组下标从 0 开始:
days = {[0]="Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
注意:不推荐数组下标从 0 开始,否则很多标准库不能使用。
在构造函数的最后的","是可选的,可以方便以后的扩展。
a = {[1]="red", [2]="green", [3]="blue",}
在构造函数中域分隔符逗号(",")可以用分号(";")替代,通常我们使用分号用来
分割不同类型的表元素。
{x=10, y=45; "one", "two", "three"}
1.可扩展性,可通过Lua代码或者C代码扩展
简单:简单
高效率:平均效率最高
与平台无关:不是通过使用条件编译实现平台无关,而是完全使用ANSI C。所以只要有ANSI C编译器就可以编译并使用Lua。
2.Lua大部分强大的功能来自于它的类库。可通过新类型和函数来扩展其功能。动态类型检查最大限度允许多台出现,并自动简化调用内存管理的额接口,因为
这样不需要关心谁来分配内存谁来释放内存, 也不必担心数据溢出。高级函数和匿名函数均可以接受高级参数,使函数更为通用。
3.Lua的使用者
a.嵌入在应用程序,比如CGILua(搭建动态网页),LuaOrb(访问COBRA对象)。这些类型用Lua-API注册新函数,创建新类型。通过配置 Lua就可以
改变应用宿主语言的行为。
b.作为一种独立运行的语言,Lua 也是很有用的,主要用于文本处理或者只运
行一次的小程序。这种应用 Lua 主要使用它的标准库来实现,标准库提供模式匹配和其
它一些字串处理的功能。我们可以这样认为:Lua 是文本处理领域的嵌入式语言。
c.还有一些使用者使用其他语言开发,把 Lua 当作库使用。这些人大多使用 C语言开发,但使用 Lua建立简单灵活易于使用的接口。
如果你真得想学一门语言,参考手册是必备的。本书和 Lua 参考手册互为补充,手
册仅仅描述语言本身,因此他既不会告诉你语言的数据结构也不会举例说明,但手册是
Lua 的权威性文档,http://www.lua.org可以得到手册的内容。
-- Lua 用户社区,提供了一些第三方包和文档
http://lua-users.org
-- 本书的更新勘误表,代码和例子
http://www.inf.puc-rio.br/~roberto/book/
另外本书仅针对 Lua 5.0,如果你的版本不同,请查阅 Lua 手册或者比较版本间的差
异。
第一章
1.1Chunks
Chunk 是一系列语句,Lua 执行的每一块语句,比如一个文件或者交互模式下的每一行都是一个 Chunk。
每个语句结尾的分号(;)是可选的,但如果同一行有多个语句最好用;分开
一个 Chunk 可以是一个语句,也可以是一系列语句的组合,还可以是函数,Chunk
可以很大,在 Lua 中几个 MByte的Chunk 是很常见的。
你还可以以交互模式运行 Lua,不带参数运行 Lua:
你键入的每个命令(比如:"Hello World")在你键入回车之后立即被执行,键入文
件结束符可以退出交互模式(Ctrl-D in Unix, Ctrl-Z in DOS/Windows),或者调用 OS库
的 os.exit()函数也可以退出。
在交互模式下,Lua 通常把每一个行当作一个 Chunk,但如果 Lua 一行不是一个完
整的 Chunk时,他会等待继续输入直到得到一个完整的 Chunk.在 Lua 等待续行时,显示
不同的提示符(一般是>>).
可以通过指定参数让 Lua 执行一系列 Chunk。例如:假定一个文件 a 内有单个语句
x=1;另一个文件 b 有语句 print(x)
prompt> lua -la -lb
命令首先在一个 Chunk内先运行 a然后运行 b。(注意:-l 选项会调用 require,将会
在指定的目录下搜索文件,如果环境变量没有设好,上面的命令可能不能正确运行。
-i 选项要求 Lua 运行指定Chunk 后进入交互模式.
prompt> lua -i -la -lb
将在一个 Chunk 内先运行 a 然后运行 b,最后直接进入交互模式。
另一个连接外部 Chunk 的方式是使用 dofile 函数,dofile 函数加载文件并执行它.假
设有一个文件:
-- file 'lib1.lua'
function norm (x, y)
local n2 = x^2 + y^2
return math.sqrt(n2)
end
function twice (x)
return 2*x
end
在交互模式下:
> dofile("lib1.lua") -- load your library
> n = norm(3.4, 1.0)
> print(twice(n)) --> 7.0880180586677
-i 和 dofile 在调试或者测试 Lua 代码时是很方便的。
1.2全局变量
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初
始化的全局变量也不会出错,只不过得到的结果是:nil.
print(b); nil
如果你想删除一个全局变量,只需要将变量负值为 nil
b = nil
print(b) --> nil
这样变量 b 就好像从没被使用过一样.换句话说, 当且仅当一个变量不等于 nil 时,
这个变量存在。
1.3 词法约定
标示符:字母(letter)或者下划线开头的字母、下划线、数字序列.最好不要使用下划
线加大写字母的标示符,因为 Lua的保留字也是这样的。Lua 中,letter的含义是依赖于
本地环境的。
保留字:以下字符为 Lua 的保留字,不能当作标识符。
and break do else elseif
end false for function if
in local nil not or
repeat return then true until
while
注意:Lua 是大小写敏感的.
注释:单行注释:--
多行注释:--[[ --]]
--[[
print(10) -- no action (comment)
--]]
1.4 命令行方式
-e:直接将命令传入 Lua
prompt> lua -e "print(math.sin(12))" --> -0.53657291800043
-l:加载一个文件.
-i:进入交互模式.
_PROMPT 内置变量作为交互模式的提示符
prompt> lua -i -e "_PROMPT=' lua> '"
lua>
Lua 的运行过程,在运行参数之前,Lua 会查找环境变量 LUA_INIT 的值,如果变
量存在并且值为@filename,Lua 将加载指定文件。如果变量存在但不是以@开头,Lua
假定 filename 为 Lua 代码文件并且运行他。利用这个特性,我们可以通过配置,灵活的
设置交互模式的环境。可以加载包,修改提示符和路径,定义自己的函数,修改或者重
名名函数等。
全局变量 arg存放 Lua 的命令行参数。
prompt> lua script a b c
在运行以前,Lua 使用所有参数构造 arg 表。脚本名索引为 0,脚本的参数从 1 开始
增加。脚本前面的参数从-1 开始减少。
prompt> lua -e "sin=math.sin" script a b
arg 表如下:
arg[-3] = "lua"
arg[-2] = "-e"
arg[-1] = "sin=math.sin"
arg[0] = "script"
arg[1] = "a"
arg[2] = "b"
第二章 类型和值
Lua是动态类型语言,变量不要类型定义。 Lua中有8个基本类型分别为: nil、 boolean、
number、string、userdata、function、thread和 table。函数 type 可以测试给定变量或者值
的类型。
print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(X))) --> string
变量没有预定义的类型,每一个变量都可能包含任一种类型的值。
print(type(a)) --> nil ('a' is not initialized)
a = 10
print(type(a)) --> number
a = "a string!!"
print(type(a)) --> string
a = print -- yes, this is valid!
a(type(a)) --> function
注意上面最后两行,我们可以使用 function 像使用其他值一样使用(更多的介绍参
考第六章)。一般情况下同一变量代表不同类型的值会造成混乱,最好不要用,但是特殊
情况下可以带来便利,比如 nil。
2.1 Nil
Lua 中特殊的类型,他只有一个值:nil;一个全局变量没有被赋值以前默认值为 nil;
给全局变量负 nil 可以删除该变量。
2.2 Booleans
两个取值 false 和 true。但要注意 Lua中所有的值都可以作为条件。在控制结构的条
件中除了 false 和 nil 为假,其他值都为真。所以 Lua 认为0 和空串都是真。
2.3 Numbbers
表示实数,Lua 中没有整数。一般有个错误的看法 CPU运算浮点数比整数慢。事实
不是如此,用实数代替整数不会有什么误差(除非数字大于 100,000,000,000,000)。Lua
的 numbers 可以处理任何长整数不用担心误差。你也可以在编译 Lua的时候使用长整型
或者单精度浮点型代替 numbers,在一些平台硬件不支持浮点数的情况下这个特性是非
常有用的,具体的情况请参考 Lua发布版所附的详细说明。和其他语言类似,数字常量
的小数部分和指数部分都是可选的,数字常量的例子:
4 0.4 4.57e-3 0.3e12 5e+20
2 2.4 Strings
指字符的序列。lua是 8位字节,所以字符串可以包含任何数值字符,包括嵌入的 0。
这意味着你可以存储任意的二进制数据在一个字符串里。 Lua 中字符串是不可以修改的,
你可以创建一个新的变量存放你要的字符串,如下:
a = "one string"
b = string.gsub(a, "one", "another") -- change string parts
print(a) --> one string
print(b) --> another string
string 和其他对象一样,Lua 自动进行内存分配和释放,一个 string 可以只包含一个
字母也可以包含一本书,Lua 可以高效的处理长字符串,1M的 string 在 Lua 中是很常见
的。可以使用单引号或者双引号表示字符串
a = "a line"
b = 'another line'
为了风格统一,最好使用一种,除非两种引号嵌套情况。对于字符串中含有引号的
情况还可以使用转义符\来表示。Lua中的转义序列有:
\a bell
Copyright ? 2005, Translation Team, www.luac
\b back space -- 后退
\f form feed -- 换页
\n newline -- 换行
\r carriage return -- 回车
\t horizontal tab -- 制表
\v vertical tab
\\ backslash -- "\"
\" double quote -- 双引号
\' single quote -- 单引号
\[ left square bracket -- 左中括号
\] right square bracket -- 右中括号
例子:
> print("one line\nnext line\n\"in quotes\", 'in quotes'")
one line
next line
"in quotes", 'in quotes'
> print('a backslash inside quotes: \'\\\'')
a backslash inside quotes: '\'
> print("a simpler way: '\\'")
a simpler way: '\'
还可以在字符串中使用\ddd(ddd 为三位十进制数字)方式表示字母。
"alo\n123\""和'\97lo\10\04923"'是相同的。
还可以使用[[...]]表示字符串。这种形式的字符串可以包含多行也,可以嵌套且不会
解释转义序列,如果第一个字符是换行符会被自动忽略掉。这种形式的字符串用来包含
一段代码是非常方便的。
page = [[
<HTML>
<HEAD>
<TITLE>An HTML Page</TITLE>
</HEAD>
<BODY>
Lua
[[a text between double brackets]]
</BODY>
</HTML>
]]
io.write(page)
运行时,Lua 会自动在 string 和 numbers 之间自动进行类型转换,当一个字符串使
用算术操作符时,string 就会被转成数字。
print("10" + 1) --> 11
print("10 + 1") --> 10 + 1
print("-5.3e - 10" * "2") --> -1.06e-09
print("hello" + 1) -- ERROR (cannot convert "hello")
反过来,当 Lua 期望一个 string 而碰到数字时,会将数字转成 string。
print(10 .. 20) --> 1020
..在 Lua 中是字符串连接符,当在一个数字后面写..时,必须加上空格以防止被解释
错。
尽管字符串和数字可以自动转换,但两者是不同的,像 10 == "10"这样的比较永远
都是错的。如果需要显式将 string 转成数字可以使用函数 tonumber(),如果 string 不是正
确的数字该函数将返回 nil。
line = io.read() -- read a line
n = tonumber(line) -- try to convert it to a number
if n == nil then
error(line .. " is not a valid number")
else
print(n*2)
end
反之,可以调用 tostring()将数字转成字符串,这种转换一直有效:
print(tostring(10) == "10") --> true
print(10 .. "" == "10") --> true
2.5 Functions
函数是第一类值(和其他变量相同),意味着函数可以存储在变量中,可以作为函数
的参数,也可以作为函数的返回值。这个特性给了语言很大的灵活性:一个程序可以重
新定义函数增加新的功能或者为了避免运行不可靠代码创建安全运行环境而隐藏函数,
此外这特性在 Lua 实现面向对象中也起了重要作用(在第 16 章详细讲述)。
Lua 可以调用 lua 或者 C 实现的函数,Lua 所有标准库都是用 C 实现的。标准库包
括 string 库、table 库、I/O库、OS库、算术库、debug 库。
2.6 Userdata and Threads
userdata 可以将 C 数据存放在 Lua 变量中, userdata 在 Lua 中除了赋值和相等比较外
没有预定义的操作。userdata 用来描述应用程序或者使用 C 实现的库创建的新类型。例
如:用标准 I/O库来描述文件。下面在 C API章节中我们将详细讨论。
在第九章讨论协同操作的时候,我们介绍线程。
第 3 章 表达式
Lua 中的表达式包括数字常量、字符串常量、变量、一元和二元运算符、函数调用。
还可以是非传统的函数定义和表构造。
3.1 算数运算符
二元运算符:+ - * / ^ (加减乘除幂)
一元运算符:- (负值)
这些运算符的操作数都是实数。
3.2 关系运算符
< > <= >= == ~=
这些操作符返回结果为 false 或者true;==和~=比较两个值,如果两个值类型不同,
Lua 认为两者不同;nil 只和自己相等。Lua 通过引用比较 tables、userdata、functions。
也就是说当且仅当两者表示同一个对象时相等。
a = {}; a.x = 1; a.y = 0
b = {}; b.x = 1; b.y = 0
c = a
a==c but a~=b
Lua 比较数字按传统的数字大小进行,比较字符串按字母的顺序进行,但是字母顺
序依赖于本地环境。
当比较不同类型的值的时候要特别注意:
"0" == 0 -- false
2 < 15 -- true
"2" < "15" -- false (alphabetical order!)
为了避免不一致的结果,混合比较数字和字符串,Lua 会报错,比如:2 < "15"
3.3逻辑运算符
and or not
逻辑运算符认为 false 和nil 是假(false),其他为真,0 也是 true.
and 和 or的运算结果不是 true和 false,而是和它的两个操作数相关。
a and b -- 如果 a为false,则返回 a,否则返回 b
a or b -- 如果 a为true,则返回 a,否则返回 b
例如:
print(4 and 5) --> 5
print(nil and 13) --> nil
print(false and 13) --> false
print(4 or 5) --> 4
print(false or 5) --> 5
一个很实用的技巧:如果 x 为 false或者 nil 则给x 赋初始值v
x = x or v
等价于
if not x then
x = v
end
and 的优先级比 or高。
C 语言中的三元运算符
a ? b : c
在 Lua 中可以这样实现:
(a and b) or c
not 的结果一直返回 false或者 true
print(not nil) --> true
print(not false) --> true
print(not 0) --> false
print(not not nil) --> false
3.4连接运算符
.. --两个点
字符串连接,如果操作数为数字,Lua 将数字转成字符串。
print("Hello " .. "World") --> Hello World
print(0 .. 1) --> 01
3.5优先级
从高到低的顺序:
^
not - (unary)
* /
+ -
..
< > <= >= ~= ==
and
or
除了^和..外所有的二元运算符都是左连接的。
a+i < b/2+1 <--> (a+i) < ((b/2)+1)
5+x^2*8 <--> 5+((x^2)*8)
a < y and y <= z <--> (a < y) and (y <= z)
-x^2 <--> -(x^2)
x^y^z <--> x^(y^z)
3.6表的构造
构造器是创建和初始化表的表达式。表是 Lua特有的功能强大的东西。最简单的构
造函数是{},用来创建一个空表。可以直接初始化数组:
days = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
Lua 将"Sunday"初始化 days[1](第一个元素索引为 1) ,用"Monday"初始化 days[2].
print(days[4]) --> Wednesday
构造函数可以使用任何表达式初始化:
tab = {sin(1), sin(2), sin(3), sin(4),
sin(5),sin(6), sin(7), sin(8)}
如果想初始化一个表作为 record 使用可以这样:
a = {x=0, y=0} <--> a = {}; a.x=0; a.y=0
不管用何种方式创建 table,我们都可以向表中添加或者删除任何类型的域,构造函
数仅仅影响表的初始化。
w = {x=0, y=0, label="console"}
x = {sin(0), sin(1), sin(2)}
w[1] = "another field"
x.f = w
print(w["x"]) --> 0
print(w[1]) --> another field
print(x.f[1]) --> another field
w.x = nil -- remove field "x"
每次调用构造函数,Lua都会创建一个新的 table,可以使用 table 构造一个 list:
list = nil
for line in io.lines() do
list = {next=list, value=line}
end
这段代码从标准输入读进每行,然后反序形成链表。下面的代码打印链表的内容:
l = list
while l do
print (l.value)
l = l.next
end
在同一个构造函数中可以混合列表风格和 record 风格进行初始化,如:
polyline = {color="blue", thickness=2, npoints=4,
{x=0, y=0},
{x=-10, y=0},
{x=-10, y=1},
{x=0, y=1}
}
这个例子也表明我们可以嵌套构造函数来表示复杂的数据结构
print(polyline[2].x) --> -10
上面两种构造函数的初始化方式还有限制,比如你不能使用负索引初始化一个表中
元素,字符串索引也不能被恰当的表示。下面介绍一种更一般的初始化方式,我们用
[expression]显示的表示将被初始化的索引:
opnames = {["+"] = "add", ["-"] = "sub", ["*"] = "mul", ["/"] = "div"}
i = 20; s = "-"
a = {[i+0] = s, [i+1] = s .. s, [i+2] = s .. s .. s}
print(opnames[s]) -->sub
print(a[22]) --> ---
list 风格初始化和 record风格初始化是这种一般初始化的特例:
{x=0, y=0} <--> {["x"]=0, ["y"]=0}
{"red", "green", "blue"} <-->
{[1]="red", [2]="green", [3]="blue"}
如果真的想要数组下标从 0 开始:
days = {[0]="Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
注意:不推荐数组下标从 0 开始,否则很多标准库不能使用。
在构造函数的最后的","是可选的,可以方便以后的扩展。
a = {[1]="red", [2]="green", [3]="blue",}
在构造函数中域分隔符逗号(",")可以用分号(";")替代,通常我们使用分号用来
分割不同类型的表元素。
{x=10, y=45; "one", "two", "three"}