当前位置:   article > 正文

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

lua function

   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,加入请注明来源。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/658189
推荐阅读
相关标签
  

闽ICP备14008679号