lua 4 函数

来源:互联网 发布:高斯白噪声协方差矩阵 编辑:程序博客网 时间:2024/06/06 02:25

Lua语言基础汇总(4) -- 函数

果冻想2014-07-07 16:50:402435 次阅读

Lua中的函数和C++中的函数的含义是一致的,Lua中的函数格式如下:

1
2
3
function MyFunc(param)
     -- Do something
end


在调用函数时,也需要将对应的参数放在一对圆括号中,即使调用函数时没有参数,也必须写出一对空括号。对于这个规则只有一种特殊的例外情况:一个函数若只有一个参数,并且此参数是一个字符串或table构造式,那么圆括号便可以省略掉。看以下代码:

1
2
3
4
5
6
print "Hello World"          --> print("Hello World")等价
print [[a multi-line
          message]]          -->print([[a multi-line
                              -->               message]]) 等价
-- f是一个函数
f{x=10, y=20}               -->f({x=10, y=20}) 等价

上面代码的一些简便写法,如果不熟悉的话,在阅读别人的代码时,就会是一头雾水。


一个函数定义具有一个名称、一系列的参数和一个函数体。函数定义时,所定义的参数的使用方式与局部变量非常相似,它们是由调用函数时的“实际参数”初始化的。调用函数时提供的实参数量可以与形参数量不同。Lua会自动调整实参的数量,以匹配参数表的要求,若“实参多余形参,则舍弃多余的实参;若实参不足,则多余的形参初始化为nil”。这个与接下来要介绍的多重返回值非常相似。


多重返回值

这个应该是Lua的一个特征吧。允许函数返回多个结果,只需要在return关键字后列出所有的返回值即可。以下根据带来来说明情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function foo0() end                         -- 无返回值
function foo1() return "a" end          -- 返回一个结果
function foo2() return "a""b" end     -- 返回两个结果
 
-- 在多重赋值时,如果一个函数调用是最后,或仅有的一个表达式,
-- 那么Lua会保留其尽可能多的返回值,用于匹配赋值变量
x, y = foo2()               -- x = "a", y = "b"
x = foo2()                    -- x = "a""b"被丢弃
x, y, z = 10, foo2()     -- x = 10, y = "a", z = "b"
 
-- 如果一个函数没有返回值或者没有足够多的返回值,那么Lua会用
-- nil来补充缺失的值
x, y = foo0()               -- x = nil, y = nil
x, y = foo1()               -- x = "a", y = nil
x, y, z = foo2()          -- x = "a", y = "b", z = nil
 
-- 如果一个函数调用不是一系列表达式的最后一个元素,那么将只产生一个值:
x, y = foo2(), 20          -- x = "a", y = 20
x, y = foo0(), 20, 30     -- x = nil, y = 20, 30则被丢弃
 
-- table构造式可以完整的接收一个函数调用的所有结果,即不会有任何数量
-- 方面的调整
local t = {foo0()}          -- t = {}(一个空的table)
local t = {foo1()}          -- t = {"a"}
local t = {foo2()}          -- t = {"a""b"}
 
-- 但是,对于上述的行为,只有当一个函数调用作为最后一个元素时才会发生,
-- 而在其他位置上的函数调用总是只产生一个结果值
local t = {foo0(), foo2(), 4}          -- t[1] = nil, t[2] = "a", t[3] = 4
 
-- 我们也可以在一个函数中,使用return返回另一个函数
function MyFunc()          -- 返回a
     return foo1()          -- 注:这里是return foo1(),而不是return (foo1())
end
 
-- return foo1()和return (foo1())是两个完全不同的意思
-- 将一个函数调用放入一对圆括号中,从而迫使它只返回一个结果
print((foo0()))          -- nil
print((foo1()))          -- a
print((foo2()))          -- a


变长参数

在C语言中,函数可以接受不同数量的实参,Lua中的函数也可以接受不同数量的实参,例如以下代码:

