使用命令行参数-i来启动lua解释器,那么解释器就会在运行完指定的程序块后进入交互模式。

1
2
3
4
5
test.lua
function ( ... )

print("foo")
end
1
2
3
4
lua -i test.lua
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
foo()
foo

另一种运行程序块的方式是使用函数dofile,该函数会立即执行一个文件。

行注释

1
-- here

块注释

1
2
3
4
--[[
here
here
--]]

类型与值

在lua中有8种基础类型:nil,boolean,number,string,userdata,function,thread,table

函数type可根据一个值返回其类型名称:

1
print(type(type)) -- function

lua将值false和nil视为“假”,而除此之外的其他值视为“真”。

lua提供运行时的数字与字符串的自动转换。在一个字符串上应用算数操作时,lua会尝试将这个字符串转换成一个数字:

1
print("10" + 1) -- 11

lua不仅在算数操作中会使用这种强制转换,还会在其他任何需要数字的地方这么做,反之亦然:

1
print(10 .. 20) -- 1020

a.x和a[“x”]等价

关系操作符

操作符==和~=用来做相等测试,如果两个值具有不同的类型,lua就认为它们不相等。否则,lua会根据它们的类型来比较两者。对于table,userdata和函数,lua是作引用比较。

只能对两个数字或两个字符串作大小性比较。lua是按字母次序来比较字符串的,具体的字母次序取决于对lua的区域设置。

lua操作符优先级(从高到低):

1
2
3
4
5
6
7
8
^
not # -
* / %
+ -
..
< > <= >= ~= ==
and
or

在二元操作符中,除了指数操作符^和连接操作符..是右结合,所有其他操作符都是左结合。

1
2
a + i < b / 2 + 1	-- 		(a + i ) < ((b / 2) + 1)
x ^ y ^ z -- x ^ (y ^ z)

语句

lua允许多重赋值

1
a, b = 10, 2

尽可能地使用局部变量是一种良好的编程风格,局部变量可以避免将一些无用的名称引入全局环境,避免扰乱了全局环境。此外,访问局部变量比访问全局变量更快,。最后一个局部变量通常会随着其作用域的结束而消失,这样便使垃圾收集器可以释放其值。

控制结构:

if-else-then:

1
2
3
4
5
6
7
if cond then
-- do
elseif cond1 then
-- do1
else
-- rest
end

while:

1
2
3
while cond do
-- do
end

repeat:

1
2
3
repeat
-- do
until cond

对于lua的repeat语句来说,一个声明在循环体中的局部变量的作用域包括了条件测试。

函数

调用函数无论哪种方法都需要将所有参数放到一对圆括号中。即使调用函数时没有参数,也必须写出一对空括号。对于此规则只有一种特殊的例外情况:一个函数若只有一个参数,并且此参数是一个字面字符串或者table构造式,那么圆括号便是可有可无的:

1
2
print "hello world"
f{x = 10, y = 20}

lua函数可以返回多个值,如果一个函数没有返回值或者没有足够多的返回值,那么lua会用nil来补充缺失的值:

1
2
3
4
5
6
function ()
return 1, 2
end

local a, b, c = foo()
print("a = " .. a .. " b = " .. b .. " c = " .. type(c)) -- a = 1 b = 2 c = nil

unpack函数接受一个数组作为参数,并从下标1开始返回该数组的所有元素:

1
print(unpack{10, 20, 30}) -- 10 20 30

参数表中使用3个点(…)表示该函数可接受不同数量的实参,表达式“…”的行为类似于一个具有多重返回值的函数,它返回的是当前函数的所有变长参数。

在lua中,函数是第一类值,它们具有特定的词法域。“第一类值”表示在lua中函数与其它传统类型的值具有相同的权利。函数可以存储到变量或者table中,可以作为实参传递给其他函数,还可以作为其它函数的返回值。“词法域”是指一个函数可以嵌套在另外一个函数中,内部的函数可以访问外部函数中的变量。

一个closure就是一个函数加上该函数所需访问的所有“非局部的变量”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function newCounter()
local i = 0
return
function()
i = i + 1
return i
end
end

c1 = newCounter()
c2 = newCounter()
print(c1()) -- 1
print(c1()) -- 2
print(c2()) -- 1

上面这段代码中,匿名函数访问了一个“非局部变量”i,如果再次调用newCounter,那么它会创建一个新的局部变量i,从而也将得到一个新的closure。

可以使用同样的技术创建一个安全的运行环境,即所谓的“沙盒(sandbox)”。当执行一些未受信任的代码时就需要一个安全的执行环境,例如在服务器中执行那些从internet上接收到的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
do
local oldOpen = io.open
local access_ok = function (filename, mode)
--检查访问权限
end

io.open = function (filename, mode)
if access_ok(filename, mode) then
return oldOpen(filename, mode)
else
return nil, "access denied"
end
end
end

经过重新定义后,一个程序就只能通过新的受限版本来调用原来那个未受限的open函数了。

表和面向对象

创建表

1
t = {}

设置和访问表内容,不存在的表内容返回nil

1
2
t["key] = 1
local value = t["key"]