加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Lua基础 函数(一)

发布时间:2020-12-14 21:59:06 所属栏目:大数据 来源:网络整理
导读:?? ???? Lua基础 函数(一) 分类: Lua 2013-03-09 21:25 17527人阅读 评论(0) 收藏 举报 Lua 脚本语言 目录(?) [+] 多返回值 变参 带名字的参数 在Lua中,函数是对语句和表达式进行抽象的主要方法。既可以用来处理一些特殊的工作,也可以用来计算一些值。
??
????

Lua基础 函数(一)

分类: Lua 17527人阅读 评论(0) 收藏 举报
Lua 脚本语言

目录(?)[+]

  1. 多返回值
  2. 变参
  3. 带名字的参数

在Lua中,函数是对语句和表达式进行抽象的主要方法。既可以用来处理一些特殊的工作,也可以用来计算一些值。下面有3个例子,分别将函数当作一条语句;当作表达式(后面两个是一类)。

[plain] view plain copy print ?
  1. print(8*9,?9/8)??????????????????-->?72??1.125??
  2. a?=?math.sin(3)?+?math.cos(10)???-->?a?=?-0.69795152101659??
  3. print(os.date())?????????????????-->?Sat?Mar??9?12:14:08?2013??

函数如果带参数,那么就要用(arg1, arg2,...)括起来,如果没有参数,就写个空(),说明这是个函数调用。

特例,如果函数只有一个参数,并且参数类型是字符串或者table,那么()可以省略,如下示例:

[plain] view plain copy print ?
  1. print?"Hello?World"???<==>???print("Hello?World")??
  2. dofile?'a.lua'????????<==>???dofile('a.lua')??
  3. print[[a?multi-line???<==>???print([[a?multi-line??
  4. ?message]]????????????????????message]])??
  5. f{x=10,?y=20}?????????<==>???f({x=10,?y=20})??
  6. type{}????????????????<==>???type({})??

Lua支持面向对象,操作符为冒号‘:’。o:foo(x) <==> o.foo(o,x),这个在后面会专门写一篇文章。


Lua程序可以调用C语言或者Lua实现的函数。Lua基础库中的所有函数都是用C实现的。调用一个用C实现的函数,和调用一个用Lua实现的函数,二者没有任何区别。

Lua函数的定义语法比较常规,如下示例:

[plain] view plain copy print ?
  1. function?add(a)??
  2. ????local?sum?=?0??
  3. ????for?i,?v?in?ipairs(a)?do??
  4. ????????sum?=?sum?+?v??
  5. ????end??
  6. ????return?sum??
  7. end??


函数的参数跟局部变量一样,用传入的实参来初始化,多余的实参被丢弃,多余的形参初始化为nil。示例如下:

[plain] view plain copy print ?
  1. function?f(a,?b)?return?a?or?b?end??
  2. ??
  3. f(3)????????--?a=3,?b=nil??
  4. f(3,?4)?????--?a=3,?b=4??
  5. f(3,?4,?5)??--?a=3,?b=4?(5被丢弃)??

虽然Lua可以处理这样的情况,但是不鼓励这种传入错误数量参数的函数调用,可能会使程序运行时有点小问题。不过,有些情况下,这个特性可以加以利用,例如下面示例的默认参数:


1.多返回值

不同于常规函数,Lua的函数可以返回多个返回值。一些Lua中预定义的函数可以返回多个返回值。例如string.find函数,在string中匹配一个sub-string,string.find返回sub-string的起始位置和结束位置。利用多赋值语句来获取函数的多个返回值。


用Lua写的函数也可以返回多个返回值,如下示例,查找array中的最大值,并返回其位置和值


Lua会根据实际情况来使函数的返回值个数适应调用处的期望。

1)如果一个函数调用作为一条语句,所有的返回值都被丢弃

2)如果一个函数调用作为一个表达式,除了3)的情况,返回值只保留第一个

3)在多赋值,返回值作为实参来调用其他函数,table中,return语句中,这4种调用场景,如果函数调用作为其最后一个表达式,那么会保留所有的返回值,然后根据实际调用需要再纠正。

示例:

