lua闭包和尾调用

虽然工作已经基本上没有什么变动了,但是想起来面试的时候,总是屡试不爽的这么个问题。乘着这会儿空闲还是写下来,留作备用。也希望可以帮助到刚出来找Lua方面的工作的童鞋。
其实都是一些基础的东西,只是自己记性不好,还是写下来比较妥当。这样,忘记的时候,还可以回头看看。
1.lua闭包

--[[@
    lua闭包:
        从形式上来讲:子函数可以使用父函数中的局部变量,这种行为就叫做闭包!
        闭包 = 函数 + 引用环境
    ]]
    function FUN1()
        local num = 10      --upvalue(非局部变量,非全局变量,和C和C++中的成员变量有点类似)


        function InFun1()
            print(num)
        end


        function InFun2()
            num = num + 10
            print(num,"...")
        end
        return InFun1, InFun2
    end


    local a, b = FUN1()
    a()
    b()
    a()


    运行结果:10
          20    ...
          20

总结:由于lua中可以内嵌函数,也就是函数内部声明的函数。称函数内部的函数为子函数(可以有多个子函数),子函数外部的函数为父函数,多个子函数之见可以共享父函数中声明的变量。子函数 + 父函数内部的环境就构成了闭包。有点类似于C++类中的成员函数可以访问和修改成员变量。具体引用实例,后面遇到再添加。

2.lua尾调用:

--[[@
    lua尾调用:
        尾调用是一种类似在函数结尾的goto调用
        当函数的最后一个动作是调用另外一个函数时,我们称这种调用为尾调用

        尾调用不需要使用栈空间,因此尾调用递归的层次可以无限制。
        例如下面调用不论n为何值不会导致栈溢出。
        function foo (n)
            if n > 0 then return foo(n - 1) end
        end
    ]]

需要注意的是:什么是尾调用?
格式:

在Lua中,只有“return <func> (<args>)”这样的调用形式才算是一条“尾调用”,Lua会在调用前对<func>及其参数求值,所以 它们可以是任意复杂的表达式。
return x[m].foo(x[n] + a*b,m+n)
Lua中“尾调用”的一大应用就是编写“状态机”。这种程序通常以一个函数来表示一个状态,改变状态就是goto到另一个特定的函数。

借鉴博客:闭包

<think>好的,我现在需要帮助用户理解Lua尾调用如何实现无限递归的原理及行为,并提供示例解释。首先,我需要回忆Lua尾调用的相关知识,并确保自己的理解正确。 根据用户提供的引用内容,特别是引用[1],里面提到尾调用不需要使用栈空间,因此尾调用递归的层次可以无限制。例如,无论n为何值都不会导致栈溢出。这说明尾调用优化(Tail Call Optimization, TCO)在Lua中是存在的,允许进行无限递归而不会出现栈溢出的问题。 接下来,我需要明确尾调用的定义。尾调用是指在函数的最后一个动作是调用另一个函数,且无需保留当前函数的任何状态。这种情况下,Lua的虚拟机可以重用当前的栈帧,避免栈的不断增长。这对于递归尤为重要,特别是无限递归的情况,常规递归会因为栈空间的耗尽而崩溃,但尾递归则可以无限进行下去。 然后,我需要考虑如何构造一个尾调用的示例。根据引用[3],Lua函数的多返回值尾调用有相关例子。例如,inCount函数使用了默认参数,但这里需要的是一个尾递归的例子。可能的例子是一个无限递归函数,每次调用自身时都是尾调用。 比如,一个简单的无限循环函数,使用尾递归实现。例如: function infinite_tail_call() print("This is a tail call") return infinite_tail_call() -- 尾调用 end 这个函数每次调用自身时,都是函数的最后一个动作,因此符合尾调用的条件。Lua会优化这个调用,不会增加栈深度,因此不会导致栈溢出。 但是,用户可能需要更具体的例子,比如带有参数的递归函数。例如,计算一个数的阶乘,但常规递归不是尾调用,需要调整成尾递归形式。例如: function factorial(n, acc) acc = acc or 1 if n <= 1 then return acc else return factorial(n - 1, acc * n) -- 尾调用 end end 不过,这个例子虽然是尾递归,但并不是无限递归。为了展示无限递归,应该构造一个无限循环的尾调用,比如持续打印信息或递增计数器。 另外,需要区分普通递归尾递归的区别。普通递归如: function normal_recursion(n) if n <= 0 then return end normal_recursion(n - 1) end 当n很大时会导致栈溢出,而尾递归则不会。用户可能想知道为什么尾调用可以无限,而普通调用不行。这是因为尾调用优化后,每次调用替换当前栈帧,而不是新增,因此栈深度保持不变。 接下来,需要确保示例正确,并解释其工作原理。例如,无限尾调用的示例应该没有终止条件,但每次调用都是尾调用。例如: function infinite_tail() print("Infinite loop via tail call") return infinite_tail() -- 尾调用 end 调用这个函数会导致无限循环,但不会栈溢出,因为每次调用都被优化,复用当前栈帧。 此外,需要引用用户提供的参考文献,特别是引用[1]提到的尾调用不需要栈空间,从而支持无限递归。同时,引用[3]中的例子也涉及尾调用函数参数处理,可以辅助说明。 最后,需要验证自己的理解是否正确,确保没有错误。比如确认Lua确实执行TCO,并且尾调用的定义准确。可能还需要考虑Lua版本是否支持,但用户提供的引用中提到的GopherLuaLua 5.1的实现,而Lua 5.1是支持尾调用优化的。</think>### Lua尾调用实现无限递归的原理及示例 #### 尾调用定义 **尾调用**(Tail Call)指函数的最后一个动作是调用另一个函数(或自身),且**不需要保留当前函数的上下文**(如后续计算或状态保存)。Lua编译器会对尾调用进行优化(Tail Call Optimization, TCO),直接复用当前栈帧,避免栈空间增长[^1]。 #### 无限递归实现原理 1. **栈帧复用** 普通递归每次调用会创建新的栈帧,而尾调用递归会直接覆盖当前栈帧。因此,无论递归深度多大,栈空间始终固定。 2. **行为表现** 尾调用无限递归不会导致栈溢出,而是形成一个无限循环。例如: ```lua function infinite_tail_call() print("This is a tail call") return infinite_tail_call() -- 尾调用自身 end ``` 此函数会无限打印信息,但内存占用稳定。 #### 示例对比 1. **普通递归(栈溢出)** ```lua function normal_recursion(n) if n <= 0 then return end normal_recursion(n - 1) end ``` 调用`normal_recursion(100000)`会导致栈溢出。 2. **尾调用递归(无栈溢出)** ```lua function tail_factorial(n, acc) acc = acc or 1 if n <= 1 then return acc else return tail_factorial(n - 1, acc * n) -- 尾调用优化 end end ``` 调用`tail_factorial(100000)`可正常计算(需注意数值溢出问题)。 #### 关键条件 - **严格的尾调用形式**:必须是函数的最后一个操作,且无后续计算。 - **语法要求**:需使用`return func(args)`形式,不可嵌套表达式。 #### 应用场景 - 状态机实现(如游戏循环)[^3] - 流式数据处理(如无限序列生成)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值