当前位置: 首页 > 编程笔记 >

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

华峰
2023-03-14
本文向大家介绍Lua中的函数(function)、可变参数、局部函数、尾递归优化等实例讲解,包括了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久也无堆栈溢出;

 类似资料:
  • 我有一个家庭作业问题,它给出了一个递归函数,我必须使用尾部递归来实现它。函数为f(0)=1 f(n)=1 2*f(n-1) 我并不擅长尾部递归,我试着查找示例,但我发现的都是没有斐波那契序列的示例,这没有多大帮助。 我真正拥有的是 我知道尾递归基本上每次调用都计算函数,我只是不知道如何实现它。 编辑:我打了一个错字f(n)应该是1 2*f(n-1)

  • 假设我编写这样的代码: 我如何让Kotlin优化这些相互递归的函数,以便在不抛出StackOverflower错误的情况下运行main?tailrec关键字适用于单函数递归,但没有更复杂的功能。我还看到一个警告,在使用关键字tailrec的地方没有找到尾部调用。也许这对编译器来说太难了?

  • 我一直在关注这一点,我试图获得一些将普通递归函数转换为尾部递归函数的实践。我设法理解斐波那契和阶乘的版本,但这一个难住了我。我理解算法在做什么,以及在转换中让我困惑的else语句。 在else中,它试图找到一个更接近你想要的数字,然后放弃,并继续使用它找到的数字,该数字低于你建议的数字。 我不知道如何编写使这个尾部递归的辅助函数。对于斐波那契和阶乘,我最终使用了累加器。有没有类似的东西可以在这里使

  • 各种各样的书籍、文章、博客帖子表明,将递归函数重写为尾部递归函数可以加快速度。毫无疑问,对于生成斐波那契数或计算阶乘等琐碎情况,它会更快。在这种情况下,有一种典型的重写方法,即使用“辅助函数”和用于中间结果的附加参数。 尾部递归很好地描述了尾部递归函数和非尾部递归函数之间的差异,以及如何将递归函数转换为尾部递归函数。对于这种重写来说什么是重要的-函数调用的数量是相同的(重写之前/之后),不同之处在

  • 本文向大家介绍Lua的函数环境、包实例讲解,包括了Lua的函数环境、包实例讲解的使用技巧和注意事项,需要的朋友参考一下 定义:函数环境就是函数在执行时所见的全局变量的集合,以一个表来承载。 说明:每个函数都可以有自己的环境,可以通过setfenv来显示的指定一个函数的环境。如果不显示的指定,函数的环境缺省为定义该函数的函数的环境。在前面的代码中,函数foo的缺省环境里没有定义变量g,因此第一次执行

  • 本文向大家介绍js的函数的按值传递参数(实例讲解),包括了js的函数的按值传递参数(实例讲解)的使用技巧和注意事项,需要的朋友参考一下 js的函数传参的方式是按值传递,正常情况下,改变函数参数的值,并不会对函数外部的变量造成影响。例如: 这是因为js的函数在接收参数时,会生成一个副本变量,该副本变量等于参数的值,可以分析js这样运行的: 但是当函数的参数传递的是一个对象呢? 发现函数内部居然改变了