当前位置: 首页 > 工具软件 > Lua Fun > 使用案例 >

(4)LUA程序设计-函数及深入理解(function)

华善
2023-12-01

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的第一个参数,第三个参数我们随便,如下

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的成员时,只有函数调用作为成员的最后一个时,函数调用才会返回其所有的值,其他情况,只返回第一个返回值。

2.2 可变参数

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)

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 end

end

c1 = count() --c1为一个闭合函数,它除了有一个匿名函数function() i = i+1 return i end外,还拥用自己的局部变量i

print(c1()) --1,调用c1时,返回0+1的结果,

再次调用c1()时

print(c1()) --2,返回1+1的结果,

再调用c1()时

print(c1()) --3,返回2+1的结果,我们可以看到c1这个闭合函数拥用自己的超值i

如果此时再创建一个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.you = 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)
(3)LUA程序设计-表达式与语句(expression & statement)(2012-07-29 22:51)
(4)LUA程序设计-函数及深入理解(function)(2012-08-03 23:00)
(5)LUA程序设计-迭代器(state iterator & stateless iterator)(2012-08-06 23:05)
(6)LUA程序设计-编译执行与错误(compile 、run & error)处理(2012-08-11 00:05)
(7)LUA程序设计-协同程序(coroutine)(2012-09-01 00:06)

 类似资料: