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

一、函数

在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久也无堆栈溢出;

时间: 2024-11-09 02:17:47

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

可变参数的函数的原理

原文地址: 可变参数. 1:必须有一个提前参数,(即:...之前必须要有一个参数),用以计算出后面的第一个未知参数的地址. 知道了第一个未知参数的地址之后, 就可以根据fmt格式化串,可以依次计算出剩余的参数地址. sprintf()的原型:sprintf(char* buffer, const char* fmt, ... ) ,其中,fmt就是提前参数 2:每一个可变阐述函数,其编写者与使用者 都要有一个参数的使用约定.不然,会乱套. 3:可变函数实现的技术基础1:所有参数,在汇编级别,其大

c/c++支持可变参数的函数

一.为什么要使用可变参数的函数? 一般我们编程的时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数.但在某些情况下希望函数的参数个数可以根据需要确定,因此c语言引入可变参数函数.这也是c功能强大的一个方面,其它某些语言,比如fortran就没有这个功能. 典型的可变参数函数的例子有大家熟悉的printf().scanf()等. 二.c/c++如何实现可变参数的函数? 为了支持可变参数函数,C语言引入新的调用协议, 即C语言调用约定 __cdecl . 采用C/

C语言利用va_list、va_start、va_end、va_arg宏定义可变参数的函数

在定义可变参数的函数之前,先来理解一下函数参数的传递原理: 1.函数参数是以栈这种数据结构来存取的,在函数参数列表中,从右至左依次入栈. 2.参数的内存存放格式:参数的内存地址存放在内存的堆栈段中,在执行函数的时候,从最后一个(最右边)参数开始入栈.因此栈底高地址,栈顶低地址,举个例子说明一下: void test(int a, float b, char c); 那么,在调用test函数的时候,实参char c先进栈,然后是float b,最后才是int a,因此在内存中变量的存放次序是c->

JS与PHP向函数传递可变参数的区别实例代码_php技巧

# JS 调用函数传递可变参数的方法 复制代码 代码如下: <script> function test() { for(var i = 0;i < arguments.length; i++) { alert(arguments[i]); } } //调用函数 test(1, 2, 3, 'abc'); </script> # PHP 调用函数传递可变参数的方法 复制代码 代码如下: <?php //方法一 //接收一系列参数,并逐一输出 function show_

非递归二叉树遍历-c语言中函数指针作为参数与函数的嵌套

问题描述 c语言中函数指针作为参数与函数的嵌套 函数指针作为另一函数的参数和函数的嵌套的区别,感觉都是调用,有什么不一样呢?他们都适用在什么情况下!(我是在学非递归遍历二叉树时看到的) Status Visit(TElemType e){ printf("%cn",e); return OK; } Status InOrderTraverse(BiTree T ,Status(*Visit)(TElemType e)){ SqStack S; InitStack(S); Push(S,

简单谈谈Python中函数的可变参数_python

前言 在Python中定义函数,可以用必选参数.默认参数.可变参数和关键字参数,这4种参数都可以一起使用,或者只用其中某些,但是请注意,参数定义的顺序必须是:必选参数.默认参数.可变参数和关键字参数. 可变参数( * ) 可变参数,顾名思义,它的参数是可变的,比如列表.字典等.如果我们需要函数处理可变数量参数的时候,就可以使用可变参数. 我们在查看很多Python源码时,经常会看到 某函数(*参数1, **参数2)这样的函数定义,这个*参数和**参数就是可变参数,一时会让人有点费解.其实只要把函

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

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

C++函数的可变参数详谈

可变参数的英文表示为:variable argument. 它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔. 可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不 定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有 实际的名称与之相对应. 由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间. 然而,更多地自由,同样也加大操作上的难度. 以

Lua教程(四):在Lua中调用C语言、C++的函数_Lua

本教程将介绍如何在Lua里面调用c/c++函数. 在Lua里面调用c/c++函数其实是比较简单,本文将通过两个示例演示具体的做法:一个是求平均数,另一个是打印lua函数的一些参数信息. 最后,本文会介绍如何把这两个函数定义成一个模块,这样lua代码里面就可以不再使用全局的名字空间了. 前言 当我们需要在Lua里面调用c/c++函数时,所有的函数都必须满足以下函数签名: 复制代码 代码如下: typedef int (*lua_CFunction) (lua_State *L); 换句话说,所有的