lua function 详解

lua function

lua八种数据类型

  • nil 全局变量没被赋值默认为 nil,删除变量就赋值为 nil
  • boolean(bool) false和nil为假,其它都为真
  • number 实数,可以是整数,浮点数
  • string 字符串,一旦赋值不能被修改,可以通过方法string.gsub()来修改
  • function 函数
  • table 数组、容器
  • userdata (类,其它语言转换过来就变成userdata类型)
  • thread 线程

lua function

  • 在这几种数据类型中,其中nil,boolean,number比较简单,string需要注意一下它里面的几个内置函数,这个网上都有介绍的,这里主要先介绍一下function
  • Lua支持面向对象,操作符为冒号‘:’ o:foo(x) <==> o.foo(o, x)
  • Lua程序可以调用C语言或者Lua实现的函数
  • 函数的参数跟局部变量一样,用传入的实参来初始化,多余的实参被丢弃,多余的形参初始化为nil
  • 最近细看了一下,lua function有一些比较特别也比较有用的语法
    • 多个返回值
    • 变参
    • 函数变量(函数名)
    • closure(闭合函数)
    • 非全局函数
    • 尾调用

多个返回值

  • 不同于常规函数,Lua的函数可以返回多个返回值

    function foo() 
    	return "a","b" 
    end
    print(foo())   a, b
    
    • 运用:函数需要返回多个值的时候,没必要组合成table,可以按照返回值的重要程度,指定顺序返回。获取值的时候,可以选择性获取自己比较感兴趣位置的参数赋值即可,不用关心其他参数的值
  • 如果一个函数调用不是一系列表达式的最后一个元素,那么将只产生一个值

    x,y=foo(),"c"
    print(x,y)  a,c 
    
    
  • table构造式可以完整接受一个函数调用的所有结果

    t={foo()}
    print(#t)  2
    
    • return语句,诸如return f()这样的语句,将返回f的所有返回值,如果写成return (f(x)),将只返回一个值。
    • 运用:需要转成table时也可以在外部得到值了以后,用table构造
  • unpack,他接受一个数组作为参数,并从下标1开始返回该数组的所有元素,unpack递归实现

    function unpack (t, i)
      i = i or 1
      if t[i] ~= nil then
        return t[i], unpack(t, i + 1)
      end
    end
    

    运用:可以灵活实现table多参数的解包,对应压缩包函数pack

变参

  • Lua中的一些函数接受可变数量的参数

    function add(...)
    	local s=0
    	for i,v in ipairs{...} do
        	s=s+v
    	end
    	return s
    end
    print(add(3,4,5))
    
    • 参数中的3个点(…)表示该函数可接受不同数量的实参,调用时3个点代表了实参。
    • 表达式"…"的行为类似于一个具有多重返回值的函数,他返回的是当前函数的所有变长参数
    function foo(a,b,c)
    //等价于
    function foo(...)
    	local a,b,c=...
    end
    
    • 运用:这里我的感觉是写法简洁,传递的时候不会传错,即如果这个函数只是这个中间作用,并没有使用这个参数,这样用比较好,但是如果一般只有一次传值,且马上使用的话,还是尽量不要用变参

函数变量(函数名)

  • 函数与所有其他值一样都是匿名的,即他们都没有名称。当讨论一个函数名时(例如print)。实际上是在讨论持有某函数的变量,这与其他变量持有各种值一个道理

    a = { p = print }
    a.p("Hello World")
    b = print
    b("Hello World")
    
    function foo(x) return 2 * x end
    //等价于
    foo = function(x) return 2 * x end
    
    
  • 所以我们可以这样去理解,一个函数定义实际上就是一条语句(更准确说是赋值语句)。这条语句创建了一种类型为”函数“的值

closure(闭合函数)

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

  • 一个closure就是一个函数加上该函数所需访问的所有“非局部变量”

    function newCounter() 
    	local i = 0
    	return function() --匿名函数
        	i = i + 1
        	return i
    	end
    end
    c1 = newCounter()
    print("The return value of first call is " .. c1())
    print("The return value of second call is " .. c1())
    --输出结果为:
    --The return value of first call is 1
    --The return value of second call is 2
    
    
  • 其函数体内的局部变量i被称为"非局部变量",该变量被newCounter函数体内的匿名函数访问并操作。再有就是在函数newCounter返回后,其值仍然被保留并可用于下一次计算

    扫描二维码关注公众号,回复: 12442943 查看本文章
    function newCounter() 
    	local i = 0
    	return function() --匿名函数
        	i = i + 1
        	return i
    	end
    end
    c1 = newCounter()
    c2 = newCounter()
    print("The return value of first call with c1 is " .. c1())
    print("The return value of first call with c2 is " .. c2())
    print("The return value of second call with c1 is " .. c1())
    --输出结果为:
    --The return value of first call with c1 is 1
    --The return value of first call with c2 is 1
    --The return value of second call with c1 is 2
    
    
  • 由此可以推出,Lua每次在给新的闭包变量赋值时,都会让不同的闭包变量拥有独立的"非局部变量"

非全局函数

  • Lua中的函数不仅可以直接赋值给全局变量,同时也可以赋值给其他类型的变量,如局部变量和table中的字段等

    Lib = {}
    Lib.add = function(x,y) return x + y end
    Lib.sub = function(x,y) return x - y end
    //或者是在table的构造式中直接初始化,如:
    Lib = { add = function(x,y) return x + y end, 
           sub = function(x,y) return x - y end
    }
    //除此之外,Lua还提供另外一种语法来定义此类函数,如:
    Lib = {}
    function Lib.add(x,y) return x + y end
    function Lib.sub(x,y) return x - y end
    
    
  • 对于这种局部函数,Lua还提供另外一种更为简洁的定义方式,如:

    local function f(x,y) return x + y end
    //该写法等价于:
    local f
    f = function(x,y) return x + y end
    
    • **运用:**对于写递归函数,需要先申明这个变量,才能使用

      //错误递归
      local f = function()
          f()
          if true then
              return
          end
      end
      f()
      
      //应改成
      local f
      f = function()
          f()
          if true then
              return
          end
      end
      f()
      
      
      

尾调用

  • 在Lua中支持这样一种函数调用的优化,即“尾调用消除”。我们可以将这种函数调用方式视为goto语句,如:

    function f(x) return g(x) end
    
    
  • 由于g(x)函数是f(x)函数的最后一条语句,在函数g返回之后,f()函数将没有任何指令需要被执行,因此在函数g()返回时,可以直接返回到f()函数的调用点。由此可见,Lua解释器一旦发现g()函数是f()函数的尾调用,那么在调用g()时将不会产生因函数调用而引起的栈开销。这里需要强调的是,尾调用函数一定是其调用函数的最后一条语句,否则Lua不会进行优化。

  • 一条“尾调用”就好比是一条“goto语句”。因此在Lua中”尾调用“的一大应用就是编写”状态机(state machine)"

    • 这种程序通常以一个函数来表示一个的状态,改变状态就是goto到另一个特定的函数。举一个简单的迷宫游戏的例子来说明这个问题。例如,一个迷宫有几间房间,每间房间中最多有东南西北4扇门。用户在每一步移动中都需要输入一个移动的方向。如果在某个方向上有门,那么用户可以进入相应的房间;不然,程序就打印一条警告。游戏目标是让用户从最初的房间走到最终的房间。
    • 实现一个具有4间房间的迷宫
    function room1 ()
      local move = io.read()
      if move == "south" then return room3()
      elseif move == "east" then return room2()
      else print("invalid move")
           return room1()   -- stay in the same room
      end
    end
    
    function room2 ()
      local move = io.read()
      if move == "south" then return room4()
      elseif move == "west" then return room1()
      else print("invalid move")
           return room2()
      end
    end
    
    function room3 ()
      local move = io.read()
      if move == "north" then return room1()
      elseif move == "east" then return room4()
      else print("invalid move")
           return room3()
      end
    end
    
    function room4 ()
      print("congratulations!")
    end
    
    
  • 若没有”尾调用消除“的话,每次用户的移动都会创建一个新的stack,移动若干步之后就有可能导致栈溢出。

小结

  • 其实function的这几种用法,我们平时也遇到过很多,需要我们合理的运用

参考链接:

https://www.cnblogs.com/youxin/p/3796497.html

猜你喜欢

转载自blog.csdn.net/weixin_41722502/article/details/110521063