[plain] view plain copy print ?
  1. --?多赋值,函数调用是最后一个表达式??
  2. x,y?=?foo2()??????--?x="a",?y="b"??
  3. x?=?foo2()????????--?x="a",?"b"?is?discarded??
  4. x,y,z?=?10,foo2()?--?x=10,?y="a",?z="b"??
  5. x,y?=?foo0()??????--?x=nil,?y=nil??
  6. x,y?=?foo1()??????--?x="a",?y=nil??
  7. x,z?=?foo2()????--?x="a",?y="b",?z=nil??
  8. ??
  9. --?多赋值,函数调用不是最后一个表达式,因此返回值只保留第一个??
  10. x,y?=?foo2(),?20?????--?x="a",?y=20??
  11. x,y?=?foo0(),?20,?30?--?x=nil,?y=20,?30?is?discarded??
  12. ??
  13. --?返回值作为实参来调用其他函数??
  14. print(foo0())?????????-->??
  15. print(foo1())?????????-->?a??
  16. print(foo2())?????????-->?a?b??
  17. print(foo2(),?1)??????-->?a?1??
  18. print(1,?foo2())??????-->?1?a?b???
  19. print(foo2()?..?"x")??-->?ax?(see?next)??
  20. ??
  21. --?table中??
  22. t?=?{foo0()}?--?t?=?{}?(an?empty?table)??
  23. t?=?{foo1()}?--?t?=?{"a"}??
  24. t?=?{foo2()}?--?t?=?{"a",?"b"}??
  25. ??
  26. --?table中,但是函数调用不是最后一个表达式??
  27. t?=?{foo0(),?foo2(),?4}?--?t[1]?=?nil,?t[2]?=?"a",?t[3]?=?4??
  28. ??
  29. --?return语句中??
  30. function?foo?(i)??
  31. ????if?i?==?0?then?return?foo0()??
  32. ????elseif?i?==?1?then?return?foo1()??
  33. ????elseif?i?==?2?then?return?foo2()??
  34. ????end??
  35. end??
  36. ??
  37. print(foo(1))?-->?a??
  38. print(foo(2))?-->?a?b??
  39. print(foo(0))?--?(no?results)??
  40. print(foo(3))?--?(no?results)??

用括号来强制返回值个数为一个:

[plain] view plain copy print ?
  1. print((foo0()))?-->?nil??
  2. print((foo1()))?-->?a??
  3. print((foo2()))?-->?a??

因此,括号千万别乱用,尤其是return后的值,如果用了括号,那么就只返回一个值。


函数unpack可以返回多个值,它传入一个array,然后返回array中的每一个值。

[plain] view plain copy print ?
  1. print(unpack{10,30})?-->?10?20?30??
  2. a,b?=?unpack{10,30}?--?a=10,?b=20,?30?is?discarded??

unpack的一个重要用法是泛型调用,提供了比C语言中更大的灵活性。在Lua中,如果你想调用一个函数f,传入可变数量的参数,很简单,

[plain] view plain copy print ?
  1. f(unpack(a))??

unpack返回a中的所有值,并传给f?作为参数,下面示例:

[plain] view plain copy print ?
  1. f?=?string.find??
  2. a?=?{"hello",?"ll"}??
  3. print(f(unpack(a)))??-->?3?4??

Lua中的unpack是用C实现的。其实我们也可以用Lua来实现它

[plain] view plain copy print ?
  1. function?unpack?(t,?i)??
  2. ????i?=?i?or?1??
  3. ????if?t[i]?then??
  4. ????????return?t[i],?unpack(t,?i?+?1)??
  5. ????end??
  6. end??


2. 变参

Lua中的一些函数接受可变数量的参数,例如print函数。print函数是用C来实现的,但是我们也可以用Lua来实现变参函数。下面是一个示例:

[plain] view plain copy print ?
  1. function?add?(...)??
  2. ????local?s?=?0??
  3. ????for?i,?v?in?ipairs{...}?do??
  4. ????????s?=?s?+?v??
  5. ????end??
  6. ????return?s??
  7. end??
  8. print(add(3,?10,?25,?12))?-->?54??

参数列表中的'...'指明该函数可以接受变参。我们可以将‘...’当作一个表达式,或者一个多返回值的函数(返回当前函数的所有参数)。例如

[plain] view plain copy print ?
  1. local?a,?b?=?...??

用可选参数的前两个初始化局部变量a,b的值。再看下面的例子,

