OpenResty最佳实践
OpenResty最佳实践
https://moonbingbing.gitbooks.io/openresty-best-practices/content/lua/brief.html
1、Lua入门
1.1Lua简介
Lua在游戏开发、机器人控制、分布式应用、图像处理、生物信息学等各种各样的领域中得到了越来越广泛的应用。
Lua有着如下的特性:
变量名没有类型,值才有类型,变量名在运行时可与任何类型的值绑定;
语言只提供唯一一种数据结构,称为表(table),它混合了数组、哈希,可以用任何类型的值作为key和value。提供了一致且富有表达力的表构造语法,使得Lua很适合描述复杂的数据;
函数是一等类型,支持匿名函数和正则尾递归(propertailrecursion);
支持词法定界(lexicalscoping)和闭包(closure);
提供thread类型和结构化的协程(coroutine)机制,在此基础上可方便实现协作式多任务;
运行期能编译字符串形式的程序文本并载入虚拟机执行;
通过元表(metatable)和元方法(metamethod)提供动态元机制(dynamicmeta-mechanism),从而允许程序运行时根据需要改变或扩充语法设施的内定语义;
能方便地利用表和动态元机制实现基于原型(prototype-based)的面向对象模型;
从5.1版开始提供了完善的模块机制,从而更好地支持开发大型的应用程序;
Lua和LuaJIT的区别
LuaJIT就是一个为了再榨出一点速度的尝试,它利用JIT编译技术把Lua代码编译成本地机器码后交由CPU直接执行。LuaJIT测评报告表明,在浮点运算、循环和协程的切换等方面它的加速效果比较显著,但如果程序大量依赖C编写的函数,那么运行速度便不会有什么改进。目前LuaJIT只支持X86CPU。
LuaJIT和Lua的一个区别是,LuaJIT的运行速度比标准Lua快数十倍,可以说是一个Lua的高效率版本。
HelloWorld程序
function main() print("Hello World") end main()
luajit./HelloWorld.lua运行这个HelloWorld.lua程序,输出如下结果:
HelloWorld
Lua基础数据类型
函数type能够返回一个值或一个变量所属的类型。
print(type("hello world")) -->output:string print(type(print)) -->output:function print(type(true)) -->output:boolean print(type(360.0)) -->output:number print(type(nil)) -->output:nil
nil(空)
nil是一种类型,Lua将nil用于表示“无效值”。一个变量在第一次赋值前的默认值是nil,将nil赋予给一个全局变量就等同于删除它
local num print(num) -->output:nil num = 100 print(num) -->output:100
boolean(布尔)
布尔类型,可选值true/false;Lua中nil和false为“假”,其它所有值均为“真”。
local a = true local b = 0 local c = nil if a then print("a") -->output:a else print("not a") --这个没有执行 end if b then print("b") -->output:b else print("not b") --这个没有执行 end if c then print("c") --这个没有执行 else print("not c") -->output:not c end
number(数字)
number类型用于表示实数,和c/c++里面的double类型一样。可以使用数学函数math.floor(向下取整)和math.ceil(向上取整)进行取整操作。
local order = 3.0 local score = 98.5 print(math.floor(order)) -->output:3 print(math.ceil(score)) -->output:99
string(字符串)
Lua中有三种方式表示字符串:
1、使用一对匹配的单引号。例:'hello'。
2、使用一对匹配的双引号。例:"abclua"。
3、字符串还可以用一种长括号(即[[]])括起来的方式定义。
例:[[abc\nbc]],里面的"\n"不会被转义。
Lua的字符串是不可改变的值,不能像在c语言中那样直接修改字符串的某个字符,而是根据修改要求来创建一个新的字符串。Lua也不能通过下标来访问字符串的某个字符。
local str1 = 'hello world' local str2 = "hello lua" local str3 = [["add\name",'hello']] local str4 = [=[string have a [[]].]=] print(str1) -->output:hello world print(str2) -->output:hello lua print(str3) -->output:"add\name",'hello' print(str4) -->output:string have a [[]].
table(表)
table类型实现了“关联数组”。“关联数组”是一种具有特殊索引方式的数组,索引可为字符串string或(整)数number类型。
local corp = { web = "www.google.com", --索引为字符串,key = "web", value = "www.google.com" telephone = "12345678", --索引为字符串 staff = {"Jack", "Scott", "Gary"}, --索引为字符串,值也是一个表 100876, --相当于 [1] = 100876,此时索引为数字,key = 1, value = 100876 100191, --相当于 [2] = 100191,此时索引为数字 [10] = 360, --直接把数字索引给出 ["city"] = "Beijing" --索引为字符串 } print(corp.web) -->output:www.google.com print(corp["telephone"]) -->output:12345678 print(corp[2]) -->output:100191 print(corp["city"]) -->output:"Beijing" print(corp.staff[1]) -->output:Jack print(corp[10]) -->output:360
function(函数)
函数也是一种数据类型,函数可以存储在变量中,可以通过参数传递给其他函数,还可以作为其他函数的返回值。
function foo() print("in the function") --dosomething() local x = 10 local y = 20 return x + y end local a = foo --把函数赋给变量 print(a()) --output: in the function 30
Lua语言中不等于运算符的写法为:~=
aandb如果a为nil,则返回a,否则返回b;
aorb如果a为nil,则返回b,否则返回a。
local c = nil local d = 0 local e = 100 print(c and d) -->打印 nil print(c and e) -->打印 nil print(d and e) -->打印 100 print(c or d) -->打印 0 print(c or e) -->打印 100 print(not c) -->打印 true print(not d) -->打印 false
字符串连接
在Lua中连接两个字符串,可以使用操作符“..”(两个点)。如果其任意一个操作数是数字的话,Lua会将这个数字转换成字符串。注意,连接操作符只会创建一个新字符串,而不会改变原操作数。也可以使用string库函数string.format连接字符串。
print("Hello " .. "World") -->打印 Hello World print(0 .. 1) -->打印 01 str1 = string.format("%s-%s","hello","world") print(str1) -->打印 hello-world str2 = string.format("%d-%s-%.2f",123,"world",1.21) print(str2) -->打印 123-world-1.21 res = 5 + x^2*8 -->等价于res = 5 + ((x^2) * 8)
1.5控制结构
Lua语言提供的控制结构有if,while,repeat,for,并提供break关键字来满足更丰富的需求。
控制结构:if-else
score = 90 if score == 100 then print("Very good!Your score is 100") elseif score >= 60 then print("Congratulations, you have passed it,your score greater or equal to 60") --此处可以添加多个elseif else print("Sorry, you do not pass the exam! ") end
repeat控制结构
简单点说,执行repeat循环体后,直到until的条件为真时才结束
x = 10 repeat print(x) until false
控制结构:for
for语句有两种形式:数字for(numericfor)和范型for(genericfor)。
数字for(numeric for) for var = begin, finish, step do --body end var从begin变化到finish,每次变化都以step作为步长递增var,并执行一次“执行体”。第三个表达式step是可选的,若不指定的话,Lua会将步长默认为1。 for i=1,5 do print(i) end
如果不想给循环设置上限的话,可以使用常量math.huge:
for i=1, math.huge do if (0.3*i^3 - 20*i^2 - 500 >=0) then print(i) break end end
泛型for循环通过一个迭代器(iterator)函数来遍历所有值:
local a = {"a", "b", "c", "d"} for i, v in ipairs(a) do print("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被赋予一个对应于该索引的数组元素值。
for k in pairs(t) do print(k) end
标准库提供了几种迭代器,包括用于迭代文件中每行的(io.lines)、迭代table元素的(pairs)、迭代数组元素的(ipairs)、迭代字符串中单词的(string.gmatch)等。
泛型for循环与数字型for循环有两个相同点:(1)循环变量是循环体的局部变量;(2)决不应该对循环变量作任何赋值。
break,return关键字
return只能写在语句块的最后,一旦执行了return语句,该语句之后的所有语句都不会再执行。若要写在函数中间,则只能写在一个显式的语句块内,
1.6Lua函数
函数既可以完成某项特定的任务,也可以只做一些计算并返回结果。在第一种情况中,一句函数调用被视为一条语句;而在第二种情况中,则将其视为一句表达式。
函数定义
Lua使用关键字function定义函数,语法如下:
function function_name (arc) --arc表示参数列表,函数的参数列表可以为空 --body end function max(a, b) --定义函数max,用来求两个数的最大值,并返回 local temp = nil --使用局部变量temp,保存最大值 if(a > b) then temp = a else temp = b end return temp --返回最大值 end local m = max(-12, 20) --调用函数max,找去-12和20中的最大值 print(m) -->output 20 func() --函数调用,圆扩号不能省
按值传递
Lua函数的参数大部分是按值传递的。值传递就是调用函数时,实参把它的值通过赋值运算传递给形参,然后形参的改变和实参就没有关系了。在这个过程中,实参是通过它在参数表中的位置与形参匹配起来的。
function swap(a, b) --定义函数swap,函数内部进行交换两个变量的值 local temp = a a = b b = temp print(a, b) end local x = "hello" local y = 20 print(x, y) swap(x, y) --调用swap函数 print(x, y) --调用swap函数后,x和y的值并没有交换 -->output hello 20 20 hello hello 20
在调用函数的时候,若形参个数和实参个数不同时,Lua会自动调整实参个数。调整规则:若实参个数大于形参个数,从左向右,多余的实参被忽略;若实参个数小于形参个数,从左向右,没有被实参初始化的形参会被初始化为nil。
变长参数
上面函数的参数都是固定的,其实Lua还支持变长参数。若形参为...,表示该函数可以接收不同长度的参数。访问参数的时候也要使用...。
function func(...) --形参为 ... ,表示函数采用变长参数 local temp = {...} --访问的时候也要使用 ... local ans = table.concat(temp, " ") --使用table.concat库函数,对数组内容使用" "拼接成字符串。 print(ans) end func(1, 2) --传递了两个参数 func(1, 2, 3, 4) --传递了四个参数 -->output 1 2 1 2 3 4
具名参数
Lua还支持通过名称来指定实参,这时候要把所有的实参组织到一个table中,并将这个table作为唯一的实参传给函数。
当函数参数是table类型时,传递进来的是table在内存中的地址,这时在函数内部对table所做的修改,不需要使用return返回,就是有效的。
function change(arg) --change函数,改变长方形的长和宽,使其各增长一倍 arg.width = arg.width * 2 --表arg不是表rectangle的拷贝,他们是同一个表 arg.height = arg.height * 2 end --没有return语句了 local rectangle = { width = 20, height = 15 } print("before change:", "width =", rectangle.width, "height =", rectangle.height) change(rectangle) print("after change:", "width =", rectangle.width, "height =", rectangle.height) -->output before change: width = 20 height = 15 after change: width = 40 height = 30
函数的返回值
Lua具有一项与众不同的特性,允许函数返回多个值。Lua的库函数中,有一些就是返回多个值。
local s, e = string.find("hello world", "llo") print(s, e) -->output 3 5 返回多个值时,值之间用“,”隔开。
当函数返回值的个数和接收返回值的变量的个数不一致时,Lua也会自动调整参数个数。调整规则:若返回值个数大于接收变量的个数,多余的返回值会被忽略掉;若返回值个数小于参数个数,从左向右,没有被返回值初始化的变量会被初始化为nil。
当一个函数有一个以上返回值,且函数调用不是一系列表达式的最后一个元素,那么函数调用只会产生一个返回值,也就是第一个返回值。
1.7模块
使用require和module来定义和使用模块和包。
require函数
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用require"file"就可以了,file指模块所在的文件名。这个调用会返回一个由模块函数组成的table,并且还会定义一个包含该table的全局变量。
这里有疑问?