OpenResty最佳实践

ppppl 2015-12-08

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的全局变量。

这里有疑问?

相关推荐