JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

OpenResty实战-Lua入门-Lua函数 openresty的lua版本

wys521 2024-10-04 07:17:13 精选教程 28 ℃ 0 评论

在 Lua 中,函数是一种对语句和表达式进行抽象的主要机制。函数既可以完成某项特定的任务,也可以只做一些计算并返回结果。

在第一种情况中,一句函数调用被视为一条语句;而在第二种情况中,则将其视为一句表达式。

示例代码:

print("hello world!") -- 用 print() 函数输出 hello world!

local m = math.max(1, 5) -- 调用数学库函数 max,

-- 用来求 1,5 中的最大值,并返回赋给变量 m

使用函数的好处:

1. 降低程序的复杂性:把函数作为一个独立的模块,写完函数后,只关心它的功能,而不再考虑函数里面的细节。

2. 增加程序的可读性:当我们调用 math.max() 函数时,很明显函数是用于求最大值的,实现细节就不关心了。

3. 避免重复代码:当程序中有相同的代码部分时,可以把这部分写成一个函数,通过调用函数来实现这部分代码的功能,节约空间,减少代码长度。

4. 隐含局部变量:在函数中使用局部变量,变量的作用范围不会超出函数,这样它就不会给外界带来干扰。

函数定义

Lua 使用关键字 function 定义函数,语法如下:

function function_name (arc) -- arc 表示参数列表,函数的参数列表可以为空

-- body

end

上面的语法定义了一个全局函数,名为 function_name . 全局函数本质上就是函数类型的值赋给了一个全局变量,即上面的语法等价于

function_name = function (arc)

-- body

end

由于全局变量一般会污染全局名字空间,同时也有性能损耗(即查询全局环境表的开销),因此我们应当尽量使用“局部函数”,其记法是类似的,只是开头加上 local 修饰符:

local function function_name (arc)

-- body

end

由于函数定义本质上就是变量赋值,而变量的定义总是应放置在变量使用之前,所以函数的定义也需要放置在函数调用之前。

示例代码:

local 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

如果参数列表为空,必须使用 () 表明是函数调用。

示例代码:

local function func() --形参为空

print("no parameter")

end

func() --函数调用,圆扩号不能省

--> output:

no parameter

在定义函数要注意几点:

  1. 利用名字来解释函数、变量的目的,使人通过名字就能看出来函数、变量的作用。
  2. 每个函数的长度要尽量控制在一个屏幕内,一眼可以看明白。
  3. 让代码自己说话,不需要注释最好。

由于函数定义等价于变量赋值,我们也可以把函数名替换为某个 Lua 表的某个字段,例如

function foo.bar(a, b, c)

-- body ...

end

此时我们是把一个函数类型的值赋给了 foo 表的 bar 字段。换言之,上面的定义等价于

foo.bar = function (a, b, c)

print(a, b, c)

end

对于此种形式的函数定义,不能再使用 local 修饰符了,因为不存在定义新的局部变量了。

函数的参数

按值传递

Lua 函数的参数大部分是按值传递的。值传递就是调用函数时,实参把它的值通过赋值运算传递给形参,然后形参的改变和实参就没有关系了。在这个过程中,实参是通过它在参数表中的位置与形参匹配起来的。

示例代码:

local 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。

示例代码:

local function fun1(a, b) --两个形参,多余的实参被忽略掉

print(a, b)

end

local function fun2(a, b, c, d) --四个形参,没有被实参初始化的形参,用nil初始化

print(a, b, c, d)

end

local x = 1

local y = 2

local z = 3

fun1(x, y, z) -- z被函数fun1忽略掉了,参数变成 x, y

fun2(x, y, z) -- 后面自动加上一个nil,参数变成 x, y, z, nil

-->output

1 2

1 2 3 nil

变长参数

上面函数的参数都是固定的,其实 Lua 还支持变长参数。若形参为 ... , 表示该函数可以接收不同长度的参数。访问参数的时候也要使用 ... 。

示例代码:

local 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

值得一提的是,LuaJIT 2 尚不能 JIT 编译这种变长参数的用法,只能解释执行。所以对性能敏感的代码,应当避免使用此种形式。

具名参数

Lua 还支持通过名称来指定实参,这时候要把所有的实参组织到一个 table 中,并将这个table 作为唯一的实参传给函数。

示例代码:

local function change(arg) -- change 函数,改变长方形的长和宽,使其各增长一倍

arg.width = arg.width * 2

arg.height = arg.height * 2

return arg

end

local rectangle = { width = 20, height = 15 }

print("before change:", "width =", rectangle.width,

"height =", rectangle.height)

rectangle = change(rectangle)

