第一章 开始
按照惯例,我们的第一个Lua程序同样是显示"Hello World":
print("Hello World")
如果使用的是Lua解释器程序,那么仅需要将包含这段程序代码的文本文件作为解释器的启动参数传给它。解释器的名字通常为"lua"或者"lua5.2"。
假如下面的这段代码保存在名为"hello.lua"的文件中,那么可以使用下面的命令运行它:
% lua hello.lua
下面的例子稍显复杂,它定义了一个函数,用于计算给定值得阶乘,用户输入一个数字,然后显示出
计算的结果:
-- 定义一个计算阶乘的函数
function fact (n)
if n == 0 then
return 1
else
return n * fact(n-1)
end
end
print("enter a number:")
a = io.read("*n") -- 从用户输入中读取一个数字
print(fact(a))
1 程序块
Lua执行的每"段"代码,比如一个文件或者在交互模式下的一行,都被叫做一个"程序块"。一个程序块往往是一系列的命令(或者语句)。
Lua在连续的语句之间不需要使用分隔符(译者注:需要空格),但只要愿意,可以使用分号。作为个人的观点,当一行中包含多个语句的时候,最好用分号将他们分隔开。Lua并没有对换行符做任何规定,举个例子,下面的四个程序块均合法且等价:
a=1
b=a*2
-- 等价于
a=1;
b=a*2;
-- 等价于
a=1;b=a*2
-- 等价于
a=1 b=a*2
一个程序块可以简单到只有一行语句,就像"Hello World"那个例子;也可以由多个语句和函数定义组成(稍后我们会知道,这实际上是任务),就像阶乘的那个例子。一个程序块可以任意大小,并且因为Lua往往被作为数据描述型语言使用,一个上兆(M)的程序块并不罕见。Lua解释器可以完美处理大的程序块。
除了把代码写到文件里,也可以单独运行解释器,从而使用交互模式。如果不提供任何参数去调用
lua,那么将会看到如下提示:
% lua
Lua 5.2 Copyright (C) 1994-2012 Lua.org, PUC-Rio
>
之后,输入的每个命令(比如 print("Hello World") )都会立即执行。想退出交互模式和解释器,只需要输入end-of-file控制字符(Uninx中为Ctrl-D,Windows中为Ctrl-Z),或者调用系统库函数中的退出函数——输入os.exit()。
在交互模式中,Lua通常把输入的每一行都作为一个完整的程序块来解释,但如果它检测到该行内容无法构成一个完整的程序块,它会等待更多的输入,直到拥有一个完整的程序块。这种机制使我们可以直接在交互模式中输入包含多行的定义,比如上面的阶乘函数。尽管如此,将这样的定义放到文件中然后调用Lua去执行依旧是更加方便快捷的办法。
可以使用-i选项来启动Lua,这样可以让它在执行指定的程序块后进入交互模式:
% lua -i prog
该命令将首先执行prog文件中定义的程序块,然后进入交互模式的提示。-i选项在调试和人工测试的时候相当有用。在这一节的最后,还会看到独立交互模式下的其它选项。
另一个执行程序块的方法是调用dofile函数,该函数用于立即执行一个文件。举个例子,假设有一个
文件"lib1.lua",里面的内容是:
function norm (x, y)
return (x^2 + y^2)^0.5
end
function twice (x)
return 2*x
end
那么,可以在交互模式中输入下面的内容来调用它:
> dofile("lib1.lua") -- 加载库
> n = norm(3.4, 1.0)
> print(twice(n)) --> 7.0880180586677
"dofile"函数在调试某段代码的时候很有用,例如,可以同时打开两个窗口:一个是文本编辑器,用来编辑代码(比如编辑上面的lib1.lua文件),另一个是运行Lua交互模式的控制台。每当代码更改并保存
之后,都可以执行dofile("lib1.lua")来重新加载新的代码,这样就可以调用其中的函数并打印结果了。
2 语法规范
Lua中的标识符可以是由任何字母、数字、下划线组成的字符串,但不能以数字开头,例如这些合法的命名:
i j i10 _ij aSomewhatLongName _INPUT
要避免使用下划线后面跟随一个或多个大写字母的命名(比如 VERSION),在Lua中,它们被作为特殊用途。通常保留标识符""(一个孤立的下划线)作为虚拟变量。
在老版本的Lua中,"字母"的概念取决于系统的区域设置(译者注:比如,德文环境中可以使用β),
但如果使用了依赖区域的标识符,就会导致在其它不支持该环境的系统中出现兼容性问题。Lua5.2之后将可用于标识符的"字母"定义限制在A-Z和a-z。
下面列出的是保留字,不能用作标识符:
and break do else
elseif
end false goto for
function
if in local nil
not
or repeat return then true
until while
Lua是大小写敏感的:"and"是保留字,但"AND"和"And"这两个却可以作为不同的标识符来使用。
双连字符(--)在任何地方都作为行注释的起始,有效范围至行尾。Lua同样提供块注释,以"--[["开始,遇到"]]"结束。一个常用的小技巧是,将要注释的代码放到"--[["和"--]]"之间,就像这样:
--[[
print(10) -- 无效代码 (因为是注释)
--]]
想要让这段代码起作用,可以在第一行前再加一个连字符
---[[
print(10) --> 输出10
--]]
在第一个例子中,第一行的"--[["意味着块注释的起始,第三行的"--"依旧在注释中;
在第二个例子中,第一行的"--"意味着行注释的起始,因此,第一行和第三行就成了独立的行注释,这样,"print"就会在注释之外。
实际的块注释要复杂的多。
3 全局变量
全局变量不需要声明,可以直接使用。访问一个没有初始化的变量没有问题,只不过结果会得到"nil"。
print(b) --> nil
b = 10
print(b) --> 10
如果将一个全局变量值赋为"nil",之后的操作中,Lua就会认为这个变量从没有被使用过,下面的代码中,Lua会将"b"之前用过的内存进行回收:
b = nil
print(b) --> nil
4 解释器程序
解释器程序(根据其源码名字,也被叫做lua.c;或者根据它的可执行文件名,简单的称为lua)是一个允许直接使用Lua的小程序。
这一节将介绍它的主要选项。
解释器在加载一个文件的时候,会忽略掉其以井号("#")开头的第一行。在UNIX系统这个,这个特点允许将Lua作为该脚本的解释器。如果脚本文件以类似这样的内容开头(假设脚本解释器位于路径
/usr/local/bin 中):
#!/usr/local/bin/lua
或者这样:
#!/usr/bin/env lua
那么,就可以直接调用脚本,而不需要显式调用Lua解释器。
Lua的用法如下:
lua [options] [script [args]]
所有的选项都是可选的,正如前面使用过的那样,可以不设置任何参数启动解释器,这将会直接进入
交互模式。
-e 选项允许直接运行命令行中输入的代码,比如这样(UNIX需要将内容放到双引号中,避免shell对括号的错误解释):
% lua -e "print(math.sin(12))" --> -0.5365729180004
-l 选项用于加载一个库。前面提到,-i 选项在其它选项生效之后,会进入交互模式。因此,下面的调用会首先加载库,然后执行赋值操作"x=10",最后显示交互模式提示符:
% lua -i -llib -e "x = 10"
交互模式中,在语句前加等号("=")会立即将语句的结果打印出来:
> = math.sin(3) --> 0.14112000805987
> a = 30
> = a --> 30
这个特点使得Lua可以被用作计算器。
在所有的启动参数生效之前,终端会查找一个名为"LUA_INIT_5_2"的环境变量;如果没有定义,则查找"LUA_INIT"。
如果找到其中任一变量并且它值的内容是"@filename",解释器就会首先行"filename"所指定的文件;如果找到了任一变量但值并不是以"@"作为起始,解释器就会认为这个环境变量的值是Lua代码,并执行其中的内容。
配置Lua解释程序的时候,"LUA_INIT"变量能够提供强大的助力,它可以完全控制Lua解释器运行时的各个方面:可以预加载包,更改路径,定义自己的函数,重命名或者删除函数,等等。
被Lua解释器运行的脚本可以从预定义的一个全局变量"arg"中获取它的运行参数,比如像这样一个调用:%lua script a b c,在脚本运行前,解释器会创建一个包含所有命令行参数的表,脚本自身的名字放在表中索引为0的位置,第一个参数放在索引为1的位置(这个例子中是"a"),以此类推。脚本调用之前的Lua解释器参数会放到索引为负数的位置,对应于他们在命令中出现的位置。举个例子,就像下面的这种
调用:
% 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"
通常,脚本只会使用正数索引(比如例子中的arg[1]和arg[2])。
Lua5.1之后,脚本可以从形如变长参数的表达中获取它的运行参数。在脚本的主体代码中,"..."(三个点)的表达方式代表它的所有运行参数。(关于变长参数表达的详细内容会在5.2节讨论)
练习
练习1.1:运行阶乘的例子,试一下如果输入一个负数会出现什么情况?尝试修改例子来规避这个问题。
练习1.2 :运行"twice"的例子,试一下用"-l"和"dofile"的方式加载"lib1.lua",你更倾向于哪种方式?
练习1.3:想想还有那种语言是使用"--"作为注释标志?
练习1.4:下面的哪个字符串可以用作标识符?
--- _end End end until? nil NULL
练习1.5:写一个简单的脚本,可以在运行时打印它自身的名字,即使在脚本名发生变化时也不需要修改代码。