Lua中的函数(function)、可变参数、局部函数、尾递归优化等实例讲解

一、函数

在Lua中,函数是作为"第一类值"(First-Class Value),这表示函数可以存储在变量中,可以通过参数传递给其他函数,或者作为函数的返回值(类比C/C++中的函数指针),这种特性使Lua具有极大的灵活性。
 
Lua对函数式编程提供了良好的支持,可以支持嵌套函数。
 
另外,Lua既可以调用Lua编写的函数,还可以调用C语言编写的函数(Lua所有的标准库都是C语言写的)。
 
定义一个函数

代码如下:

function hello()
print('hello')
end

hello函数不接收参数,调用:hello(),虽然hello不接收参数,但是还可以可以传入参数:hello(32)
 
另外如果只传递一个参数可以简化成functionname arg的调用形式(注意数值不行)

代码如下:

> hello '3'
hello
> hello {}
hello
> hello 3
stdin:1: syntax error near '3'

另外对变量名也不适用

代码如下:

> a = 21
> print a
stdin:1: syntax error near 'a'

另外,Lua函数不支持参数默认值,可以使用or非常方便的解决(类似Javascript)

代码如下:

> function f(n)
>> n = n or 0
>> print(n)
>> end
> f()
0
> f(1)
1

Lua支持返回多个值,形式上非常类似Python:

代码如下:

> function f()
>> return 1,2,3
>> end
> a,b,c = f()
> print(a .. b .. c)
123

函数调用的返回值可以用于table:

代码如下:

> t = {f()}
> print(t[1], t[2], t[3])
1        2        3

可见,f()返回的三个值分别称为table的3个元素,但是情况并不总是如此:

代码如下:

> t = {f(), 4}
> print(t[1], t[2], t[3])
1        4        nil

这次,f()返回的1,2,3只有1称为table的元素;

代码如下:

> t = {f(), f()}
> print(t[1], t[2], t[3], t[4], t[5])
1        1        2        3        nil

总之:只有最后一项会完整的使用所有返回值(假如是函数调用)。
 
对于无返回值的函数,可以使用(f())的形式强行返回一个值(nil)

代码如下:

> function g()
>> end
> print(g())
 
> print((g()))
nil

实际上,(f())形式的调用返回一个且只返回一个值

代码如下:

> print((f()))
1
> print(f())
1        2        3

二、变长参数

Lua支持编程参数,使用简单(借助于table、多重赋值)

代码如下:

> function f(...)
for k,v in ipairs({...}) do
print(k,v)
end
end
> f(2,3,3)
1        2
2        3
3        3

使用多重赋值的方式

代码如下:

> function sum3(...)
>> a,b,c = ...
>> a = a or 0
>> b = b or 0
>> c = c or 0
>> return a + b +c
>> end
> =sum3(1,2,3,4)
6
> return sum3(1,2)
3

通常在遍历变长参数的时候只需要使用{…},然而变长参数可能会包含一些nil;那么就可以用select函数来访问变长参数了:select('#', …)或者 select(n, …)

select('#', …)返回可变参数的长度,select(n,…)用于访问n到select('#',…)的参数

代码如下:

> =select('#', 1,2,3)
3
> return select('#', 1,2, nil,3)
4
> =select(3, 1,2, nil,3)
nil        3
> =select(2, 1,2, nil,3)
2        nil        3

注意:Lua5.0中没有提供…表达式,而是通过一个隐含的局部table变量arg来接收所有的变长参数,arg.n表示参数的个数;

三、函数式编程

函数做一个First-Class Value可以赋值给变量,用后者进行调用

代码如下:

> a = function() print 'hello' end
> a()
hello
> b = a
> b()
hello

匿名函数

代码如下:

> g = function() return function() print 'hello' end end
> g()()
hello

函数g返回一个匿名函数;
 
闭包是函数式编程的一种重要特性,Lua也支持

代码如下:

> g = function(a) return function() print('hello'.. a); a = a + 1 end end
> f = g(3)
> f()
hello3
> f()
hello4

四、局部函数

局部函数可以理解为在当前作用域有效的函数,可以用local变量来引用一个函数:

代码如下:

> do
>> local lf = function() print 'hello' end
>> lf()
>> end
hello
> lf()
stdin:1: attempt to call global 'lf' (a nil value)
stack traceback:
stdin:1: in main chunk
[C]: in ?

需要注意的是,对于递归函数的处理

代码如下:

