LUA学习(四)函数

来源:互联网 发布:萨达姆 知乎 编辑:程序博客网 时间:2024/06/12 22:32

基础知识:

lua中函数主要有两种作用:

1.完成指定的任务,这种情况下函数作为调用语句使用;
2.计算并返回值,这种情况下函数作为赋值语句的表达式使用。

函数的定义形式如下:

optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)function_bodyreturn result_params_comma_separatedend

其中:

optional_function_scope表示可选的局部函数标志,如果该标记未设定,表明该函数为全局函数,如果该标记被设置为local,表明该函数为局部函数。

argument等为参数;

result_params_comma_separated表示返回值序列,需要注意的一点是,lua支持函数返回多个值。就像多值赋值一样,如果要接收多个返回值,需要多个变量来接收。如果变量数量不够,则多余的返回值被丢弃。如果变量数量多于返回值数量,多余的变量会被设置为nil。返回值序列中,多个值之间以逗号分隔开。

例子如下:

function max(num1, num2)   if (num1 > num2) then      result = num1;   else      result = num2;   end   return result; endprint("两值比较最大值为 ",max(10,4))     --> 10print("两值比较最大值为 ",max(5,6))<span style="white-space:pre"></span>--> 6

注意:如果一个函数的参数列表是空的话,调用该函数时必须像C语言一样,在后面加上(),表明将要调用该函数。如:

function print_test()print("hello world")endprint_test()--print_test   --这样不加括号的调用不能被正确编译 
注意:如果一个函数只有一个参数,并且这个参数是字符串或是表结构的话,在调用该函数时,可以不用加括号。如:

print "hello world"a = {1,2,3,4}print(a)print a --不可以是代表表结构的变量,不能编译通过print {1, 2, 3, 4} -- 必须是这种参数直接为表结构的方式
另外,函数的实参可以传入随意个数,并不影响函数的调用,但是能否执行成功,需要函数自己做检查。如果实参的个数多于形参,多余的实参会被舍弃;如果实参的个数少于形参,多余的形参会被置为nil,如:

function f(a, b)return a, bendf(3)-- a = 3, b = nilf(3, 4)-- a = 3, b = 4f(3, 4, 5)-- a = 3, b = 4, 5被舍弃

多返回值得例子如下:

function f()return 3, 4enda = f()print(a)<span style="white-space:pre"></span>-- a = 3, 4被丢弃a, b = f()print(a, b)<span style="white-space:pre"></span>-- a = 3, b = 4a, b, c =f()print(a, b, c)  -- a = 3, b = 4, c = nil


注意:对于有多个返回值的函数,如果该函数不是在表达式的最尾被调用的话,它只返回第一个值。如:

function f()return 3, 4enda, b = f()print(a, b)-- 3, 4a, b = f(), 20print(a, b)-- 3, 20a, b, c = 30, f(), 20print(a, b, c)  -- 30, 3, 20
函数在作为函数参数、表元素时,也是同样的。如:

function f()return 3, 4endprint(1, f())            --> 1, 3, 4 print(f())            --> 3, 4  print(f(), 1)         --> 3, 1 print(f() .. "x")    --> 3x  a = {1, f()}print(a[1], a[2], a[3], a[4])-->1, 3, 4, nila = {f(), 2}print(a[1], a[2], a[3], a[4])-->3, 2, nil, nila = {f()}print(a[1], a[2], a[3], a[4])-->3, 4, nil, nil

另外,如果函数是return语句的元素的话,return的返回值,就是f()的返回值。如
function f()return 3, 4endfunction t()return f()endprint(t())-->3, 4

如果需要函数只返回一个值,可以使用括号,来使函数只返回一个值。如:

function f()return 3, 4endprint((f()))-->3

unpack()函数:

unpack()函数是lua中的一个特殊函数,它接收一个数组变量,返回数组的所有元素,该函数多用来协助实现可变参数的函数。在5.2版本以前的lua中,该函数作为一个全局函数存在的,可以随意调用。在5.2以后的版本中,该函数被移动到table库下面了,需要使用table.unpack()调用。如:

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



可变参数:

Lua  函数可以接受可变数目的参数,和 C  语言类似在函数参数列表中使用三点(...) 表示函数有可变的参数。

