Lua语言学习之函数探索

 

前面已经接触到函数了,几乎所有语言都会引入函数的概念。在Lua中,函数是一种对语句和表达式进行抽象的主要机制。

函数既可以完成某项特定的任务,也可以只做一些计算并返回结果。

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

函数一般有三部分构成:函数名、参数和函数体,Lua中用function来定义一个函数。

有的函数有返回值,有的则没有返回值。Lua中与其他语言不同的是:函数的返回值可以有多个。

例如:

function getData( a )
	return 1, 2, 3 , a
end

 

与其他语言一样,函数名后面跟一对括号,参数放在括号中,返回值用return,

最后用end表示该函数结束,这个不能忘记,否则就会导致错误。

1.参数有默认值的情况

 

function inccount( n )
	n = n or 1
	count = count + n
end

对于inccount函数,如果没有传入参数,那么n就使用默认值 1,这样就用 or 实现了参数有默认值的功能。

2.省略括号的情况

当函数的参数只有一个,且传入的是字面字符串或者table构造式,那么就可以把括号省去不写:

print “ Hello lua ”

print { 2, 3, 4, 5}

dofile ' a.lua '

3.多重返回值

上面已经看到了多重返回值的情况,再讨论一下不同的使用情况。

当函数的调用作为一条单独语句时,Lua会丢弃所有的返回值。若将函数作为表达式的一部分来

调用时,Lua只保留函数的第一个返回值。只有当一个函数调用是一系列表达式中的最后一个元素时,

才能获得它的所有返回值。

对于函数: function func1( ) return a, b end, 调用时:x, y = funct1( ), 可以得到函数返回的两个值,

如果:x,y = func1( ), 20, 这样调用就只能得到第一个返回值。也就是说,只要函数的调用不是在最后,

就只能得到一个返回值。所以,x, y, z= func1, 20, 即使这样调用,x = a,y = 20, z = nil,也只能得到a,

且 z 没有被赋值。

 

4.变长参数(variable number of arguments)

Lua中可以接收不同数量的实参,C++中也有这种功能,用三个点(...)来表示所有的参数。

 

function foo( ... )
	print(" parameters: ", ... )
end

 

上面这个函数就是打印所有的变长参数。也可以在变长参数前加任意个数的固定参数。函数select可以

 

访问变长参数的个数,调用select函数时必须传入一个固定实参selector和一系列变长参数。如果selector

为数字n,纳闷select返回它的第n个可变实参;否则,select只能为字符“#”,这样select会返回变长参数的总数。

 

for i = 1, select ( ' # ', ... ) do
	local arg = select ( i, ... )
	print( arg )
end

上面演示了循环打印出所有的变长参数。

注意:select ( ' # ', ... )会返回所有的变长参数,其中包括 nil。

5.具名实参(named arguments)

看一行Objective-C的代码:

[ button addTarget: self action:@selector(doAction) forControlEvents:UIControlEventTouchUpInside ]

 

 

这是给一个按钮添加一个点击回调事件的方法,每个参数前都有一个部分方法名对应,

很容易区分各个参数的意义,Lua没有这个功能。但是,我们可以一些细微的改变来

获得相同的效果。这时要用到当参数只有一个table构造式时,可以省略圆括号。

rename { old = " oldname.lua ", new = " newname.lua " }

6.匿名函数

很多时候我们会使用匿名函数,最常用的就是回调函数,例如 table.sort 这个函数中会传入一个定义

排序规则的次序函数就用到了匿名函数:

table.sort( tab, function ( a, b ) return ( a.name > b.name ) end )

 

 

7.闭包函数 (closure)

     将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,

这项特征称之为”词法域“,也称这个局部变量为“非局部变量(non-local variable),那么闭包就

是一个函数加上该函数所需访问的所有“非局部变量”。考虑以下代码:

 

function newCounter ( )
local i = 0
return function ( )
		i = i + 1
		return i
	end
end

 

调用函数:

 

 

c1 = newCounter ( )
print( c1( ) )  -------> 1
print( c1( ) )  -------> 2
c2 = newCounter ( )
print( c2( ) )  -------> 1
print( c1( ) )  -------> 3
print( c2( ) )  -------> 2

 

咋一看,得到的结果很神奇,c1( )第二次调用还会使用上一次调用 i 保存的值,这个变量 i 就是“非局部变量”,

也称“upvalue”。

用一个C++的例子来解释:

class Counter {
public:
      Counter() : i(0) {}
      int operator () () {
           return i++;
      }
      int i;
};


类Counter中的成员变量 i 就相当于newCounter中的 i ,只要实例化一个Counter对象,那么每次调用

operator ( ) 都会对同一个i 加1,和闭包的原理是类似的。

8. 非全局函数(non-global function)

    由于函数是“第一类值”,所以很明显,函数不仅可以存储在全局变量中,还可以存储在table的字段中和局部变量中。

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

这相当于:

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

注意:Lua为面向对象式的调用也提供了一种特殊的语法——冒号操作符。所以,Lib.foo(x,y)

也可以写成:Lib:foo( x, y )。后面会在面向对象编程中详细介绍。

 

9.递归函数

一个错误的递归函数定义:

 

local fact = function ( n )
if n == 0 then return 1
else return n * fact( n - 1 )
	end
end

想想为什么?

 

改成:

local fact 
fact = function ( n )
if n == 0 then return 1
else return n * fact( n - 1 )
	end
end

现在就对了。

 

也可改成:

local  function fact ( n )
if n == 0 then return 1
else return n * fact( n - 1 )
	end
end

这样也是没问题的。

10.尾调用(tail call)

所谓“尾调用”就是一种类似goto的函数调用。当一个函数调用是另一个函数的最后一个动作时,该调用才算是一条“尾调用”。

如:

function f ( x ) return g ( x ) end

只有这种形式才算“尾调用”。

“尾调用”的作用是“尾调用消除(tail-call elimination)”,也就是在“尾调用”之后,程序不需要保存任何关于该函数的栈信息。

关于函数的内容全部总结完毕!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值