> do
local lf = function(n)
if n <= 0 then
return
end
print 'hello'
n = n -1
lf(n)
end
lf(3)
end
hello
stdin:8: attempt to call global 'lf' (a nil value)
stack traceback:
stdin:8: in function 'lf'
stdin:9: in main chunk
[C]: in ?

而应该首先声明local lf, 在进行赋值

代码如下:

do
local lf;
lf = function(n)
if n <= 0 then
return
end
print 'hello'
n = n -1
lf(n)
end
lf(3)
end
hello
hello
hello

Lua支持一种local function(…) … end的定义形式:

代码如下:

> do
local function lf(n)
if n <= 0 then
return
end
print 'hello'
n = n -1
lf(n)
end
lf(3)
end
hello
hello
hello
> lf(3)
stdin:1: attempt to call global 'lf' (a nil value)
stack traceback:
stdin:1: in main chunk
[C]: in ?

五、尾调用

所谓尾调用,就是一个函数返回另一个函数的返回值:

代码如下:

function f()

return g()
end

因为调用g()后,f()中不再执行任何代码,所以不需要保留f()的调用桟信息;Lua做了这样的优化,称为"尾调用消除",g()返回后,控制点直接返回到调用f()的地方。
 
这种优化对尾递归非常有益,通常递归意味着调用桟的不断增长,甚至可能造成堆栈溢出;而尾递归提供了优化条件,编译器可以优化掉调用桟。
 
下面的递归函数没有使用尾递归,而参数为大数时,堆栈溢出:

代码如下:

> function f(n)
>> if n <= 0 then
>> return 0
>> end
>> a = f(n-1)
>> return n * a
>> end
> f(10000000000)
stdin:5: stack overflow
stack traceback:
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
...
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:5: in function 'f'
stdin:1: in main chunk
[C]: in ?

优化为尾递归

代码如下:

function f(n, now)
if n <= 0 then
return now
end
 
return f(n-1, now*n)
end
f(10000000000, 1)

运行n久也无堆栈溢出;

(0)

