另一方面,虽然 Lua 自身的库比较简单,但它可以方便地调用 C 库,大量成熟的 C 代码都可以为其所用。比如在 OpenResty 中,很多时候都需要你调用 NGINX 和 OpenSSL 的 C 函数,而这都得益于 Lua 和 LuaJIT 这种方便调用 C 库的能力。
Lua是什么?
1993 年在巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro in Brazil)诞生了一门编程语言,发明者是该校的三位研究人员,他们给这门语言取了个浪漫的名字——Lua,在葡萄牙语里代表美丽的月亮。事实证明她没有糟蹋这个优美的单词,Lua 语言正如它名字所预示的那样成长为一门简洁、优雅且富有乐趣的语言。
Lua 从一开始就是作为一门方便嵌入(其它应用程序)并可扩展的轻量级脚本语言来设计的,因此她一直遵从着简单、小巧、可移植、快速的原则,官方实现完全采用 ANSIC 编写,能以 C 程序库的形式嵌入到宿主程序中。正由于上述特点,所以 Lua 在游戏开发、机器人控制、分布式应用、图像处理、生物信息学等各种各样的领域中得到了越来越广泛的应用。其中尤以游戏开发为最,许多著名的游戏,比如 Escape from Monkey Island、World of Warcraft、大话西游,都采用了 Lua 来配合引擎完成数据描述、配置管理和逻辑控制等任务。即使像 Redis 这样中性的内存键值数据库也提供了内嵌用户 Lua 脚本的官方支持。
>local person ={name ="Tom", sex ="M"}-- do something> person =nil-- do something>print(person.name)
stdin:1: attempt to index global 'person'(a nil value)
stack traceback:
stdin:1:in main chunk
[C]: ?
>local person ={name ="Tom", sex ="M"}-- do something> person =nil-- do something>if person ~=niland person.name ~=nilthen>>print(person.name)>>else>>end>
对于简单类型的变量,我们可以用 if (var == nil) then 这样的简单句子来判断。但是对于 table 型的 Lua 对象,就不能这么简单判断它是否为空了。一个 table 型变量的值可能是 {},这时它不等于 nil。我们来看下面这段代码:
local next = next
local a ={}local b ={ name ="Bob", sex ="Male"}local c ={"Male","Female"}local d =nilprint(#a)print(#b)print(#c)--print(#d) -- errorif a ==nilthenprint("a == nil")endif b ==nilthenprint("b == nil")endif c ==nilthenprint("c == nil")endif d ==nilthenprint("d == nil")endifnext(a)==nilthenprint("next(a) == nil")endifnext(b)==nilthenprint("next(b) == nil")endifnext(c)==nilthenprint("next(c) == nil")end
local a =truelocal b =0local c =nilif a thenprint("a")elseprint("not a")endif b thenprint("b")elseprint("not b")endif c thenprint("c")elseprint("not c")end
执行结果:
a
b
not c
number(数字)
number 类型用于表示实数,和 C/C++ 里面的 double 类型很类似。可以使用数学函数 math.floor(向下取整)和 math.ceil(向上取整)进行取整操作。
local order =3.99local score =98.01print(math.floor(order))print(math.ceil(score))
执行结果:
3
99
一般地,Lua 的 number 类型就是用双精度浮点数来实现的。值得一提的是,LuaJIT 支持所谓的“dual-number”(双数)模式,即 LuaJIT 会根据上下文用整型来存储整数,而用双精度浮点数来存放浮点数。
local str1 ='hello world'local str2 ="hello lua"local str3 =[["add\name",'hello']]local str4 =[=[string have a [[]].]=]print(str1)-->output:hello worldprint(str2)-->output:hello luaprint(str3)-->output:"add\name",'hello'print(str4)-->output:string have a [[]].
localfunctionfoo()print("in the function")-- do somethinglocal x =10local y =20return x + y;end-- 把函数赋给变量local a = foo
print(a())--output:in the function30
Lua 中的 and 和 or 是不同于 c 语言的。在 c 语言中,and 和 or 只得到两个值 1 和 0,其中 1 表示真,0 表示假。而 Lua 中 and 的执行过程是这样的:
a and b 如果 a 为 nil,则返回 a,否则返回 b;
a or b 如果 a 为 nil,则返回 b,否则返回 a。
示例代码:
local c =nillocal d =0local e =100print(c and d)-->打印 nilprint(c and e)-->打印 nilprint(d and e)-->打印 100print(c or d)-->打印 0print(c or e)-->打印 100print(not c)-->打印 trueprint(not d)-->打印 false
local a, b =1,2local x, y =3,4local i =10local res =0
res = a + i < b /2+1-->等价于res = (a + i) < ((b/2) + 1)
res =5+ x ^2*8-->等价于res = 5 + ((x^2) * 8)
res = a < y and y <= x -->等价于res = (a < y) and (y <= x)
x =10if x >0thenprint("x is a positive number")end
两个分支if…else
x =10if x >0thenprint("x is a positive number")elseprint("x is a non-positive number")end
多分支if…elseif…else
score =90if score ==100thenprint("Very good!Your score is 100")elseif score >=60thenprint("Congratulations, you have passed it,your score greater or equal to 60")--此处可以添加多个elseifelseprint("Sorry, you do not pass the exam! ")end
与 C 语言的不同之处是 else 与 if 是连在一起的,若将 else 与 if 写成 “else if” 则相当于在 else 里嵌套另一个 if 语句,如下代码:
score =0if score ==100thenprint("Very good!Your score is 100")elseif score >=60thenprint("Congratulations, you have passed it,your score greater or equal to 60")elseif score >0thenprint("Your score is better than 0")elseprint("My God, your score turned out to be 0")end--与上一示例代码不同的是,此处要添加一个endend
while
Lua 跟其他常见语言一样,提供了 while 控制结构,语法上也没有什么特别的。但是没有提供 do-while 型的控制结构,但是提供了功能相当的 repeat。
while 型控制结构语法如下,当表达式值为假(即 false 或 nil)时结束循环。也可以使用 break 语言提前跳出循环。
while 表达式 do--bodyend
示例代码:
x =1
sum =0while x <=5do
sum = sum + x
x = x +1endprint(sum)-->output 15
Lua 提供了一组传统的、小巧的控制结构,包括用于条件判断的 if 用于迭代的 while、repeat 和 for。
for 语句有两种形式:数字 for(numeric for)和范型 for(generic for)。
for数字型
语法:
for var = begin, finish, step do--bodyend
关于数字 for 需要关注以下几点:
var 从 begin 变化到 finish,每次变化都以 step 作为步长递增 var;
begin、finish、step 三个表达式只会在循环开始时执行一次;
第三个表达式 step 是可选的,默认为 1;
控制变量 var 的作用域仅在 for 循环内,需要在外面控制,则需将值赋给一个新的变量;
循环过程中不要改变控制变量的值,那样会带来不可预知的影响。
示例代码:
for i =1,5doprint(i)end-- output:12345
for i =1,10,2doprint(i)end-- output:13579
以下是这种循环的一个典型示例:
for i =10,1,-1doprint(i)end-- output:10987654321
如果不想给循环设置上限的话,可以使用常量 math.huge:
for i =1, math.huge doif(0.3* i ^3-20* i ^2-500>=0)thenprint(i)breakendend
for泛型
泛型 for 循环通过一个迭代器(iterator)函数来遍历所有值
-- 打印数组a的所有值local a ={"a","b","c","d"}for i, v inipairs(a)doprint("index:", i," value:", v)end-- output:
index:1 value: a
index:2 value: b
index:3 value: c
index:4 value: d
Lua 的基础库提供了 ipairs,这是一个用于遍历数组的迭代器函数。在每次循环中,i 会被赋予一个索引值,同时 v 被赋予一个对应于该索引的数组元素值。
下面是另一个类似的示例,演示了如何遍历一个 table 中所有的 key
-- 打印table t中所有的keyfor k inpairs(t)doprint(k)end
从外观上看泛型 for 比较简单,但其实它是非常强大的。通过不同的迭代器,几乎可以遍历所有的东西, 而且写出的代码极具可读性。标准库提供了几种迭代器,包括用于迭代文件中每行的(io.lines)、 迭代 table 元素的(pairs)、迭代数组元素的(ipairs)、迭代字符串中单词的(string.gmatch)等。
泛型 for 循环与数字型 for 循环有两个相同点: (1)循环变量是循环体的局部变量; (2)决不应该对循环变量作任何赋值。
对于泛型 for 的使用,再来看一个更具体的示例。假设有这样一个 table,它的内容是一周中每天的名称:
local days ={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
语句 break 用来终止 while、repeat 和 for 三种循环的执行,并跳出当前循环体, 继续执行当前循环之后的语句。下面举一个 while 循环中的 break 的例子来说明:
-- 计算最小的x,使从1到x的所有数相加和大于100
sum =0
i =1whiletruedo
sum = sum + i
if sum >100thenbreakend
i = i +1endprint("The result is ".. i)-->output:The result is 14
localfunctionadd(x, y)return x + y
--print("add: I will return the result " .. (x + y))--因为前面有个return,若不注释该语句,则会报错endlocalfunctionis_positive(x)if x >0thenreturn x .." is positive"elsereturn x .." is non-positive"end--由于return只出现在前面显式的语句块,所以此语句不注释也不会报错--,但是不会被执行,此处不会产生输出print("function end!")endlocal sum =add(10,20)print("The sum is ".. sum)-->output:The sum is 30local answer =is_positive(-10)print(answer)-->output:-10 is non-positive
有时候,为了调试方便,我们可以想在某个函数的中间提前 return,以进行控制流的短路。此时我们可以将 return 放在一个 do ... end 代码块中,例如:
local sum =add(10,20)print("The sum is ".. sum)-->output:The sum is 30local answer =is_positive(-10)print(answer)-->output:-10 is non-positivefor i =1,3doif i <=2thenprint(i,"yes continue")goto continue
endprint(i," no continue"):: continue ::print([[i'm end]])end
输出结果:
1 yescontinue
i'm end
2 yes continue
i'm end
3 no continue
i'm end