• LUA学习笔记1-Functions
    时间:2008-09-03   作者:佚名   出处:互联网

    Function Description

    在LUA中, Function和Number, String一样属于基本类型. 凡是基本类型可以出现的地方Function一样可以出现. 这意味着Function可以存储在变量中,可以作为另一个Function的参数传递,也可以作为某个Function的返回值. 可以把Function德名字视为一种变量,它指向具体的Function实现. 这样的机制给程序设计带来了极大的弹性. 一个程序可以重写某个Function以便给他增加功能, 或者删除某个函数创建安全运行环境(SandBox).

        a = {p = print}
        a.p("Hello World") --> Hello World
        print = math.sin  -- `print' now refers to the sine function
        a.p(print(1))     --> 0.841470
        sin = a.p         -- `sin' now refers to the print function
        sin(10, 20)       --> 10      20

        function foo (x) return 2*x end 等价于 foo = function (x) return 2*x end

     

    在调用LUA的Function时,所有的参数应该包围在'('和')'之间, 即使函数没有参数, '('和')'也不应该被省略.
        print(8*9, 9/8)
        a = math.sin(3) + math.cos(10)
        print(os.date())

    这儿有一种例外情况, 如果Function只有一个参数且这个参数是literal string或者table constructor, 这时大括号keyi省略.
        print "Hello World"     <-->     print("Hello World")
        dofile 'a.lua'          <-->     dofile ('a.lua')
        print [[a multi-line    <-->     print([[a multi-line
         message]]                        message]])
        f{x=10, y=20}           <-->     f({x=10, y=20})
        type{}                  <-->     type({})

    对于面向对象程序设计, LUA支持一种特殊的语法支持, 这里的冒号称作冒号运算符. 例如 o:foo(x) 的意义是调用 o.foo 且把 o 作为额外的参数放到第一个参数的位置.

    Function Definition

    LUA中的函数定义和许多程序设计语言一样, 需要有一个函数名, 一系列的参数和函数体.
        -- add all elements of array `a'
        function add (a)
          local sum = 0
          for i,v in ipairs(a) do
            sum = sum + v
          end
          return sum
        end

    函数的参数是局部变量, LUA中比较特殊的是调用函数时参数的个数可以和定义时不一样. LUA会调整参数的个数, 这一点很像多重赋值, 调用函数时所给的多余参数会被丢掉,  调用函数时如果参数不够后边的会被赋为nil.
    例如函数定义为:
    function f(a, b) return a or b end
    如下调用:
        f(3)             a=3, b=nil
        f(3, 4)          a=3, b=4
        f(3, 4, 5)       a=3, b=4   (5被抛弃)

    这种机制和大多数强类型编成语言(C,C++,Java)不同, 不过这种做法带来了一些好处:
        function incCount (n)
          n = n or 1
          count = count + n
        end

    这里1作为函数的默认参数, 如果调用时不给参数, 那么n的值就是nil, 否则就是用用户调用时给的值.


    Multiple Results
    多重返回值是LUA提供的一种并不符合传统, 但是十分方便的机制. 一些LUA预定义的函数也使用了多重返回值的机制, 例如 string.find 函数, 他的两个返回值分别表示查找到的字符串的起始和结束位置. 如果没找到返回值为nil.
        s, e = string.find("hello Lua users", "Lua")   
        print(s, e)   -->  7      9

    使用多重返回值的方法很简单, 你只需要将返回的变量列举出来就可以了. 以下的例子函数返回一个数组中的最大值和最大值元素的索引.
       function maximum (a)
          local mi = 1          -- maximum index
          local m = a[mi]       -- maximum value
          for i,val in ipairs(a) do
            if val > m then
              mi = i
              m = val
            end
          end
          return m, mi
        end
       
        print(maximum({8,10,23,12,5}))     --> 23   3


    Variable Number of Arguments
    在LUA中Function支持不定个数的参数, 用 ... 表示. 这和LUA中普通函数的调用是有所区别的. 调用函数时所给参数可以和定义时不同, 但这里其实是LUA编译器对参数作了自动补足, 调用时参数不够的话多余的置为nil, 调用时参数过多则将多余的抛弃. 并不是真正的参数个数不定.
        printResult = ""
       
        function print (...)
          for i,v in ipairs(arg) do
            printResult = printResult .. tostring(v) .. "\t"
          end
          printResult = printResult .. "\n"
        end
    这里...表示参数个数不确定, 当print调用时, 所有的参数都被存储在一个table内, 这个隐藏变量的名字叫arg, 除了函数的参数外, arg还含有一个额外的元素n, 用来表示参数的个数.

    一个Function可以有多个返回值, 有时我们想指定使用返回值中的某一个而忽略其他的, 这时可以用 _ 表示忽略.
        local _, x = string.find(s, p)
        -- now use `x'
        ...

    另一种方法是使用select函数,
        print(string.find("hello hello", " hel"))         --> 6  9
        print(select(1, string.find("hello hello", " hel"))) --> 6
        print(select(2, string.find("hello hello", " hel"))) --> 9

    对于以下情况:
     function g (a, b, ...) end

    调用结果如下:
        g(3)             a=3, b=nil, arg={n=0}
        g(3, 4)          a=3, b=4, arg={n=0}
        g(3, 4, 5, 8)    a=3, b=4, arg={5, 8; n=2}

    Named Arguments
    在LUA中, 如果一个函数有许多参数而且大多数参数都是可选的话, 将参数定义为table会带来一些额外的方便性, 你不需要记住参数的位置, 只需要记住参数的名字即可.

    例如一个产生窗口的函数可能有许多参数,
        function Window (options)
          -- check mandatory options
          if type(options.title) ~= "string" then
            error("no title")
          elseif type(options.width) ~= "number" then
            error("no width")
          elseif type(options.height) ~= "number" then
            error("no height")
          end
       
          -- everything else is optional
          _Window(options.title,
                  options.x or 0,    -- default value
                  options.y or 0,    -- default value
                  options.width, options.height,
                  options.background or "white",   -- default
                  options.border      -- default is false (nil)
                 )
        end

     调用时很简单, 你只需要给出参数的名字和值, 顺序无关紧要.
        w = Window{ x=0, y=0 }

    Closure
        Function属于基本类型, 所以一个Function可以返回另一个Function.
       function foo()
               return function() return end
       end
       
       c1 = foo()  --> 每次调用返回一个匿名的function对象.
       c2 = foo()
       
       print(c1)  --> function: 0x87dd470
       print(c2)  --> function: 0x87dd490
        c1和c2是不同的对象.

        同样, 如果一个作为返回值的函数对象内使用了上层的局部变量, 每一个返回的函数对象内使用的这个局部变量都是不同的. 这种情况称作closure.
       
       function foo()
           local x=10
           return function()
               x=x+1
               return x end
       end
       
       c1 = foo()  --> 每次调用返回一个匿名的function对象.
       c2 = foo()

       print(c1)               --> function: 0x87dd470
       print(c2)               --> function: 0x87dd490
       
       print(c1())  --> 11
       print(c1())  --> 12
       print(c2())  --> 11
       print(c1())  --> 13
       print(c2())  --> 12

       
    Proper Tail Calls
    Proper Tail Calls是LUA的另一个有趣的特性, 在一个LUA函数中, 如果最后一个操作是返回一个函数调用, 例如 return g(...), 那么LUA不会把它当作一个函数调用而建立调用堆栈而只简单的跳转到另一个函数中.
     function foo (n)
          if n > 0 then return foo(n - 1) end
     end
    这个函数无论n是多大都不会发生堆栈溢出, 这里return foo(...)的功能相当于goto.
    以下调用并不是Tail Calls, 因为函数调用返回后还作了其他的操作:
        return g(x) + 1     -- must do the addition
        return x or g(x)    -- must adjust to 1 result
        return (g(x))       -- must adjust to 1 result

    在LUA中, 只有形如return g(...)是tail call. 然而g和他的参数可以是复杂的调用形式, 因为LUA会先计算表达式的值然后调用函数, 所以下面是一个tail call.
          return x[i].foo(x[j] + a*b, i + j)

    网友留言/评论

    我要留言/评论