Lua 函数、闭包、尾调用总结

《lua 程序设计》在线阅读:http://book.luaer.cn/

1.函数

函数有两种用途:

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

1.1 语法

function func_name (arguments-list)
    statements-list;
end;

示例

function foo (x) return 2*x end
foo = function (x) return 2*x end

从上面我们可以看出lua函数定义实际上是一个赋值语句,将类型为function的变量赋给一个变量,需注意:

  • 调用函数的时候,如果参数列表为空,必须使用()表明是函数调用。
  • 当函数只有一个参数并且这个参数是字符串或者表构造的时候,()可有可无,例如
print "Hello World"      <-->       print("Hello World")

1.2 返回值

lua函数可以返回多个值

function foo0 () end                   -- returns no results
function foo1 () return 'a' end        -- returns 1 result
function foo2 () return 'a','b' end    -- returns 2 results

需注意返回多个值是:

  1. 多返回值的函数必须是作为表达式最后一个参数,否则只返回一个值
  2. 接收的话不足补nil,超出舍去,如
x,y = foo2()             -- x='a', y='b'
x,y = foo1();            -- x = 'a', y= nil
x = foo2()               -- x='a', 'b'舍去
x,y = foo2(), 20         -- x='a', y=20

另外,括号可以强制返回一个值

print((foo2()))      --> a

1.3 可变参数

  • 可变参数用...来表示
  • Lua将函数的可变参数放在一个叫arg的表中,除了参数以外,arg表中还有一个域n表示参数的个数。
function fn(name,nick,...)
    print(name,nick)      -- 一撮毛  大帅锅
  for i,v in ipairs(arg) do
       print(arg[i])      -- 1,2,3,4
    end
end
fn("毛毛","大帅锅",1,2,3,4);  -- name="毛毛",nick="大帅锅",arg={1,2,3,4; n = 4}

2.函数闭包

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

先看如下代码:

function fn()
    local i = 0
    return function()     -- 注意这里是返回函数的地址,不是执行
       i = i + 1
        return i
    end
end

c1 = fn()           -- 接收函数返回的地址
print(c1())  --> 1          --c1()才表示执行
print(c1())  --> 2

如上,调用c1()时,fn函数明显已经返回,lua闭包闭包思想正确处理这种情况:

  • 我们称i为从c1的外部局部变量(external local variable)或者upvalue。
  • 简单的说,闭包是一个函数以及它的upvalues

如果我们再次调用fn,将创建一个新的局部变量i:

c2 = fn()
print(c2())  --> 1
print(c1())  --> 3
print(c2())  --> 2

3.函数尾调用

  • 尾调用是一种类似在函数结尾的goto调用。
  • 当函数最后一个动作是调用另外一个函数时,我们称这种调用尾调用。

例如:

function f(x)
    return g(x)  -- 类似于goto g(x)函数的地址
end
  • 尾调用不需要使用栈空间,因此尾调用递归的层次可以无限制的。

例如下面调用不论n为何值不会导致栈溢出。

function foo (n)
    if n > 0 then return foo(n - 1) end
end

需要注意的是:必须明确什么是尾调用。

一些调用者函数调用其他函数后也没有做其他的事情但不属于尾调用。比如:

function f (x)
    g(x)
    return
end

上面这个例子中f在调用g后,不得不丢弃g地返回值,所以不是尾调用,同样的下面几个例子也不时尾调用:

return g(x) + 1      -- 还需+1
return x or g(x)     -- 还需比较
return (g(x))        -- 还需调整为一个返回值
时间: 2024-10-09 13:07:39

Lua 函数、闭包、尾调用总结的相关文章

Lua程序设计 函数 正确的尾调用

Lua中的"尾调用"就是一种类似于goto的函数调用,当一个函数调用是另一个函数的最后一个动作时,该调用才算是一条"尾调用".[一个函数在调用完另一个函数之后,是否就无其他事情需要做了] function f(x) return g(x) end   也就是说,当f调用完g之后,f所代表的整个函数的调用才算执行完成也就无其他事情可做了.因此,这种情况中,程序就不需要返回那个"尾调用"所在的函数了. 所以在"尾调用"之后,程序也

