最近在学习写Kong的插件,作为新手,首先要学习的就是它所使用的编程语言lua,这个语言主要应用在nginx的开发中,Kong作为一个API网关系统,有时候需要写一些自定义的插件,所以会涉及到lua语言。这篇文章主要记录学习过程中的一些经验,希望对大家的学习有帮助。
Install 安装
学习的第一步,肯定是安装对应的开发环境。Lua的开发环境,在不同的操作系统上有不同的安装方法,比较简单的是在MAC上一条命令就搞定了。
brew install lua
Windows就下载对应的二进制安装包进行安装就好了。
Hello World
安装完环境后,我们来写一个hello world的程序,来测试一下环境是否正常。
新建一个helloworld.lua的文件:
print("Hello World")
在命令行中输入:
lua helloworld.lua
看到输出熟悉的hello world,就证明一切正常。
Comment注释
下来就是语言的基本语法,先学习一下注释的使用。lua语言当中采用的是–作为注释标示符,就像这样:
--- comment
变量声明
作为弱类型语言,在声明变量的时候,我们可以不必去指定它的数据类型。
来看个简单的例子:
name = "scq000"
io.write("Length of name", #name, "\n")
age = 4
io.write("My age is ", age, "\n")
使用io.write
函数直接输出对应的变量名等,其中#
作为运算符可以获取字符串的长度。
如果要生成一个长字符串,可以使用下面这种写法:
longString = [[
I am a very long long string that
dfsfdsfsdfs
]]
io.write(longString, "\n")
longString = longString .. name
-- 连接操作
io.write(longString, "\n")
数据类型的话,lua语言当中只支持浮点数:
bigNum = 9234324342342 + 1
io.write("Big Number" , type(bigNum), "\n)
floatPrecision = 1.99999999999999 + 0.0000000000000005
floatPrecision = 1.99999999999999 + 0.00000000000000055
获取变量类型信息
我们可以使用type函数来获取变量的基本类型:
isWrite = true
io.write(type(isWrite))
Math Function
和其他语言类似,在lua语言内部包含了很多内置的数学函数可以直接调用。
io.write("5 + 3 = ", "\n")
io.write("5 * 3 = ")
-- number
-- floor ceil max min, sin, cos, sqrt
math.floor(134.33223)
math.random()
math.random(10)
math.random(5, 100)
math.randomseed(os.time())
print(string.format("Pi = %.10f", math.pi))
关系运算符和逻辑运算符
关系运算符和其他语言大同小异,逻辑运算符采用的是单词形式 and or not,简单的例子如下:
-- Relational Operators: > < >= <= == ~=
age = 13
if age < 16 then
io.write("You can go to school", "\n")
local localVar = 10
elseif (age <= 16) and (age < 18) then
io.write("You can drive", "\n")
else
io.write("You can vote", "\n")
end
-- Logical Operators: and or not
toString(not true)
canVote = age > 18 ? true : false
canvote = age > 18 and true or false
lua语言中的代码分块采用end进行结尾,和其它语言中使用{}
进行分块不太一样。
字符串操作
以下是常用的字符串操作:
-- get length
string.len(quote)
-- or
#quote
-- replace string
string.gsub(quote, "I", "me")
-- find and get index
string.find(quote, "password")
-- upper /lower
string.upper(quote)
string.lower(quote)
循环,分支控制语句
lua中支持的循环语句类型比较多:
- while循环:
i = 1
while (i <= 10) do
io.write(i)
i = i + 1
if i == 8 then break end
end
- until循环: 和其它语言中do…until类似,这里采用的是repeat…until的语法。
repeat
io.write("enter your guess:")
guess = io.read()
until tonumber(guess) == 15
- for循环: 循环体的写法不需要用括号进行分隔,采用for…do的语法。
for i = 1, 10, 1 do
io.write(i)
end
months = {"Juanary", "February", "March", "April"}
for key, value in pairs(months) do
io.write(value, " ")
end
Table
在lua语言中,支持一种table的抽象数据结构,可以用来存储键值对对象。
aTable = {}
for i = 1, 10 do
aTable[i] = i
end
io.write("First : ", aTable[1])
io.write("Number of Items: ", #aTable, "\n")
table.insert(aTable, 1, 0)
io.write("First : ", aTable[1])
-- remove
table.remove(aTable, 1)
-- convert to string
print(table.concat(aTable, ", "))
二维表结构的定义与上面的例子差不多:
aMultiTable = {}
for i = 0, 9 do
aMultiTable[i] = {}
for j = 0, 9 do
aMultiTable[i][j] = tostring(i) .. tostring(j)
end
print()
end
io.write("Table[0][0] : ", aMultiTable[0][0], "\n")
functions
函数是进行语言抽象化必备的工具,所以在lua语言中,直接使用function来定义对应的函数就可以了:
function getSum(num1, num2)
return num1 + num2
end
print(string.format(" 2 + 6= %d", getSum(2,6)
下面这里例子演示了如何一个实现分割字符串功能的函数:
function splitStr(theString)
stringTable = {}
local i = 1
for str in string.gmatch(theString, "[^%s]+") do
stringTable[i] = str
i + i + 1
end
return stringTable, i
end
splitStrTable, numOfStr = splitStr("The Turtle")
for j = 1, numOfStr do
print(string.format("%d : %s", j splitStrTable[j]))
end
这里需要注意在函数内部声明局部变量要使用local关键字,以确定变量的作用域。
function的参数支持不定长参数,使用...
语法就可以了:
function getSumMore(...)
local sum = 0
for k, v in pairs{...} do
sum = sum + v
end
return sum
end
io.write("Sum ", getSomeMore(1,2,3,4,5,6), "\n")
闭包这个结构在函数式编程语言中算是比较常见的,内部函数可以访问它上层所定义的局部变量。
doubleIt = function(x) return x * 2 end
print(doubleIt(4))
--- 输出 8
function outerFunc()
local i = 0
return function()
i = i + 1
return i
end
end
getI = outerFunc()
print(getI())
-- 输出 1
coroutine 协程
协程是跟线程类似的概念,但在粒度上比线程要小,主要是在其自身上下文中进行切换。而且各个协程需要进行协作才能很好地工作。一个程序在同一时间只能运行一个协程,而且协程要显示挂起才能不强占资源。
主要的方法有create, resume, yield, running, status等,分别用来进行创建、恢复、传参、返回正在运行的协程和返回协程状态等操作。
co = coroutine.create(function()
for i = 1, 10, 1 do
print(i)
print(coroutine.status(co))
if i == 5 then coroutine.yield() end
end
end
)
print(coroutine.status(co))
coroutine.resume(co)
print(coroutine.status(co))
运行结果如下:
> print(coroutine.status(co))
suspended
>
> coroutine.resume(co)
1
running
2
running
3
running
4
running
5
running
true
>
> print(coroutine.status(co))
suspended
file文件操作
文件操作主要利用io这个接口中。常用的语法和c语言等都很类似,运行一个实例方法用采用:
这种语法:
-- r
-- w
-- a: Append
-- r+: read & write existing file
-- w+: overwrite read or create a file
-- a+: append read or create file
file = io.open("test.lua", "w+")
file:write("Random String of text\n")
file:write("Some more text\n")
file:seek("set", 0)
print(file:read("*a"))
file:close()
file = io.open("test.lua", "a+")
file:write("Even more text\n")
file:seek("set", 0)
print(file:read("*a))
file:close()
Modules
在进行大工程化开发,模块化是必须的,所以我们直接在一个文件中声明一个模块,并将这个模块导出:
新建一个covert.lua的文件:
-- covert.lua
local convert = {}
function convert.ftToCm(feet)
return feet + 30.48
end
return convert
在另一个入口文件index.lua中,使用require
语法进行引用:
convertModule = require("convert")
print(string.format("%.3f cm", convertModule.ftToCm(12)))
在命令行中运行lua index.lua后结果显示:
42.480 cm
MetaTable
lua中有一种数据类型是元表,和哈希表类似,通常可以把它看做是一个备用查找表。在Lua中在一个表中查找元素,如果没有找到,会去其关联的元表中继续进行查找。
使用setmetatable方法可以给一个表对象新增一个元表。我们还可以自己去定义元表的操作方法:
aTable = {}
for x = 1, 10 do
aTable[x] = x
end
-- _sub _mul _div _mod _concat
mt = {
__add = function(table1, table2)
sumTable = {}
for y = 1, #table1 do
if(table1[y] ~= nil) and (table2[y] ~= nil) then
sumTable[y] = table1[y] + table2[y]
else
sumTable[y] = 0
end
end
return sumTable
end,
__eq = function(table1 table2)
return table1.value = table2.value
end
}
setmetatable(aTable, mt)
print(aTable == aTable)
--- print true
Class OOP 面向对象
lua也支持面向对象的语言机制,在新建一个对象的时候,会调用其new
方法
Animal = {height = 0, weight = 0, name = "No Name", sound = "No Sound"}
function Animal:new (height, weight, name, sound)
setmetatable({}, Animal)
self.height = height
self.weight = weight
self.name = name
self.sound = sound
return self
end
function Animal:toString()
animalStr = string.format("%s weighs %.1f lbs, is %.1f in tall and says %s", self.name, self.height, self.weight, self.sound)
return animalStr
end
spot = Animal:new(10, 15, "Spot", "woof")
print(spot:toString())
--- print Spot weighs 10.0 lbs, is 15.0 in tall and says woof
总结
大多数的lua语言都和其它的现代编程语言大同小异,如果你已经有了其它的语言的基础,在学习的时候多对比,多总结,可以加速你掌握的速度。熟悉了大致的语言语法之后就可以投入实践中,多写一些demo,就能熟能生巧了。