【1】函数定义
Lua函数定义格式如下:
1 optional_function_scope function function_name(argument1, argument2, argument3..., argumentn)
2 function_body
3 return result_params_comma_separated
4 end
解析:
optional_function_scope: 该参数是可选的,指定函数是全局函数还是局部函数。
未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
function_name: 指定函数名称。
argument1, argument2, argument3..., argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
function_body: 函数体,函数中需要执行的代码语句块。
result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。
【2】Lua函数示例
函数示例
1 --[[ 函数返回两个值的最大值 --]]
2 local function max(num1, num2)
3 if (num1 > num2) then
4 result = num1;
5 else
6 result = num2;
7 end
8
9 return result;
10 end
11
12 -- 调用函数
13 print("两值比较最大值为 (10, 4) :: ", max(10, 4))
14 print("两值比较最大值为 (5, 6) :: ", max(5, 6))
15
16
17 --[[
18 两值比较最大值为 (10, 4) :: 10
19 两值比较最大值为 (5, 6) :: 6
20 ]]
【3】Lua函数特色
(1)将函数作为参数传递给函数。示例如下:
1 --[[将函数作为参数传递给函数]]
2 myprint = function(param)
3 print("这是打印函数 - ##", param, "##")
4 end
5
6 function add(num1, num2, functionPrint)
7 result = num1 + num2
8 -- 调用传递的函数参数
9 functionPrint(result)
10 end
11
12 myprint(10)
13 -- myprint 函数作为参数传递
14 add(2, 5, myprint)
15
16 --[[
17 这是打印函数 - ## 10 ##
18 这是打印函数 - ## 7 ##
19 ]]
(2)函数可以返回多个值
例1,代码如下:
1 --[[例如:string.find 其返回匹配串“开始和结束的下标”(如果不存在匹配串返回nil)]]
2 s, e = string.find("http://www.baidu.com", "baidu")
3 print(s, e)
4
5 --[[执行结果
6 12 16
7 ]]
8
9 s, e = string.find("http://www.google.com", "baidu")
10 print(s, e)
11
12 --[[执行结果
13 nil nil
14 ]]
例2,代码如下:
1 --[[return后列出要返回值的列表即可返回多值]]
2 function maximum (a)
3 local mi = 1 -- 最大值索引
4 local m = a[mi] -- 最大值
5 for i,val in ipairs(a) do
6 if val > m then
7 mi = i
8 m = val
9 end
10 end
11 return m, mi
12 end
13
14 print(maximum({8, 10, 23, 12, 5}))
15
16 --[[执行结果
17 23 3
18 ]]
例3,特殊函数unpack,它可以接受一个数组作为参数,并从下标1开始返回该数组的所有元素:
1 print(unpack{10, 20, 30}) -- 10 20 30
(3)不定形参函数
3.1 自定义不定形参函数:Lua函数可以接收可变数目的参数
1 function add(...)
2 local s = 0
3 for i, v in ipairs{...} do -- {...} 表示一个由所有变长参数构成的数组
4 s = s + v
5 end
6 return s
7 end
8
9 print(add(3, 4, 5, 6, 7)) -- 25
3.2 将可变参数赋值给一个变量
1 --[[将可变参数赋值给一个变量]]
2 function average(...)
3 result = 0
4 local arg = {...} -- arg 为一个表,局部变量
5 for i,v in ipairs(arg) do
6 result = result + v
7 end
8 print("总共传入 " .. #arg .. " 个数")
9 return result / #arg
10 end
11
12 print("平均值为", average(10, 5, 3, 4, 5, 6));
13
14 --[[执行结果
15 总共传入 6 个数
16 平均值为 5.5
17 ]]
3.3 可以通过select("#", ...来获取可变参数的数量)
1 --[[可以通过 select("#",...) 来获取可变参数的数量]]
2 function average(...)
3 result = 0
4 local arg = {...}
5 for i, v in ipairs(arg) do
6 result = result + v
7 end
8 print("总共传入 " .. select("#", ...) .. " 个数")
9 return result / select("#", ...)
10 end
11
12 print("平均值为", average(10, 5, 3, 4, 5, 6))
13
14 --[[执行结果
15 总共传入 6 个数
16 平均值为 5.5
17 ]]
3.4 需要几个固定参数再加上可变参数时,固定参数必须放在可变参数之前
1 function fwrite(fmt, ...) -- 固定的参数fmt
2 return io.write(string.format(fmt, ...))
3 end
4
5 fwrite("baidu\n") -- fmt = "baidu", 没有变长参数。
6 fwrite("%d%d\n", 1, 2) -- fmt = "%d%d", 变长参数为 1 和 2
7
8 --[[执行结果
9 baidu
10 12
11 ]]
3.5 遍历变长参数时只需要使用{...},然而变长参数可能会包含一些nil,那么就需要用select函数来访问变长参数。
[1] select('#', ...)返回可变参数的长度
[2] select(n, ...)用于访问n到select('#', ....)的参数
1 --[[
2 遍历变长参数的时候只需要使用{…},然而变长参数可能会包含一些nil,那么就可以用select函数来访问变长参数了:select('#', …) 或者 select(n, …)
3 select('#', …) 返回可变参数的长度
4 select(n, …) 用于访问 n 到 select('#',…) 的参数
5 ]]
6
7 do
8 function foo(...)
9 for i = 1, select('#', ...) do --> 获取参数总数
10 local arg = select(i, ...); --> 读取参数
11 print("arg", arg);
12 end
13 end
14
15 foo(11, 12, 13, 14);
16 end
17
18 --[[执行结果
19 arg 11
20 arg 12
21 arg 13
22 arg 14
23 ]]
(4)具名实参
具名实参,即具体名称的实参值。比如,字符串拷贝函数有两个参数(src, dest),当我们记不清第一个参数是src还是dest时,可以在调用时明确指明具体实参值。
Lua语言中的rename接口,就是个很有代表性的例子。试着新建一个文件temp.lua,然后复制以下代码段:
1 function rename(arg)
2 return os.rename(arg.old, arg.new)
3 end
4
5 rename({old = "temp.lua", new = "temp1.lua"})
6 --rename({new = "new1.lua", old = "temp1.lua"})
注意观察:执行结果,同目录下会多一个temp1.lua文件
再把第六行的注释去掉,然后执行结果:同目录下会多一个new1.lua文件。
(5)待续
【4】深入函数
(1)闭合函数
[1.1] 在Lua中,函数是第一类值
如何理解这个“第一类值”?类比着学习,我们知道C语言的数据类型有整数、浮点数、枚举、指针、数组、结构、共用体、void等等。
前面我们也学习了《Lua数据类型》包括nil、boolean、number、string、function等等。
即就是,在Lua中,函数可以存储到变量(无论全局变量还是局部变量)中或table中,可以作为实参传递给其他函数,还可以作为其他函数的返回值。
先看最常见的这个求和函数定义:
1 function func(a, b) return a + b end
其等价于
1 func = function(a, b) return a + b end
两种方式比较:
第一种方式,似乎更符合我们对函数的潜意识(传统的)视觉印象规则。
第二种方式,更彰显了Lua中函数作为“第一类值”的体现和范畴。
通过以上两种方式的对比,我们可以更深入的理解Lua中函数的定义本质:
一个函数定义实质上是一条赋值语句,创建了一种类型为“函数”的值,且将其赋值于变量func。
可以验证一下类型,如下所示:
1 func = function(a, b) return a + b end
2 print(func) -- function: 02DF8FC8
3 print(type(func)) -- function
4 print(func(10, 20)) -- 30
在Lua中,将表达式“function(x)
end”视为一种函数的构造式,就像table的构造式{ }一样。将这种函数构造式的结果称为一个“匿名函数”,虽然一般情况下,会将函数赋予全局变量,即给予其一个名称。
但是,在某些特殊情况下,仍会需要直接用到匿名函数。
不要问我哪里需要用到,其实你肯定见过,比如sort函数,请看如下示例:
1 students =
2 {
3 {name = "Wang", age = "18"},
4 {name = "Qin", age = "20" },
5 {name = "Li", age = "19"},
6 {name = "Wei", age = "21"},
7 }
8
9 --[[ before sort ::]]
10 print("before sort ::")
11 for i = 1, #students do
12 print(i, students[i].name, students[i].age)
13 end
14
15 table.sort(students, function(a, b) return (a.name > b.name) end)
16
17 --[[ after sort ::]]
18 print("after sort ::")
19 for k, v in pairs(students) do
20 print(k, v.name, v.age)
21 end
22
23 --[[执行结果
24 before sort ::
25 1 Wang 18
26 2 Qin 20
27 3 Li 19
28 4 Wei 21
29 after sort ::
30 1 Wei 21
31 2 Wang 18
32 3 Qin 20
33 4 Li 19
34 ]]
像sort这样的函数,接受另一个函数作为实参,称其是一个“高阶函数”,是一种强大的编程机制。
1 names = {"Peter", "Paul", "Mary"}
2 grades = {Mary = 10, Paul = 7, Peter = 8}
3 table.sort(names, function(n1, n2)
4 return grades[n1] > grades[n2]
5 end)
6
7 for k, v in ipairs(names) do
8 print(k, v)
9 end
10
11 --[[
12 1 Mary
13 2 Peter
14 3 Paul
15 ]]
16
17 names = {"Peter", "Paul", "Mary"}
18 grades = {Mary = 10, Paul = 7, Peter = 8}
19
20 function sortbygrade(names, grades)
21 table.sort(names, function(n1, n2)
22 return grades[n1] > grades[n2]
23 end)
24 return names
25 end
26
27 for k, v in ipairs(sortbygrade(names, grades)) do
28 print(k, v)
29 end
30
31 --[[
32 1 Mary
33 2 Peter
34 3 Paul
35 ]]
36
37 --[[一般函数]]
38 function increCounter1()
39 local i = 0
40 i = i + 1
41 return i
42 end
43
44 print(increCounter1) -- function: 02A6B8F8
45 print(increCounter1()) -- 1
46 print(increCounter1()) -- 1
47
48 --[[闭合函数]]
49 function increCounter2()
50 local i = 0
51 return function()
52 i = i + 1
53 return i
54 end
55 end
56
57 print(increCounter2) -- function: 02A6B738
58 print(increCounter2()) -- function: 02A6B918
59
60 c1 = increCounter2()
61 print(c1()) -- 1
62 print(c1()) -- 2
63 c2 = increCounter2()
64 print(c2()) -- 1
65 print(c1()) -- 3
66 print(c2()) -- 2
67
68 --[[闭合函数 局部变量]]
69 function increCounter3()
70 return function()
71 local i = 9
72 i = i + 1
73 return i
74 end
75 end
76
77 print(increCounter3) -- function: 02A6B6F8
78 print(increCounter3()) -- function: 02A6B7D8
79
80 c3 = increCounter3()
81 print(c3()) -- 10
82 print(c3()) -- 10
83 c4 = increCounter3()
84 print(c4()) -- 10
85 print(c4()) -- 10
86 print(c4()) -- 10
匿名函数
(2)非全局函数
(3)正确尾调用
【5】总结
Good Good Study, Day Day Up.
顺序 选择 循环 总结