Lua进阶(一)——函数闭包、元表

函数闭包 function createCountdownTimer(second) local ms=second * 1000; local function countDown() ms = ms - 1; return ms; end return countDown; end timer1 = createCountdownTimer(1); for i=1,3 do print(timer1()); end print("------------"); timer2 = c

栈溢出 缓冲区溢出的一种 缓冲区以外的存储单元被改写 优化方法之一:尾调用

个人:尾调用时函数式编程的一个重要概念, 栈溢出: 函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置和内部变量等信息.如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧.等到B运行结束,将结果返回到A,B的调用帧才会消失.如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推.所有的调用帧,就形成一个"调用栈"(call stack).递归调用非常耗内存. 尾调用: 就是指某

尾调用及递归优化

ES6 规范中添加了对尾调用优化的支持,虽然目前来看支持情况还不是很好,但了解其原理还是很有必要的,有助于我们编写高效的代码,一旦哪天引擎支持该优化了,将会收获性能上的提升. 讨论尾调用前,先看函数正常调用时其形成的堆栈(stack frame)情况. 函数的调用及调用堆栈 先看一个概念:调用堆栈(call stack),或叫调用帧(stack frame). 我理解的调用堆栈:因为函数调用后,流程会进入到被调用函数体,此时需要传入一些入参,这些入参需要存放到一个位置,另外当函数调用结束后,执行

lua报错,看到报错信息有tail call,以为和尾调用有关,于是查了一下相关知识

尾调用是指在函数return时直接将被调函数的返回值作为调用函数的返回值返回,尾调用在很多语言中都可以被编译器优化, 基本都是直接复用旧的执行栈, 不用再创建新的栈帧, 原理上其实也很简单, 因为尾调用在本质上看的话,是整个子过程调用的最后执行语句, 所以之前的栈帧的内容已经不再需要, 完全可以被复用.报错的回溯日记,因为旧的执行栈已经没了,所以报错日记只显示(tail call).一般调用栈的长度为1M到2M,保存了调用过程中的参数和相关环境,如果递归调用太长,就会溢出.尾调用就能解决递归函数

php闭包实现函数的自调用,也是递归

php的闭包可能不常用,但是在某些场合之下还是可以考虑用php的闭包来实现某些功能的,比如递归,这里讲一下用php的闭包实现递归 //php闭包实现函数的自调用,也就是实现递归 function closure($n,$counter,$max){ //匿名函数,这里函数的参数加&符号是,引址调用参数自己 $fn = function (&$n,&$counter,&$max=1) use(&$fn){//use参数传递的是函数闭包函数自身 $n++; if($n

C语言调用Lua函数

记得上学时,初中英文课本中,上网叫做surfing the internet,中文叫网上冲浪.那个时期,人们经常称互联网为赛博空间.如今工作了,大量的零碎时间用于上微博,知乎,QQ,这些碎片化的阅读让人读起来轻松,也能获取些粗浅的信息.然而它们是消耗时间的黑洞,时间就这样一分一秒地飞逝,年末的时候,知乎会告诉你回答了多少问题,阅读了相当于一部<红楼梦>那么多的文字.只是当你静下来一想,这些浅阅读并没给你带来有深度,系统的知识.在你的时间线上,两条相邻信息往往是八竿子也打不着的.而且你还时不时去

C中调用Lua函数

我们先来看一个简单的例子: lua_State* L = NULL; // 内部调用lua函数 double f(double x, double y) { double z; lua_getglobal(L, "f"); // 获取lua函数f lua_pushnumber(L, x); // 压入参数x和y lua_pushnumber(L, y); if(lua_pcall(L, 2, 1, 0) != 0) error(L, "error running functi

cocos2d-x 3.0 在C++中调用lua函数(2)

个人觉得3.0里面, 在C++下面调用lua函数很不方便, 所以就扩展了一个类, 继承自LuaStack, 代码和使用方式如下: #ifndef __CC_LUA_STACKEX_H_ #define __CC_LUA_STACKEX_H_ #include "CCLuaStack.h" NS_CC_BEGIN class LuaStackEx : public LuaStack { public: void call_script_fun(const char* fun) { exe