lambda实现递归

lambda实现递归

在 C++ 中,lambda 表达式在定义时实际上不能直接调用自己,因为 lambda 在定义时没有名字。要让一个 lambda 自我引用,你需要使用一个技巧:将 lambda 自身作为参数传递给自己,从而实现递归。

为什么 Lambda 自身在定义时无法被调用?

  1. 匿名性:Lambda 表达式是匿名的,编译器在定义时不为其生成名称,因此无法在其内部直接引用或调用自己。
  2. 捕获和名称:在 lambda 定义时,虽然可以捕获外部变量,但不能直接引用自身,因为 lambda 的名字在定义时尚未确定。

解决方案一:使用引用捕获

通过使用 lambda 的引用捕获(通常是将 lambda 自身作为参数传递),你可以在 lambda 内部实现递归。假设我们要查找一个 AActor 下所有的 URectLightComponent,我们可以使用一个 lambda 函数来递归地遍历所有子组件。

include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Components/RectLightComponent.h"
#include "GameFramework/Actor.h"

class FRectLightFinder
{
public:
    static void FindRectLightComponents(AActor* Actor, TArray<URectLightComponent*>& OutRectLightComponents)
    {
        if (!Actor)
        {
            return;
        }

        // 定义 lambda 函数进行递归
        auto RecursiveSearch = [](USceneComponent* Component, TArray<URectLightComponent*>& OutComponents, auto& RecursiveSearchRef)
        {
            if (!Component)
            {
                return;
            }

            // 检查是否是 RectLightComponent
            if (URectLightComponent* RectLightComponent = Cast<URectLightComponent>(Component))
            {
                OutComponents.Add(RectLightComponent);
            }

            // 递归查找所有子组件
            for (USceneComponent* Child : Component->GetChildrenComponents(true))
            {
                RecursiveSearchRef(Child, OutComponents, RecursiveSearchRef);
            }
        };

        // 从 Actor 的根组件开始递归
        RecursiveSearchRef(Actor->GetRootComponent(), OutRectLightComponents, RecursiveSearch);
    }
};

解释

  1. Lambda 函数RecursiveSearch 是一个递归的 lambda 表达式,它负责遍历组件及其子组件。注意,lambda 使用 auto& RecursiveSearchRef 参数来递归调用自己。为了在 lambda 内部递归调用自身,必须通过引用捕获。
  2. RecursiveSearchRef:这参数用于实现递归调用,因为 lambda 自身在定义时还无法被调用。我们将它作为参数传递给 lambda,并在递归时引用它。
  3. 递归逻辑:在 RecursiveSearch 内部,我们首先检查当前组件是否是 URectLightComponent。如果是,我们将其添加到结果数组中。然后我们递归地遍历当前组件的所有子组件。
  4. 启动递归:在 FindRectLightComponents 函数中,我们从 Actor 的根组件开始调用递归 lambda。

关键点

  • auto& RecursiveSearchRef:在 lambda 的参数列表中,使用 auto& RecursiveSearchRef 来传递 lambda 自身的引用。这允许 lambda 在其内部递归调用自身。
  • 递归调用:通过 RecursiveSearchRef 引用,lambda 可以递归调用自己,实现遍历所有子组件的功能。

这种方式有效地绕过了 lambda 定义时无法直接自引用的问题,允许你在 lambda 内部实现递归逻辑。

解决方案二:使用Y组合因子

在数学和计算机科学中,“Y组合因子”通常指的是 Y 组合子Y Combinator),一个在函数式编程中非常重要的概念。Y 组合子是一个能够在没有明确递归定义的情况下实现递归的高阶函数。它由 Haskell CurryRobert Feys 在其工作中引入,并且在计算机科学中具有重要意义,特别是在理解和实现递归函数时。

Y 组合子的定义

Y 组合子的定义可以用以下的 Lambda 演算表达式来描述:

Y = λf.(λx.f (x x)) (λx.f (x x))

这里,λ 表示 Lambda 表达式。简要地说,Y 组合子能够接收一个函数 f,并返回一个可以递归调用 f 的函数。具体来说,Y 组合子的作用是“找到”一个函数的固定点,即函数 f 在其输入和输出上保持不变的点,从而使得递归能够在没有显式自引用的情况下进行。

示例

我们可以通过 Y 组合子实现经典的递归函数,如阶乘函数。下面是一个阶乘函数的实现示例,展示了如何使用 Y 组合子来定义递归。

  1. 定义阶乘函数的递归函数(未使用 Y 组合子):

    factorial(n) = if n == 0 then 1 else n * factorial(n - 1)
    
  2. 用 Y 组合子实现阶乘函数

    首先定义一个辅助函数 F,它接受一个函数 f 并返回一个新的函数,这个新的函数执行阶乘逻辑:

    F = λf.λn.if n == 0 then 1 else n * (f (n - 1))
    

    然后用 Y 组合子来定义阶乘函数:

    factorial = Y F
    

    在这里,Y FF 应用到自身上,从而允许 F 函数的递归调用。

重要性和应用

  1. 理解递归:Y 组合子在函数式编程中被广泛应用,用于理解和实现递归函数,特别是在没有传统语言提供的递归支持的环境中。
  2. 计算机科学理论:Y 组合子展示了 Lambda 演算的强大能力,是计算机科学理论中的一个重要概念。
  3. 编程语言设计:在设计编程语言时,理解 Y 组合子的工作原理有助于实现更复杂的功能,如固定点计算和高阶函数的处理。
posted @   Dream_moon  阅读(406)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示