1
2
3
4
5
6
7
8
-- 打印所有的参数
function VarArguments(...)
     for i, v in ipairs{...} do
          print(v)
     end
end
 
VarArguments(1, 2, 3)

参数表中的3个点(…)表示该函数可接受不同数量的实参。当这个函数被调用时,它的所有参数都会被收集到一起。这部分收集起来的实参称为这个函数的“变长参数”。一个函数要访问它的变长参数时,仍需要用到3个点(…)。但不同的是,此时这3个点是作为一个表达式来使用的。在上例中,表达式{…}表示一个由所有变长参数构成的数组。在C语言中使用变长参数需要注意的问题,在Lua中同样需要注意。


通常一个函数在遍历其变长参数时只需要使用表达式{…},这就像访问一个table一样,访问所有的变长参数。然而在某些特殊的情况下,变长参数中可能会包含一些故意传入的nil,那么此时就需要用select来访问变长参数了。调用select时,必须传入一个固定实参selector和一系列变长参数。如果selector为数字n,那么select返回它的第n个可变实参;否则selector只能为字符串“#”,这样select会返回变长参数的总数,请看以下代码:

1
2
3
4
for i = 1, select('#', ...) do
    local arg = select(i, ...) -- 得到第i个参数
    -- Do something else
end


select(‘#’, …)会返回所有变长参数的总数,其中包括nil(还记得table.maxn么?)对于Lua 5.0版本来说,变长参数则有另外一套机制。声明函数的语法是一样的,也是将3个点作为最后一个参数。但Lua 5.0没有提供“…”表达式。而是通过一个隐含的局部table变量“arg”来接受所有的变长参数。这个table还有一个名为“n”的字段,用来记录变长参数的总数,例如以下代码:

1
2
3
4
5
function MyFunc(a, b, ...)
     print(arg.n)
end
 
MyFunc(1, 2, 3, 4, 5)     -->3


这套旧机制的缺点在于,每当程序调用了一个具有变长参数的函数时,都会创建一个新的table。而在新机制中,只有在需要时才会去创建这个用于变长参数访问的table。这里只是对这个方法进行简单介绍,别在阅读别人的代码时,看不懂!!!


深入讨论函数

在Lua中,函数与其它传统类型的值具有相同的权利。函数可以存储到变量或table中,也可以作为实参传递给其它函数,还可以作为其它函数的返回值。在Lua中有一个容易混淆的概念是,函数与所有其它值一样都是匿名的,即它们都没有名称。当讨论一个函数名时,实际上是在讨论一个持有某函数的变量,例如以下代码:

1
2
3
4
5
6
-- 我们经常这样定义函数
function foo(x) return 2 * x end
 
-- 实际上,这只是一种“语法糖”而已;
-- 上述代码只是下面代码的一种简化书写形式
foo = function (x) return 2 * x end


实际上,一个函数定义实际就是一条语句(更准确地说是一条赋值语句),这条语句创建了一种类型为“函数”的值,并将这个值赋予一个变量。由于函数在Lua中就是一个普通的值,所以不仅可以将其存储在全局变量中,还可以存储在局部变量甚至table的字段中。


内嵌函数

若将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,这个特征叫做“词法域”。我们来看看下面一段有趣的代码:

1
2
3
4
5
6
7
8
9
10
11
function newCounter()
     local i = 0
     return function () -- 匿名函数
          i = i + 1
          return i
     end
end
 
c1 = newCounter()
print(c1())     -->输出什么?
print(c1())     -->又输出什么?


如果你很明白上面的输出,很明白上面的代码,那么闭合函数这一小节就不需要阅读了。在上述代码中,有一个变量i,对于函数newCounter来说,i是一个局部变量,但是对于匿名函数来说,当它访问这个i时,i既不是全局变量,也不是局部变量,对于我们来说,我们称这样的变量为一个“非局部的变量”。下面这段代码也是同样的道理:

1
2
3
4
5
6
7
8
9
10
function newCounter(i)
     return function () -- 匿名函数
          i = i + 1
          return i
     end
end
 
c1 = newCounter(10)
print(c1())     -->输出什么?
print(c1())     -->又输出什么?


匿名函数访问了一个“非局部的变量”i,该变量用于保持一个计数器。乍一看,由于创建变量i的函数,也就是newCounter已经返回,所以之后每次调用匿名函数时,i都应该是已经超出了作用范围。但是,Lua会以closure的概念来正确地处理这种情况。在这里简单的讲,一个closure就是一个函数加上该函数所需访问的所有“非局部的变量”。如果再次调用newCounter,那么它会创建一个新的局部变量i,从而将得到一个新的closure。在后续的总结中,我会专门总结一篇关于Lua中的闭包的博文,敬请期待。


非全局的函数

由于函数和普通变量一样,所以函数不仅可以存储在全局变量中,还可以存储在table的字段中,或局部变量中。我们可以把函数存在一个table中,比如以下代码:

1
2
3
Lib = {}
Lib.foo = function (x, y) return x + y end
Lib.goo = function (x, y) return x - y end


只要将一个函数存储在一个局部变量中,就得到了一个“局部函数”,也就是说这个函数只能在某个特定的作用域内才有效。我们可以这样定义一个局部的函数:

1
2
3
4
5
6
7
local f = function (<参数>)
     <函数体>
end
-- Lua还提供另一种特殊的“语法糖”
local function f (<参数>)
     <函数体>
end


有的时候,我们需要进行函数的前置声明,比如以下代码:

1
2
3
4
5
6
7
8
9
10
11
local f, g
 
function f()
     <一些其它操作>
     g()
end
 
function g()
     <一些其它操作>
     f()
end


总结

这篇博文对Lua中的函数进行了大体上的总结,至少看完这篇博文,你会使用Lua写函数了,会使用Lua中的函数了。但是对于比较深的东西,这里没有总结,比如“闭包”。我会专门写一篇关于Lua中的闭包的文章。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 遇到一个新手买家恶意拍下怎么办 淘宝卖螃蟹有什么要求美工怎么办 淘宝衣服吊牌剪了想退货怎么办修 用图片在淘宝搜衣服搜不到怎么办 汽车黑塑料水砂纸磨的不平怎么办 sat报名要你填10位电话怎么办 手绘板连接电脑绘画有点迟钝怎么办 走路不小心滑了一下特尴尬怎么办 小孩子头撞了头发长不出来怎么办 小孩子头磕破了不长头发怎么办 晚上洗了冷水头早上头痛怎么办 头发洗了一天就油了怎么办 米诺地尔搽剂喷了头皮油怎么办 头发可以种植吗如果是秃顶怎么办 前额头发少怎么办如何使头发增多 头发又细又软又少怎么办 宝宝一岁了头发又少又黄怎么办 生完孩子头发掉的厉害怎么办 洗完头发后头发很蓬松怎么办 头发掉的厉害怎么办吃什么好得快 头发掉的很厉害怎么办吃什么好 我头发掉的厉害怎么办吃什么药 头发干枯毛躁掉发怎么办吃什么 最近洗头时头发掉的厉害怎么办 生完小孩头发掉的厉害怎么办 生完小孩后头发掉的厉害怎么办 生完孩子后严重掉头发怎么办 生过孩子后掉头发严重怎么办 孩孑16岁了高中没考上怎么办 小孩的嘴巴里有点点该怎么办 小孩从出生哭笑嘴巴有点歪怎么办 儿童耳朵受伤后嘴巴歪了怎么办 手机锁屏图案忘了怎么办求解锁 忘给仓鼠买吃的了怎么办 两岁宝宝不爱吃饭只喝奶粉怎么办 下载百度云压缩包要提取密码怎么办 微信表情包图片过大无法添加怎么办 管理员吧群员全部踢了群主怎么办 微信笑脸表情斗图文字没有了怎么办 钨珠钢笔头用完笔墨了怎么办 暴走大事件被禁六一红鼻子节怎么办