Lua-基础(三)函数

发布于 2020-02-01  132 次阅读


函数可以完成2件事,完成工作或者计算并返回结果

函数语法

function func_name (arguments-list)
statements-list
end

Ps:Lua 使用的函数可以是 Lua 编写也可以是其他语言编写,对于 Lua 程序员来说用什
么语言实现的函数使用起来都一样。

function Show(a)
    print(a)
end
Show(100)

返回多个值

在lua中函数可以返回多个值,例如 string.find,其返回匹配串“开始和结束的下标”
(如果不存在匹配串返回 nil)。

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

计算最大值和最大值的索引

function MaxM(arr)
    local maxIndex=1
    local max=arr[maxIndex]
    for key, value in pairs(arr) do
        if value>max then
            maxIndex=key
            max=value
        end
    end
    return max,maxIndex
end
local max,maxIndex=MaxM({1.5,6,7,9})
print('最大值和索引为:'..max..'  '..maxIndex)

返回函数

return fac()这样的形式就是返回函数,其意义是执行fac函数并返回fac的返回值,若没有返回nil。

可变参数

unpack

unpack,接受一个数组作为输入参数,返回数组的所有元素。

f = string.find
a = {"hello", "ll"}
print(f(unpack(a))) --> 3 4
...可变参数

可以使用...表示不明确数量的可变参数,在使用时在函数中以表的形式获取。

function Sum(a,b,c,...)
    local arg={...} --将可变参数存储到arg表中
    for key, value in pairs(arg) do
        print(value) -- 9 7 4 10
    end
end

Sum(5,4,8,9,7,4,10)

使用命名参数

由于lua使用弱类型的缘故,我们可以像这样使用命名参数,在这种情况下调用函数可以写为函数名{参数}的形式。

function Show(arg)
    print(arg.name..'  '..arg.title)
end

Show{name='小屋',title='学习lua'}

深入理解函数

Lua 中的函数是带有词法定界(lexical scoping)的第一类值(first-class values)。
第一类值指:在 Lua 中函数和其他值(数值、字符串)一样,函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值。

词法定界指:被嵌套的函数可以访问他外部函数中的变量。这一特性给 Lua 提供了强大的编程能力。

匿名函数

得益于上面的第一条规则,其实我们声明函数,本是 fac=function(arg) body end
这个语句创建了一个匿名函数,并把这个函数赋值为fac,我们就可以称这个函数为fac了。
table.sort()函数中使用匿名函数:

在函数中接收2个参数,第一个参数接收一个表,第二个参数接收一个比较函数

arr={
    {name='A',age=16},
    {name='B',age=20},
    {name='C',age=9},
}
table.sort(arr,function (a,b)
    return a.age>b.age
end)

for key, value in pairs(arr) do
    for k, v in pairs(value) do
        print(k,v)
    end
end

以其他函数作为参数的函数在 Lua 中被称作高级函数,高级函数在 Lua 中并没有特
权,只是 Lua 把函数当作第一类函数处理的一个简单的结果。

闭包

当一个函数内容嵌套另一个函数时,被嵌套的匿名函数还可以访问第一函数中的参数,这就被称为闭包;事实上仅有很少的语言支持这样的特性。
来看看下面这个例子:

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

在匿名函数中,我们依然可以直接正确的访问 i。


现在,假如我们这样调用这个函数结果是怎样呢?

local c1=NewCount()
local c2=NewCount()
print(c1())
print(c1())
print(c2())
print(c1())


可以发现,函数的结果并非我们的预期,在第一函数体中的i的值可以被保留下来,而且c1和c2的保存值并不相同,这就是闭包。
Ps:我们把这个i称为外部的局部变量或者 upvalue
简单的说闭包是一个函数加上它可以正确访问的 upvalues。如果我们再次调用 newCounter,将创建一个新的局部变量 i,因此我们得到了一个作用在新的变量 i 上的新闭包。

分析:c1和c2是作用同一个局部变量上的不同实例上的两个闭包。

使用闭包重写函数

闭包在完全不同的上下文中也是很有用途的。因为函数被存储在普通的变量内我们
可以很方便的重定义或者预定义函数。通常当你需要原始函数有一个新的实现时可以重
定义函数。例如你可以重定义 sin 使其接受一个度数而不是弧度作为参数:

local oldSin=math.sin
math.sin=function (x)
    return oldSin(x*math.pi/180)
end

通过这个特性我们可以对原函数进行修改,在某些时刻可以构建运行危险代码的沙盘。

局部函数

Lua中函数既可以作为全局变量也可以作为局部变量,函数作为table的域。
使用表声明函数

Lib={}
Lib.foo=function (x,y)
    return x+y
end
Lib.goo=function (x,y)
    return x-y
end

Lib={
    foo=function (x,y)
        return x+y
    end,
    goo=function (x,y)
        return x-y
    end
}

Lib={}
function Lib.foo(x,y)
    return x+y
end
function Lib.goo(x,y)
    return x-y
end

Ps:在使用递归函数时,要注意要先声明函数在调用。

尾调用

尾调用是一种类似在函数结尾的 goto 调用,当函数最后一个动作是调用另外一个函
数时,我们称这种调用尾调用。
Ps:Lua中尾调用不消耗栈空间,由于尾调用不需要使用栈空间,那么尾调用递归的层次可以无限制的