相关推荐

  • Lua中的函数知识总结

    前言 Lua中的函数和C++中的函数的含义是一致的,Lua中的函数格式如下: 复制代码 代码如下: function MyFunc(param)      -- Do something end 在调用函数时,也需要将对应的参数放在一对圆括号中,即使调用函数时没有参数,也必须写出一对空括号.对于这个规则只有一种特殊的例外情况:一个函数若只有一个参数,并且此参数是一个字符串或table构造式,那么圆括号便可以省略掉.看以下代码: 复制代码 代码如下: print "Hello World"

  • Lua面向对象之多重继承、私密性详解

    在Lua中的多重继承和私密性可能用得比较少,也可能只是我个人用得比较少. 本来想偷懒不写这文章的,因为我今天刚买了个漂移板,连起步都还没学会啊,想多学一会. 咳咳,本着坚持不懈.负责到底的态度,我还是决定随便写几句~(小若:随便写几句是几吨意思啊?!) 1.多重继承之在多个类中查找一个字段 我发现这些高(shen)智(jing)商(bing)人群真的很厉害,这种技巧都能想到,很佩服. 其实多重继承没什么特别的,除非两个将要被继承的类有相同的函数名和属性,否则,处理起来很简单.   无非就是在多个

  • Lua的table库函数insert、remove、concat、sort详细介绍

    函数列表: table.insert(table,[ pos,] value) table.remove(table[, pos]) table.concat(table[, sep[, i[, j]]]) table.sort(table[, comp]) 1. insert 和 remove 只能用于数组元素的插入和移出, 进行插入和移出时,会将后面的元素对齐起来. 所以在 for 循环中进行 insert 和 remove 的时候要注意插入和移除时是否漏掉了某些项:   复制代码 代码如下

  • Lua中的常用函数库汇总

    lua库函数 这些函数都是Lua编程语言的一部分, 点击这里了解更多. assert(value) - 检查一个值是否为非nil, 若不是则(如果在wow.exe打开调试命令)显示对话框以及输出错误调试信息 collectgarbage() - 垃圾收集器. (新增于1.10.1) date(format, time) - 返回当前用户机器上的时间. error("error message",level) - 发生错误时,输出一条定义的错误信息.使用pcall() (见下面)捕捉错误

  • Lua中的面向对象编程详解

    简单说说Lua中的面向对象 Lua中的table就是一种对象,看以下一段简单的代码: 复制代码 代码如下: local tb1 = {a = 1, b = 2} local tb2 = {a = 1, b = 2} local tb3 = tb1   if tb1 == tb2 then      print("tb1 == tb2") else      print("tb1 ~= tb2") end   tb3.a = 3 print(tb1.a) 上述代码会输

  • Lua中的一些常用函数库实例讲解

    前言 这篇文章将会来一些比较轻松的内容,就是简单的介绍一下Lua中几个常用的库.简单的说就是几个API的介绍.所以说,看起来比较容易,也没有多大的分量.就是纯粹的总结.使用库就是为了方便我们的开发,提高开发效率,同时也能保证代码的质量.希望大家以后也不要重复造轮子了. 数学库 数学库(math)由一组标准的数学函数构成.这里主要介绍几个常用的函数,其它的大家可以自行百度解决. 三角函数(sin,cos,tan--) 所有的三角函数都使用弧度单位,可以用函数deg(角度)和rad(弧度)来转换角度

  • Lua面向对象之类和继承

    终于来了,在Lua中的面向对象编程,相信目前学习Lua的大部分人都是为了开发手机网游吧. 而且基本都是奔着脚本语言的热更新特性去的,所以全脚本开发变得十分流行. 对于普及不太广的Lua(相对于C++.Java等主流语言),需要短时间上手开发游戏,对新手而言不算简单. 所以大家才更习惯于继续用面向对象思想去折腾Lua吧~ 好了,不唠叨了,我最不喜欢唠叨了.(小若:是是是,你一点都不唠叨,赶紧开讲!) 1.类的对象 至于如何创建一个类,大家已经很清楚了,就是一个table而已. 那么,要使用这个类去

  • Lua面向对象编程学习笔记

    其实 Lua 中的 table 是一种对象,因为它跟对象一样,有其自己的操作方法: 复制代码 代码如下: Role = { hp = 100 } function Role.addHp(hp)     Role.hp = Role.hp + hp end   Role.addHp(50) print(Role.hp) 上面代码创建了一个名为 Role 对象,并有一个 addHp 的方法,执行 "Role.addHp" 便可调用 addHp 方法. 不过上面对象 Role 是以全局变量的

  • Lua字符串库中的几个重点函数介绍

    在<Lua中的一些库>中也说到了,要对string库的模式匹配进行单独的讲解.对于字符串的处理,对于任何语言的学习来说,都是一个难点,而且也是一个必会的知识点.给你一个字符串,让你按照某种需求进行处理,你不会,那是多么尴尬的一件事情.所以,看完<Lua中的一些库>和这篇文章之后,我争取做到让你在处理字符串时,不再感到捉襟见肘,不再尴尬. 说到Lua中的模式匹配,基本上就是围绕着以下几个函数展开的: 1.find: 2.match: 3.gsub: 4.gmatch. 我的总结也就是

  • Lua中的模块与module函数详解

    很快就要开始介绍Lua里的"面向对象"了,在此之前,我们先来了解一下Lua的模块. 1.编写一个简单的模块 Lua的模块是什么东西呢?通常我们可以理解为是一个table,这个table里有一些变量.一些函数- 等等,这不就是我们所熟悉的类吗? 没错,和类很像(实际上我说不出它们的区别).   我们来看看一个简单的模块,新建一个文件,命名为game.lua,代码如下: 复制代码 代码如下: game = {} function game.play()     print("那么

  • Lua中的string库(字符串函数库)总结

    Lua解释器对字符串的支持很有限.一个程序可以创建字符串并连接字符串,但不能截取子串,检查字符串的大小,检测字符串的内容.在Lua中操纵字符串的功能基本来自于string库. 字符串库中的一些函数是非常简单的: string.len(s)          返回字符串s的长度: string.rep(s, n)      返回重复n次字符串s的串:你使用string.rep("a", 2^20)可以创建一个1M bytes的字符串(比如,为了测试需要): string.lower(s)

  • Lua中函数与面向对象编程的基础知识整理

    函数 1. 基础知识 调用函数都需要写圆括号,即使没有参数,但有一种特殊例外:函数若只有一个参数且参数是字面字符串或table构造式,则圆括号可有可无,如dofile 'a.lua',f{x=10, y=20}. Lua为面向对象式的调用提供冒号操作符的特殊语法,如o.foo(o, x)等价于o:foo(x).和Javascript类似,调用函数时提供的实参数量可以与形参数量不同,若实参多了则舍弃,不足则多余的形参初始化为nil. 1.1 多重返回值 Lua允许函数返回多个结果,函数返回如ret

随机推荐