print("after change:", "width =", rectangle.width,

"height =", rectangle.height)

-->output

before change: width = 20 height = 15

after change: width = 40 height = 30

按引用传递

当函数参数是 table 类型时,传递进来的是 实际参数的引用,此时在函数内部对该 table 所做的修改,会直接对调用者所传递的实际参数生效,而无需自己返回结果和让调用者进行赋值。 我们把上面改变长方形长和宽的例子修改一下。

示例代码:

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

在常用基本类型中,除了 table 是按址传递类型外,其它的都是按值传递参数。 用全局变量来代替函数参数的不好编程习惯应该被抵制,良好的编程习惯应该是减少全局变量的使用。

函数返回值

Lua 具有一项与众不同的特性,允许函数返回多个值。Lua 的库函数中,有一些就是返回多个值。

示例代码:使用库函数 string.find ,在源字符串中查找目标字符串,若查找成功,则返回目标字符串在源字符串中的起始位置和结束位置的下标。

local s, e = string.find("hello world", "llo")

print(s, e) -->output 3 5

返回多个值时,值之间用“,”隔开。

示例代码:定义一个函数,实现两个变量交换值

local function swap(a, b) -- 定义函数 swap,实现两个变量交换值

return b, a -- 按相反顺序返回变量的值

end

local x = 1

local y = 20

x, y = swap(x, y) -- 调用 swap 函数

print(x, y) --> output 20 1

当函数返回值的个数和接收返回值的变量的个数不一致时,Lua 也会自动调整参数个数。

调整规则: 若返回值个数大于接收变量的个数,多余的返回值会被忽略掉; 若返回值个数小于参数个数,从左向右,没有被返回值初始化的变量会被初始化为 nil。

示例代码:

function init() --init 函数 返回两个值 1 和 "lua"

return 1, "lua"

end

x = init()

print(x)

x, y, z = init()

print(x, y, z)

--output

1

1 lua nil

当一个函数有一个以上返回值,且函数调用不是一个列表表达式的最后一个元素,那么函数调用只会产生一个返回值, 也就是第一个返回值。

示例代码:

local function init() -- init 函数 返回两个值 1 和 "lua"

return 1, "lua"

end

local x, y, z = init(), 2 -- init 函数的位置不在最后,此时只返回 1

print(x, y, z) -->output 1 2 nil

local a, b, c = 2, init() -- init 函数的位置在最后,此时返回 1 和 "lua"

print(a, b, c) -->output 2 1 lua

函数调用的实参列表也是一个列表表达式。考虑下面的例子:

local function init()

return 1, "lua"

end

print(init(), 2) -->output 1 2

print(2, init()) -->output 2 1 lua

如果你确保只取函数返回值的第一个值,可以使用括号运算符,例如

local function init()

return 1, "lua"

end

print((init()), 2) -->output 1 2

print(2, (init())) -->output 2 1

值得一提的是,如果实参列表中某个函数会返回多个值,同时调用者又没有显式地使用括号运算符来筛选和过滤,则这样的表达式是不能被 LuaJIT 2 所 JIT 编译的,而只能被解释执行。

全动态函数调用

调用回调函数,并把一个数组参数作为回调函数的参数。

local args = {...} or {}

method_name(unpack(args, 1, table.maxn(args)))

使用场景

如果你的实参 table 中确定没有 nil 空洞,则可以简化为method_name(unpack(args))

1. 你要调用的函数参数是未知的;

2. 函数的实际参数的类型和数目也都是未知的。

伪代码

add_task(end_time, callback, params)

if os.time() >= endTime then

callback(unpack(params, 1, table.maxn(params)))

end

值得一提的是, unpack 内建函数还不能为 LuaJIT 所 JIT 编译,因此这种用法总是会被解释执行。对性能敏感的代码路径应避免这种用法。

小试牛刀

local function run(x, y)

print('run', x, y)

end

local function attack(targetId)

print('targetId', targetId)

end

local function do_action(method, ...)

local args = {...} or {}

method(unpack(args, 1, table.maxn(args)))

end

do_action(run, 1, 2) -- output: run 1 2

do_action(attack, 1111) -- output: targetId 111

至此,Lua函数就介绍完了,下一篇将介绍Lua模块。

后续计划内容:

Lua入门+高阶

Nginx

OpenResty

LuaRestyRedisLibrary

LuaCjsonLibrary

PostgresNginxModule

LuaNginxModule

LuaRestyDNSLibrary

LuaRestyLock

stream_lua_module

balancer_by_lua

OpenResty 与 SSL

测试

Web服务

火焰图

OpenResty周边

零碎知识点

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表