• chapter5 函数


      在lua中,函数是语句和表达式体现的主要机制。函数可以完成某些特定的任务、计算和返回执行结果。

    前者当成一个语句,后者当成一个表达式:

    1 print(8*9,9/8)
    2 a = math.sin(3) + math.cos(10)
    3 print(os.date())

    在这两种情况下,调用函数都要用一个括号携带一系列参数去表示。如果函数不带参数,也需要用空的括号去表示。

    但是有几个特列括号是可以省略的:

    print "Hello World"            --函数只有一个字符串参数
    print [[ a multi-line message ]]
    
    f{x=10,y=20}                --参数是一个表
    type{}

      Lua为针对面向对象的调用提供了特殊的语法——冒号操作符。

    o.foo(o,x)可以用o:foo(x)表示,这样去调用foo,是隐含地把o作为函数第一个参数。

    一个Lua程序可以使用Lua或C定义的函数。就像所有的标准Lua库是用C写的一样。

    然而,在调用一个函数时,两者是没有区别的。

    一个函数定义的常规语法像这样:

    -- add the elements of sequence 'a'
    function add (a)
        local sum = 0
        for i=1, #a do
            sum = sum + a[i]
        end
        return sum
    end

    可以看到,一个函数定义由函数名(add)、参数(a)、函数体(一系列的语句)组成。

    函数参数是当作局部变量使用,由函数调用时的参数去初始化。

    当调用函数时的参数不等于函数定义时的参数个数时,Lua会自动调整数量。类似于多重赋值。

    这种调整行为其实很有用,比如对于默认实参的应用,以下是一个全局计数器:

    function incCount(n)
        n = n or 1
        count = count + n
    end

    该函数用1作为它的默认实参,每次不带参数去调用incCount就增加1。

    5.1 多重返回值

      Lua函数有一个与众不同的特点,就是返回多个值。Lua中有几个预定义函数就是多重返回值。

    string.find 函数:

    s,e = string.find("hello Lua users","Lua")
    print(s,e)            --> 7     9

    如果该函数匹配到了Lua,它就返回两个索引值,分别指向“Lua”在字符串中的开始和结束位置。

    用Lua写的函数同样可以返回多个结果,只需在return后面给出。

    例如,找出一个序列中最大值,并返回它所在的位置:

    function maximum (a)
        local mi = 1
        local m = a[mi]
        for i = 1, #a do
            if a[i] > m then
                mi = i; m = a[i]
            end
        end
        return m , mi
    end
    
    print(maximum({8,10,23,12,5}))        -- 23    3

      Lua也会调整函数返回的个数,以适应不同的调用环境。

    当函数作为一个单独的语句调用时,Lua会丢掉所有函数返回。

    当函数作为一个表达式被调用时,Lua只保留第一个返回值。

    以下情况,函数调用处在最后的表达式中时,才能获得函数所有值:

    函数作为语句用于多重赋值、 

    函数作为实参被调用、

    table构造、

    return语句、

    有以下函数:

    function foo0() end
    function foo1() return "a" end
    function foo2() return "a","b" end 
    x,y   = foo2()    -- x="a",y="b"
    x     = f002()    -- x="a","b"被丢弃
    x,y,z = 10,foo2()  --x=10,y="a",z="b"

    如果函数不返回值,或者没有返回足够多的值,Lua会用nil去填充

    x,y = foo0()        --x=nil,y= nil
    x,y = foo1()        --x="a",y=nil
    x,y,z = foo2()     --x="a",y="b",z=nil

    如果函数不是一系列表达式的最后一个元素,则只返回一个值:

    x,y = foo2(),20        --x="a",y=20

    当函数作为作为最后一个参数或只有它一个参数被调用时,函数返回所有的结果:

    print(foo0())        -->空
    print(foo1())        -->a
    print(foo2())        -->a  b
    print(foo2(),1)    --> a  1
    print(foo2() .. "x")     -->ax

    table构造时,可以接收一个函数的所有结果

    t = {foo0()}        --t={}  空table
    t = {foo1()}        --t = {"a"}
    t = {foo2()}        --t = {"a","b"}
    
    --但是函数不是最后一个元素或唯一一个元素时,它只返回一个值
    t = {foo0(),foo2(),4}        --t[1]=nil,t[2] = "a",t[3] = 4

    最后,return语句,return f()  返回所有的 f 函数的值:

    function foo (i)
        if        i == 0 then return foo0()
        elseif i == 1 then return foo1()    
        elseif i == 2 then return foo2()
        end
    end
    
    print(foo(1))        -->a
    print(foo(2))        -->a b
    print(foo(0))        -->no results
    print(foo(3))        -->no results

    也可以将一个函数调用放入一对括号中,强制返回一个结果:

    print((foo0()))        -->nil
    print((foo1()))        -->a
    print((foo2()))        -->a

    还有一个特殊的函数——unpack,它接收一个数组作为参数,并从下标1开始返回该数组的所有元素:

    pirnt(unpack{10,20,30})  -->10  20  30
    a,b = unpack{10,20,30}  -->a=10,b=20,30被抛弃

    unpack函数的一个重要用途体现在“泛型调用”,即可以动态地以任何参数来调用任何函数。

    如果想调用函数fun,而所有参数都在数组a中:

    f = string.find
    a = {"hello","ll"}
    print(f(table.unpack(a)))  -->等价于string.find("hello","ll")
                               -->3  4            

    通常,unpack可以通过取长度操作符"#"知道有多少个元素返回,也可以通过提供确切的限制:

    print(table.unpack({"Sun","Mon","Tue","Wed"},2,3)  --返回索引2到索引3的值
    --> Mon    Tue

    尽管unpack是由C写的,但是也可以用Lua通过递归来实现:

    function unpack(t,i,n)
        i = i or 1
        n = n or #t
        if i<= n then
            return t[i],unpack(t, i+1, n)
        end
    end

    当第一次只用一个参数调用时,i = 1 ; n = #t,然后return t[1] ,紧接着返回所有unpack的值。unpack(t,2,n)...unpack(t,3,n)....

    直到最后一个n元素。

    5.2 变长参数

      Lua中的函数是可变参的,它可以接收不同的数量的参数。

    例如下面的例子,下面的函数返回所有参数的总和:

    function add(...)
        local s = 0
        for i,v in ipairs{...} do
            s =s + v
        end
        return s
    end
    
    print(add(3,4,10,25,12))    -->54

    "..." 表示这个函数是可变参函数。函数体内可以再次使用它表示变参。

    可以把"..."表达式称作变参表达式,类似于多重返回值函数。

    local a,b = ...

    同样的,上面的命令创建了两个用变参的前两个参数初始化的变量。

    实际上,可以通过变长参数模拟Lua中普通的参数传递机制:

    function foo(a,b,c)

    可以转换为:

    function foo(...)
        local a,b,c = ...
    end

    下面的函数,它是一个多值恒定函数,简单地返回了所有的参数:

    function id(...)
        return ...
    end

    来看看另一个很有用的例子。Lua提供了一个格式化文本函数(string.format)和一个文本输出函数(io.write)。

    将两个结合到一个变参函数里:

    function fwrite(fmt,...)
        return io.write(string.format(fmt,...))
    end

    注意在”...“前有一个fmt存在,可变参函数可以有任意数量的固定参数,必须放在变参前。

    调用                   参数
    fwrite()              fmt = nil, 没有变参
    fwrite("a")           fmt = "a", 没有变参
    fwrite("%d%d",4,5)    fmt = "%d%d",变参=4,5    

    为了遍历变参,函数可以使用{...}把变参收集到一个table里。

    在一些特殊的场合,变参中可能有一些nil,Lua提供了一个table.pack函数。

    该函数接收变长的参数,并且返回一个由变参组成的table表。这个表有一个隐含的n元素,代表这个表的参数总个数。

    注意,这个表返回的不一定是一个序列。

    下面的函数用table.pack去检测它的参数是否含有nil:

    function nonils(...)
        local arg = table.pack(...)
        for i = i,arg.n do
            if arg[i] == nil then
                return false
            end
        end
        return true
    end
    
    print(nonils(2,3,nil))        -->false
    print(nonils(2,3))            -->true
    print(nonils())               -->true
    print(nonils(nil))            -->false

    然而请记住,当变参中没有nil时,{...}要比table.pack方便快捷。

    5.3 具名实参

      Lua中的参数传递是具有位置性的,调用函数时,参数是按照位置进行匹配。

    用os.rename来进行阐述,该函数的作用是改变文件名。通常你会不记得哪个在前,哪个在后。

    因此,会希望这个函数能接受两个具有名称的实参,例如:

    -- invalid code
    rename(old = "temp.lua", new = "temp1.lua")

    Lua不支持这种语法,但可以稍加改变达到该效果。用table传参。

    rename{old = "temp.lua",new = "temp1.lua"}

    于是,函数定义如下:

    function rename(arg)
        return os.rename(arg.old,arg.new)
    end

    当函数有很多可选参数的时候,这种方式传参非常有用。

    比如,一个GUI库创建一个窗口的函数,就有很多参数,并且很多参数是可选的。

    最好的做法就是用具名实参:

    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.width,
            options.height, options.x
    or 0,         --default value options.y or 0,         --default value options.background or "white",   --default options.border         -- default is false (nil) ) end w = window{ x= 0,y = 0, width = 300,height = 200, title = "Lua" , background = "blue",border = true }

    window可以自由检查必要参数,也可以添加默认参数。

    _window函数实际才是需要所有正确参数来创建一个新窗口。

  • 相关阅读:
    [No0000F0]DataGrid一行Row添加ToolTip,wpf
    [No0000EE]主要的宏观经济指标查询
    [No0000E9]Microsoft Help Viewer 2.3绿色版
    [No0000F2]ip安全监视器
    [No0000ED]IPSec策略之管理
    [No0000EC]C# 字符串(String)
    [No0000EB]C# 数组(Array)
    [No0000EA]C# 可空类型(Nullable)
    [No0000E8]C# 方法 参数传递
    [No0000E7]C# 封装 与访问修饰符
  • 原文地址:https://www.cnblogs.com/daiker/p/5796846.html
Copyright © 2020-2023  润新知