Lua入门教程 5.函数

0x05 函数


定义与调用形式


定义:函数是对语句表达式的抽象。


函数的调用形式:无论是语句或者表达式,都需要将所有参数放到一对圆括号中。即使调用没有参数也必须写出一对空括号。
注:此条规则对一种情况例外—一个函数只有一个参数,并且此参数是一个字面字符串或者table构造式(见下列代码)

print "Hello World"   --只有一个参数且为字面字符串
f{x = 20, y = 30}     --只有一个参数且为table构造式

冒号操作符 Lua为面向对象式的调用提供了一种特殊的语法—冒号表达式 如 o.foo(o,x)o:foo(x)是等价的

关于函数中的参数数量调用函数提供的实参数量可以与形参数量不同

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被抛弃

Lua中函数的调用规则与多重赋值的规则相似

参数规则 “实参多于形参,则舍弃多余实参;实参少于形参,则多余形参初始化为nil”


多重返回值

Lua允许函数返回多个结果

    function morevals()
        return "Hello", "World"
    end

    print(morevals())   --"Hello" "World"

可以直接将多个值在函数中进行返回


Lua将会根据实际情况进行返回参数的调整:

假如有以下函数:

function foo0() end
function foo1() return "a" end
function foo2() return "a","b" end
  • 若一个函数的调用是最后的一个表达式,那么Lua会保留尽可能多的返回值
x,y = foo2()   x="a" y="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 = foo2(),20     x="a" y=20
x,y = foo0(),20,30  x=nil y=20 30被抛弃

一系列表达式 : 多重赋值,函数调用时传入的实参列表,table的构造式和return语句

  • 当一个函数调用作为另一个函数调用的最后一个参数时,第一个函数所有的返回值都将作为实参传入第二个函数
print (foo0())     
print (foo1())      a
print (foo2())      a b
print (foo2(),10)   a 10 (出现在表达式中时,Lua会将其返回值数量调整为1)

table构造式可以完整接收所有返回的参数而不会有任何的参数调整

t = {foo0()}     t={}   (nil)
t = {foo1()}     t={"a"}
t = {foo2()}     t={"a","b"}

不过这种情况是只有当函数调用作为最后一个参数时才会发生,如果是在其他位置则只会返回一个值

  • return语句:return f() 将会返回f()中的所有返回值

变长参数

Lua中的函数可以接收不同数量的实参,看下面代码

function add(...)
local s = 0
    for i,v in ipairs{...} do
    s = s + v
    end
return s
end

print(add(1,2,3,4))   -->10

参数表中的...表示可接收不同数量的实参。当这个函数被调用时,表示所有的参数会被收集到一块。函数中使用时还依然需要使用...但此时是作为一个表达式来使用的–>{...}表示一个有所有参数构造的数组。
表达式...的行为类似一个具有多重返回值的函数。


selector 用于操作变长参数
如果selector为数字n,则返回它的第n个可变实参;否则selector只能为字符串#,这样select会返回变长参数的总数。下面的代码演示了如何用selector来遍历所有的参数

for i=1, select('#', ...) do
local arg = select(i, ...)
    <循环体>
end

--Lua5.0的代码如下
function foo(a, b, ...)
local arg = {...}; 
arg.n = select("#", ...)
end

具名实参


通过名称来指定实参

例如一个函数os.rename,用于更改文件名

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

作用:当一个函数拥有大量的参数,而其中大部分参数是可选的话,具名实参会非常有用。

w = Window{ x=0, y=0, width=200, height=100, title="lua"}

函数是第一类值,这表示函数可以与其他传统类型的值一样存储到变量中
函数还具有词法域:指一个函数可以嵌套在另外一个函数中,内部函数可以访问外部函数

对于函数print(),其实是指一个存储着可以打印字符串函数的变量print,这个变量可以存储其它的函数

a = print
a("Hello World")  --Hello World

print = math.sin --现在print变量存储了math.sin引用的函数
a(print(1.0))  --调用了正弦函数,打印出结果 0.8414709848079

函数可以被当作一种类型

function foo() return "a" end
等价于
foo = function () return "a" end

表达式 function(x) <body> end 可以被当作一种函数的构造式 如 table构造式一般

function表达式的应用,table.sort接受一个table并对其进行排序,其中一个次序函数就可以在需要时直接使用function表达式的创建

network = {
    {name = "a", IP = "1.2.3.4"},
    {name = "b", IP = "2.4.21.3"},
    {name = "c", IP = "2.32.24.33"}
}

--接下来调用table.sort
tabel.sort(network, function(a, b) return(a.name > b.name) end)

闭合函数

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

names = {"Peter", "Paul", "Mary"}
gradees = {Mary = 10, Paul = 7, Peter = 8}
table.sort(names, function(n1, n2)
                    return grades[n1] > grades[n2]
                  end)

上面代码中的grades在这个匿名函数中既不是全局变量也不是局部变量,称为 “非局部的变量”


看一下以下的代码,说明一下非局部的变量存在的合理性:

function newCounter()
local i = 0
    return function()      
            i = i + 1
           end
end

c1 = newCounter()
print(c1())  --1
print(c1())  --2
c2 = newCounter()
print(c2())  --1

Lua将以上的情况视为Closure,一个Closure代表一个函数加上这个函数所访问的非局部的变量。函数c1 c2是同一个函数创建的不同的Closure(闭合函数),他们拥有不同的非局部的变量i

非全局的函数


将函数与table结合起来

test = {}
test.foo1 = function(a, b) return a+b end
test.foo2 = function(a, b) return a-b end

test = {
    foo1 = function(a,b) return a+b end,
    foo2 = function(a,b ) return a-b end
}

--此外 Lua还提供了另外一种方式
test = {}
function test.foo1(a, b)
    return a + b
end

function test.foo2(a, b)
    return a - b
end

只要将一个函数存储在一个局部变量中,就得到一个局部函数,该函数只能在特定的作用域中使用

局部函数的定义

local f = function(参数)
    <函数体>
end

local function f(参数)
    <函数体>
end

递归函数中会较多的使用到局部函数:

local fact = function(n)
if(n==0) then return 1
    else return fact(n-1)  --Error
    end
end

在使用fact(n-1)时局部的fact的定义尚未完成,因此此时调用的是全局的fact函数。可以先定义一个局部变量,保存这个函数:

local fact 
fact = function(n)
    if(n==0) then return 1
        else return fact(n-1)
        end
end

尾调用

当一个函数调用是另一个函数的最后一个动作时,该调用是一条“尾调用” 尾调用不会耗费任何栈空间(尾调用消除)

function f(x) return g(x) end

--由于有尾调用消除的特性,下列函数不管传入什么数字都不会造成栈溢出
function foo (n)
    if n >0 then return foo(n-1) end
end

判断是否符合尾调用消除的原则
一个函数在调用完另外一个函数后,是否就无其他事情需要做了?

在Lua中只有return <func>(<args>)这样的调用形式才算是一条尾调用


猜你喜欢

转载自blog.csdn.net/yao_jianlun/article/details/51585728