[plain] view plain copy print ?
  1. function?foo(a,?b,?c)???<==>???function?foo(...)?local?a,?c?=?...??


[plain] view plain copy print ?
  1. function?id?(...)?return?...?end??

上面这个函数简单地返回它所有的参数。下面的例子,说明了一个跟踪函数调用的技巧

[plain] view plain copy print ?
  1. function?foo1?(...)??
  2. ????print("calling?foo:",?...)??
  3. ????return?foo(...)??
  4. end??

再看一个实用的例子。Lua提供了不同的函数来格式化文本string.formant和写文本io.write,我们可以简单地将二者合二为一。

[plain] view plain copy print ?
  1. function?fwrite?(fmt,?...)??
  2. ????return?io.write(string.format(fmt,?...))??
  3. end??

注意有一个固定的参数fmt。变参函数可能含有不定数目的固定参数,后面再跟变参。Lua会将前面的实参赋值给这些固定参数,剩下的实参才能当作变参看待。下面是几个示例:

[plain] view plain copy print ?
  1. CALL??????????????????????????????PARAMETERS??
  2. fwrite()???????????????????????--?fmt?=?nil,?no?varargs??
  3. fwrite("a")????????????????????--?fmt?=?"a",?no?varargs??
  4. fwrite("%d%d",?5)???????????--?fmt?=?"%d%d",?varargs?=?4?and?5??

如果想要迭代处理变参,可以用{...}来将所有的变参收集到一个table中。但是有时变参中可能含有非法的nil,我们可以用select函数。select函数有一个固定的参数selector,然后跟一系列的变参。调用的时候,如果selector的值为数字n,那么select函数返回变参中的第n个参数,否则selector的值为'#',select函数会返回可变参数的总数目。下面示例:

[plain] view plain copy print ?
  1. for?i=1,?select('#',?...)?do??
  2. ????local?arg?=?select(i,?...)?????--?get?i-th?parameter??
  3. ????<loop?body>??
  4. end??

注意,select("#",...)返回变参的数目,包括nil在内。


3. 带名字的参数

Lua中函数的参数传递是基于位置的,当调用函数的时候,实参根据位置来匹配形参。但是,有的时候,根据名字来匹配更实用。例如,系统函数os.rename,我们会经常忘记新名字和旧名字哪个在前;为了解决这个问题,我们尝试重新定义这个函数。下面这个

[plain] view plain copy print ?
  1. --?invalid?code??
  2. rename(old="temp.lua",?new="temp1.lua")??

上面这个代码是非法的,Lua并不支持这样的语法。但是我们可以修改一点点,来实现相同的效果。

[plain] view plain copy print ?
  1. function?rename?(arg)??
  2. ????return?os.rename(arg.old,?arg.new)??
  3. end??

用这种方式来传递参数是很实用的,尤其是,当函数有多个参数,并且其中一些是可有可无时。例如,用GUI库创建一个新的窗口

[plain] view plain copy print ?
  1. w?=?Window{?x=0,?y=0,?width=300,?height=200,??
  2. ????????????title?=?"Lua",?background="blue",??
  3. ????????????border?=?true??
  4. ??????????}??

Window函数可以检查必须的参数,并且给可选参数赋予默认值等。假设_Window函数可以用来创建一个新窗口,但是它必须要全部的参数。那我们就可以重新定义一个Window函数如下:

[plain] view plain copy print ?
  1. function?Window?(options)??
  2. ????--?check?mandatory?options??
  3. ????if?type(options.title)?~=?"string"?then??
  4. ????????error("no?title")??
  5. ????elseif?type(options.width)?~=?"number"?then??
  6. ????????error("no?width")??
  7. ????elseif?type(options.height)?~=?"number"?then??
  8. ????????error("no?height")??
  9. ????end??
  10. ??????
  11. ????--?everything?else?is?optional??
  12. ????_Window(options.title,??
  13. ????????options.x?or?0,?????????????????????--?default?value??
  14. ????????options.y?or?0,?????????????????????--?default?value??
  15. ????????options.width,?options.height,??
  16. ????????options.background?or?"white",??????--?default??
  17. ????????options.border??????????????????????--?default?is?false?(nil)??
  18. ????????)??
  19. end??

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读