1.函数介绍
1.1 LUA函数,跟别的编程语言函数定义大同小异,从另一方面说,它可以被看成是对表达式或语句的抽象机制,怎么说呢,函数分为有返回值与无返回值两种。对于没有返回值的函数而言,相当于将一条或多条语句封装起来执行,以便达到完成某项功能的目的。对于有返回值(return)的函数,相当于是一条表达,即最终会以一个结果返回被调用处。
1.2 LUA函数的定义如下
function xxx()
--statements here
end
1.3 LUA函数的调用,当函数只有一个参数,并且该参数是字符串或table构造式时,可以省略掉()括号,例如:print 'hello world',又如 dofile 'l.lua' 、unpack {12,23,'abc','def'}
print [[ adf
afsaf
adfsa]] f {x=1,y=2}等等
1.4 在前面,我们介绍了“语法糖”,支持以点号,对表达式o.fun(o,x)这样方式,必须显示地将对象O作为参数传给fun函数
而面向对象lua编程,提供了冒号操作符,表达式可以这样子写o:fun(x),它将o隐式地作为fun的第一个参数传给fun函数
1.5函数调用的实参与形参数目可以不致
例如:
function fun(a,b)
--xxxxx
end
调用
fun() -- a=nil ,b =nil
fun(1) --a=1,b=nill
fun(1,2) --a =1,b=2
fun(1,2,3) --a=1,b=2,3舍弃
其实是“少补nil,多舍弃”原则
2.LUA函数的三大特征
2.1多返回值
2.1.1函数返回值作为赋值语句的右值的情况
start,end = string.find('hello lua world','lua') ---string.find返回lua在hello lua word串中的起始,终止位置
上面例子是函数调用作为赋值的唯一右值出现的,如果将上面的代码改成下面的样子
start,end = string.find('hello lua world','lua'),'fuck U'
start返回的是lua在hello lua world串的起始位置7,而end则是为'fuck U'
再将上面的例子,在左边加一个变量如下
start,end,third = string.find('hello lua world','lua'),'fuck U'
此时start返回的是lua在hello lua world串的起始位置7,而end则是为'fuck U',third = nil
如果将上面的赋值语句的右边string.find('hello lua world','lua')与'fuck U'互换,如下
start,end,third = 'fuck U',string.find('hello lua world','lua')
此时 start ='fuck U',end =7,third = 9
2.1.2函数调用作为另外一个函数的实参参数
为了更好的说明一下,先创建三个简单的函数,如下
function fun0( ) end
function fun1( ) return 1 end
function fun2( ) return 1,2 end
将上面三个函数作为print函数的唯一参数时,如下
print(fun0()) -->空串''
print(fun1()) -->1
print(fun2()) -->1 2
print(fun0(),2) -->nil 2
print(fun1(),'hello') -->1'hello'
print(fun2(),'KKK') -->1 'KKK'
将上面三个函数的第一个参数与第二个参数互换一下,如下
print(2,fun0()) -->2
print('hello',fun1()) -->'hello' 1
print('KKK',fun2()) --> 'KKK' 1 2
将上面三个函数的两个参数,用..串连接符边接起来,如下
print(2 .. fun0()) -->报错,nil不能参与运算
print('hello'..fun1()) -->'hello1'
print('KKK'..fun2()) --> 'KKK12'
将上面的两个参数分别互换,再..串连接操作,如下
print( fun0() .. 2) -->报错,nil不能参与运算
print(fun1()..'hello') -->'1hello'
print(fun2()..'KKK') --> '1KKK'
2.1.3当函数调用作为table的构造式参数时,
如t = {fun0(),fun1(),fun2(),8}时 t[1] = nil t[2] = 1, t[3] =1,t [4] = 8
只有当函数调用是table的最后一个参数时,函数才返回所有值,原理跟2.1.1,2.1.2一样
2.1.4当函数调用作为return 的参数的时候
如 return fun0() --> 返回所有值 fun0()无返回值所以结果为nil
return fun1() --> 返回所有值 fun1()只有一个返回值所以结果为1
return fun2() --> 返回所有值 fun2()有两个返回值所以结果为1 2
对上面的fun加上括号,即将函数调用进行()运算,如下
return( fun0()) --> (fun0())结果为nil
return fun1() --> (fun1())只有一个返回值所以结果为1
return fun2() --> (fun2())有两个返回值但只返回第一个值,所以结果为1
再看最后三个return语句
return fun0(),'a' --> nil 'a'
return fun1(),'b' --> 1 'b'
return fun2() ,'c' -->1 'c'
总结,从2.1.1-2.1.4,我们可以知道,当函数调用作为表达式的成份的时候,将强制函数返回第一个返回值,并且如果函数没有返回值的情况下参与表达式运算,将报错
当函数调用作为多重返回赋值语句,table构造式,函数调用实参以及return的成员时,只有函数调用作为成员的最后一个时,函数调用才会返回其所有的值,其他情况,只返回第一个返回值。
LUA当中用三个点(...)代表可变参数,在函数体内,可以直接使用...,作为传入的参数列表,下面是一个简单的打印可变参数的函数定义
function printArgs(...)
for i,v = ipairs(..) do
print(v);
end
end
有时候,可能可变参数列表中间可能会有nil的实际参数,所以我们需要借助select 函数来过滤掉人为故意传入的nil实参
例子如下
function printArgs(...)
for i=1,select('#',...) do --select('xx',...)当xx为#号时返回...可变参数列表的长度,当xx为数字时,返回参数列表中第xx个参数
local v = select(i,...) --v为第i个参数
if v then --如果v不为nil时,处理函数体
--fucking here
end
end
end
注:LUA5.0并不能在函数内部直接...来访问参数列表,必须通过自定义一个局部table变量来保存...,例如
function xxx(...)
local t = {...}--定义局部变量t来保存...参数
t.n = select('#',...);
end
2.3 具名实数
所谓具名实数,指的是,在函数定义时,可以为函数可选的参数,提前设置了默认值,当函数调用时,该参数没有值时,就使用提前预调的默认值
例如创建一个人,它有必须属性名字name,身份证identity,还有,它是否是有“高富帅”gfs的特征,
首先建一个人类
function Person(opts) --lua只允许参数为table等八种基本类型,不允许在调用Person时形如Person(name='zw',identity='8888',gfs=true)这样子调用,只能使用{}将参数封装起 来,所以此处opts我们定义为一个{}表类型
local name = opts.name or'gfs' --可选参数
local identity = opts.identity or '1111' --可选参数
if type(opts.gfs) ~= boolean then --必选参数,否则出错
error('the error gfs')
end
end
总结:LUA函数有三大特征:多重返回值,可变参数列表,具名实参。
-----3. LUA函数的深入理解--------------------------------------------------------------------------------
3. LUA函数的深入理解
3.1 在LUA当中,函数作为第一类值(first-class value)。意思是说,函数可以被当成其他变量一样处理,如
function fuck() .... end 像这样子的函数定义,相当于 定义一个变量 fuck = function() ... end这样一句变量定义的语句
此外,函数变量是可以被赋给另一个变量
例1: 对打印函数 名称的修改
a = print;
a('hello') --等价于print('hello')
例2: 将math.sin(弧度)修改成math.sin(角度)
oldMathSin = math.sin;
math.sin = function(角度) return (角度* math.pi/180) end
3.1 闭合函数closure
在讲闭合函数前,我们先介绍一个‘非局部的变量’这个概念,它是指有A,B两个函数,在A函数中嵌入B函数,然后B函数调用了在A函数里边的局部变量,而该局部变量又不在内部函数B里边定义,即局部变量界于A函数里,但不在B函数里定义的变量,我们称之为非局部的变量,也叫超值upvalue
例2:利用table的sort方法来处理排序的一个问题,该方法有两个参数,第一个参数是待排序的一个table数组,第二个方法是一个具有两个参数的匿名函数(我们称之为高阶函数,所谓高阶函数,指匿名函数作为另外一个函数的参数被传到函数内)
学生名称列表,另一个是学生名称对应年级的列表
names = {'aven','tom','cat'}
grades={tom = 2,cat = 1,aven= 8}
function sortByGrade(names,grades)
table.sort(names,function(n1,n2) return grades[n1]>grades[n2] end)
说明,上面的grades是函数sortByGrade()的参数,即局部变量,但LUA支持在匿名函数function(n1,n2) return grades[n1]>grades[n2] end里边调用grades这个变量,我们称这个为非局部的变量,或超值
讲完超值(非局部的变量),我们讲一下何为闭合函数,其实说白了,LUA就只有closure,而函数只是一种特殊的closure,我们称之为闭合函数,它是指函数+该函数所访问的超值,举个例子
例3:一个简单的计数器
function count()
local i = 0;
return function() i = i+1 return i endend
c1 = count() --c1为一个闭合函数,它除了有一个匿名函数function() i = i+1 return i end外,还拥用自己的局部变量i
print(c1()) --1,调用c1时,返回0+1的结果,
再次调用c1()时
print(c1()) --2,返回1+1的结果,
再调用c1()时
如果此时再创建一个c2闭合函数,即c2 = count()
调两个c2(),打印情况如下
print(c2()) --1
print(c2()) --2
再调用一次c1,之后调用一次c2,打印情况如下
print(c1()) --4
print(c2()) --3
可以看出c1,c2有各自的超值i;
3.2 非全局的函数
3.2.1函数作为table的成员
t = {}
t.fuck = function() ... end
上面等价于
t = {
fuck = function() ... end,
you = function() ... end
}
还可以等价于
t = {}
function t.fuck() ... end
function t.you() ... end
3.2.2函数为作局部变量的值
如 local f = function() ... end
local y = function() ... end(基本函数定义规范)
在局部函数y内可以调用f()局部函数:local y = function() ... f() ...end
上面两行局部变量函数的定义等价于
local function f() ...end local function y() ...end(语法糖)
区别一下“基本函数定义规范”与“语法糖”定义的局部变量函数
我们举个例子说吧,就举阶乘为例
local fac = fucntion (n)
if n==0 then return 1
else return n*fac(n-1) --此处报错,原因是当再次调用fac时,fac并未定义,只是调用了全局的fac,而全局也找不到fac的定义
end
我们将上面的fac函数改成语法糖的定义,如下
local fucntion fac (n)
if n==0 then return 1
else return n*fac(n-1) --此处不报错
end
之所以不报错,因为是语法糖定义的,所以展开的时候,会先定义局变变量,再定义函数,代码如下面local fac ;
fac = fucntion (n)
if n==0 then return 1
else return n*fac(n-1) --此处不报错
end
4 LUA支持正确的尾消除,即尾调用消除。下一节将前面提及的泛型for的深入介绍,并讲迭代器相关知识,期待!!
LUA技术交流群,请加Q群:139315537,加入请注明来源。
(1)LUA程序设计-开篇(beginning)(2012-07-28 00:47) (2)LUA程序设计-类型与值(type & value)(2012-07-28 23:12)
|