5.1版本之前的Lua 将函数的参数放在一个叫 arg 的表中,除了参数以外,arg表中还有一个域 n 表示参数的个数。但是在5.2版本之后的lua就不会帮助组织arg数组了,必须由开发者自己组织。如:

function average(...)   result = 0   local arg={...}--5.2以后的lua版本必须显示指明,否则arg为nil,而且没有了表示个数的n变量   for i,v in ipairs(arg) do      result = result + v   end   print("总共传入 " .. #arg .. " 个数")   print(arg[n])   return result/#argendprint("平均值为",average(10,5,3,4,5,6))

Lua 中的函数是带有词法定界(lexical scoping)的第一类值(first-class values)。

 第一类值指:在 Lua 中函数和其他值(数值、字符串)一样,函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值。

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

在lua中,函数可以是匿名的,如:

a = function (x) return x endprint(a(5))
这两句中将a定义成一个function类型的变量,它的值为一个函数。在某些方面,a的作用,与C语言中的函数指针比较类似。

这也是lua中最原始的function类型变量的定义方式,不过,我们常常写成下面的这种方式:

function a(x) return x endprint(a(5))
这种方式与其他语言对函数的定义比较相似,更容易接受。

lua中函数还可以作为另一个函数的参数使用,这也与C语言中的函数指针类似。唯一的不同是,由于lua支持词法定界,可以在传参时直接定意思匿名函数作为实参传递。如:

table.sort(network,function (a,b)     return (a.name > b.name)  end)

sort函数对network数组进行排列,排列的标准以第二个参数为准。第二个参数为函数,提供了排序的参考标准。这个地方并没有像C那样传递函数指针,而是直接定义了一个匿名函数进行传递。


闭合函数:

闭合函数也称为闭包。

当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界。

由于lua支持词法定界和第一类值,lua的函数,可以访问本来不属于它的局部变量,并维持这个变量。lua的函数与它可以访问的局部变量,就组成了一个闭合函数。对于外部函数中的局部变量,闭合函数会自动维持这个值。如果不再次调用外部函数,则闭合函数中外部的局部变量不会被再次初始化。如:

function count()    local i = 0;    return function()              i = i + 1;              return i;           endenda = count();print(a());-->1print(a());-->2print(a());-->3a = count()print(a());-->1


在以上的例子中,local i 是一个局部变量,它属于count()函数。但它也并不是普通的局部变量,它对于返回函数来说,是一个外部变量。i和返回的函数在一起,就组成了一个闭合函数。每次调用count()时,都会调用这个闭合函数。虽然在count()函数中,i会重新初始化,但是在闭合函数中,它自动维护的i值是不会再次初始化的,所以每次调用时闭合函数的返回值是依次加1的。当将count()函数重新赋值给新的function类型变量时,再调用这个新的变量时,又会创建一个新的闭合函数,会再次初始化外部的局部变量。于是,就得到了上面的那个结果。


非全局函数:

lua中,非全局函数的定义就跟普通变量的定义一样,如:

local aa = function (x) return x endprint(a(3)) --> 3
它的定义方式等价于以下方式:

local function a(x)return xendprint(a(3)) --> 3



尾调用:

当一个函数的最后一个动作,是调用另一个函数时,这样的函数调用,就属于尾调用。类似于C语言的goto语句。

使用了尾调用的函数会在结束时直接进行函数调用,因为调用后不用再对本函数进行任何动作,所以不用在堆栈中保留任何本函数的信息。这样的函数可以支持无限深度的递归类函数。如:

function foo(n)    if n > 0 then        return foo(n - 1);    else        return "end";    endendprint(foo(9999999))
这个函数在很短的时间内,就会打印“end”字符串。

注意:如果在return语句中,包含函数调用和其他表达式,或者是函数调用结束后,才进行return的话,就不是真正的尾调用,它在每次函数调用之前,都需要在堆栈中保存本函数的信息。如果频繁的进行调用,会产生堆栈错误。如:

function foo(n)    if n > 0 then        return foo(n - 1) + 1;     else        return "end";    endendprint(foo(9999999))

或者是:

function foo(n)    if n > 0 thenfoo(n - 1)        return ;    else        return "end";    endend


该函数执行时,会出错,调试堆栈溢出。("stack overflow")。










0 0