Lua学习笔记,三:函数和闭包

lua的函数以function关键字开始,后跟函数名称和参数,最后以end结束,我们看一个简单的函数定义:

1 function foo()
2     --do something
3 end
4 
5 function add(a, b)
6   return a + b
7 end

在载入脚本时,函数不会执行,仅仅是会被载入内存和名称关联起来。

另外需要注意的是,调用的函数必须是在代码上方进行了定义的函数,否则运行时会报错,如下:

1 print(add(5, 10))--运行时这里会报错, 因为 add 还没有被载入到内存
2 
3 function add(a, b)
4   return a + b
5 end
6 
7 print(add(5, 10))--正常调用

参数

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

1 print "hello1"
2 print("hello2")
3 
4 function foo(a)
5   print(a)
6 end
7 
8 foo {1, 2, 3, 4}
9 foo({1, 2, 3})

函数的参数可以看做该函数体内的一个局部变量,调用函数时提供的实参数量可以与形参数量不同。Lua会自动调整实参的数量,以匹配参数表的要求,若“实参多余形参,则舍弃多余的实参;若实参不足,则多余的形参初始化为nil”:

1 function foo(a, b, c)
2   print(a, b, c)
3 end
4 
5 foo(100, 200, 300, 400)--100  200 300
6 foo(100, 200)--100  200 nil
7 foo()--nil  nil nil

变长参数

使用“...”来定义参数,Lua会在函数中定义一个名为arg的table对象来保存传递的所有参数,如下:

 1 function foo(...)
 2   --arg.n 可以取得参数的长度
 3   print("params length: ", arg.n)
 4   --打印出所有的参数及其类型
 5   for i = 1, arg.n do
 6     print("param index: ", i, ", param value: ", arg[i], ", param type: ", type(arg[i]))
 7   end
 8 end
 9 
10 foo()
11 foo(1, 2, 3)
12 foo("abc", 3.14, true, {1, 2, 3})

返回值

使用return可以返回值:

1 function add(a, b)
2   return a + b
3 end
4 
5 num = add(100, 200)
6 print(num)--300

Lua还支持返回多个值,返回值多余接收值,则舍弃多余的返回值;若返回值不足,则多余的接收值初始化为nil,和参数调用情况一直:

 1 function foo(a, b)
 2   return a + b, a * b, a - b
 3 end
 4 
 5 num1, num2, num3 = foo(100, 200)
 6 print(num1, num2, num3)--300  20000 -100
 7 
 8 num1, num2, num3, num4 = foo(100, 200)
 9 print(num1, num2, num3, num4)--300  20000 -100  nil
10 
11 num1, num2 = foo(100, 200)
12 print(num1, num2)--300  20000
13 
14 num1 = foo(100, 200)
15 print(num1)--300

深入了解Lua的函数

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

1 function foo1()
2   --do sometging
3 end
4 
5 foo2 = function()
6   --do sometging
7 end

局部函数

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

 1 obj = {}
 2 obj.add = function(a, b)
 3   return a + b
 4 end
 5 obj.subtract = function(a, b)
 6   return a - b
 7 end
 8 
 9 print(obj.add(100,200))
10 print(obj.subtract(200,123))

再看一个例子:

 1 do
 2   local function add(a, b)
 3     return a + b
 4   end
 5   local function subtract(a, b)
 6     return a - b
 7   end
 8   
 9   print(add(100,200))
10   print(subtract(200,123))
11 end
12 
13 print(add(100,200))--超出作用域, 报错
14 print(subtract(200,123))--超出作用域, 报错

闭包

闭包是由函数和与其相关的引用环境组合而成的实体。我们可以简单的理解为:子函数可以使用父函数中的局部变量,这种行为就叫做闭包。下面我们看一个经典的闭包例子:

 1 function closure()
 2   local i = 0--注意 i 被返回的函数使用
 3   return function()
 4     i = i + 1
 5     return i
 6   end
 7 end
 8 
 9 c1 = closure()--函数作用域已经结束, 但是由于闭包的原因, i 仍然被保留了下来
10 print(c1())--1
11 print(c1())--2
12 print(c1())--3
13 
14 c2 = closure()--每次调用会产生闭包的方法, 都类似类一样会生成一个新的闭包函数, i 会重新计数
15 print(c2())--1
16 print(c2())--2
17 
18 print(c1())--4
19 
20 c1 = nil--闭包保留的变量 i 会被真正销毁

更多Lua闭包的内容可以参考下面这篇博文:http://cn.cocos2d-x.org/tutorial/show?id=1078