开发中怎么使用递归?

递归

理解场景

递归其实主要有两个关键,第一个就是要可以递,第二个就是要可以归,其实也就是有去有回。

可以想象这样一个场景,你面前有一个门,你手里拿着一把钥匙,你可以用这把钥匙打开这个门,然后打开这个门之后,你发现前面还有一扇门,但是这扇门也可以用你手中的钥匙打开,就这样你一直向里面打开了很多扇门,知道你打开最后一扇门,发现门里面是一睹墙。然后你带着你得到的东西返回到最初的位置。

递归解决问题的时候,可以在递去的过程中处理问题,如下:

function recursion(大规模){
    if (end_condition){      // 明确的递归终止条件
        end;   // 简单情景
    }else{            // 在将问题转换为子问题的每一步,解决该步中剩余部分的问题
        solve;                // 递去
        recursion(小规模);     // 递到最深处后,不断地归来
    }
}

也可以在归来的过程中处理问题,如下:

function recursion(大规模){
    if (end_condition){      // 明确的递归终止条件
        end;   // 简单情景
    }else{            // 先将问题全部描述展开,再由尽头“返回”依次解决每步中剩余部分的问题
        recursion(小规模);     // 递去
        solve;                // 归来
    }
}

递归是在什么时候处理问题,我们可以通过方法里面调用自身的代码的位置来判断,看看解决问题的代码是在调用自身代码的位置之前还是在之后,如果在之前,那么递归就是在递的过程中解决问题,如果是在之后,那么递归就是在归的过程中解决问题。

递归需要的三个条件

明确递归的终止条件

我们知道,递归就是有去有回,既然这样,那么必然应该有一个明确的临界点,程序一旦到达了这个临界点,就不用继续往下递去而是开始实实在在的归来。换句话说,该临界点就是一种简单情境,可以防止无限递归。

给出递归终止时的处理办法

我们刚刚说到,在递归的临界点存在一种简单情境,在这种简单情境下,我们应该直接给出问题的解决方案。一般地,在这种情境下,问题的解决方案是直观的、容易的。

提取重复的逻辑,缩小问题规模

我们在阐述递归思想内涵时谈到,递归问题必须可以分解为若干个规模较小、与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决。从程序实现的角度而言,我们需要抽象出一个干净利落的重复的逻辑,以便使用相同的方式解决子问题。

递归例子

例子1

下面的这个例子是在递的过程中处理问题,如下图:

在这里插入图片描述

private DepartmentVO findDepartmentVO(DepartmentVO result, Long id) {
        if (result.getId().equals(id)) {
            return result;
        }

        if (CollectionUtils.isEmpty(result.getSubDepartments())) {
            return null;
        }

        for (DepartmentVO subDepartment : result.getSubDepartments()) {
            DepartmentVO departmentVO = findDepartmentVO(subDepartment, id);
            if (departmentVO != null) {
                return departmentVO;
            }
        }

        return null;
    }

上面的代码的逻辑,是在一个部门树里面搜索某个部门,result是一个部门树,id是要搜索的部门id。上面这个递归是在递的过程中处理的问题,上面的这个例子很好的说明了递归所需要的三个条件。

例子2

下面的这个例子,也是在递的过程中解决问题:

public static long f(int n){
        if(n == 1)   // 递归终止条件 
            return 1;    // 简单情景

        return n*f(n-1);  // 相同重复逻辑,缩小问题的规模
    }

上面的这个递归,也满足递归的三个条件:

1.终止条件,也就是if(n==1)

2.终止的时候的处理办法,也就是return n*f(n-1),其实也就是返回一个结果

3.抽取重复的逻辑,缩小问题的规模,其实也就是方法中上面的代码

总结

从上面的两个例子中可以看出,递归肯定是先递,然后再归。终止时的处理办法,其实也就是归来的结果。无论你是在递的过程中处理的问题,还是在归的过程中处理的问题,在最后归来的时候,你必须要有一些东西带回来,这就是递归。

什么时候可以用到递归

一个大问题,如果它可以划分成多个小的问题,并且这些小的问题可以用同一种方式解决,那么我们就可以用到递归。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是一个使用 Vite 开发递归的 TypeScript 插件的基本步骤: 1. 创建一个新的 TypeScript 项目,并运行 `npm init` 初始化项目。 2. 安装 Vite 和 TypeScript:`npm install vite typescript --save-dev`。 3. 在项目根目录下创建一个名为 `vite.config.ts` 的文件,并添加以下内容: ```typescript import { Plugin } from 'vite'; export default (): Plugin => { const pluginName = 'my-ts-plugin'; const tsPlugin: Plugin = { name: pluginName, async transform(code, id) { // TODO return null; }, }; return tsPlugin; }; ``` 这个配置文件导出了一个函数,该函数返回一个 Vite 插件对象。我们将在这个插件对象的 `transform` 方法编写递归的 TypeScript 转换逻辑。 4. 在 `transform` 方法编写递归的 TypeScript 转换逻辑。这里我们将使用 TypeScript 的 AST(抽象语法树)来遍历代码并进行转换。 ```typescript import ts from 'typescript'; import { Plugin } from 'vite'; export default (): Plugin => { const pluginName = 'my-ts-plugin'; const tsPlugin: Plugin = { name: pluginName, async transform(code, id) { if (id.endsWith('.ts')) { const sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true); function visit(node: ts.Node) { if (ts.isCallExpression(node)) { const expression = node.expression; if (ts.isIdentifier(expression) && expression.text === 'recursiveTransform') { const [, arg] = node.arguments; if (ts.isStringLiteral(arg)) { const transformed = arg.text.split('').reverse().join(''); const replacement = ts.createStringLiteral(transformed); return replacement; } } } return ts.visitEachChild(node, visit, null); } const transformedSourceFile = ts.visitNode(sourceFile, visit); const transformedCode = ts.createPrinter().printFile(transformedSourceFile); return transformedCode; } return null; }, }; return tsPlugin; }; ``` 这个递归的 TypeScript 插件将会查找代码所有名为 `recursiveTransform` 的函数调用,并将其传递的字符串参数反转。我们将使用 TypeScript 的 `ts.createSourceFile` 方法将代码解析成 AST,然后使用 TypeScript 的 `ts.visitNode` 方法遍历 AST 并进行转换。最后,使用 TypeScript 的 `ts.createPrinter().printFile` 方法将转换后的 AST 打印成代码字符串。 5. 运行 `npm run dev` 启动 Vite 开发服务器,插件将自动加载并应用于 TypeScript 文件。 这就是使用 Vite 开发递归的 TypeScript 插件的基本步骤。当然,实际开发还需要考虑更多的细节和复杂性,例如插件的选项配置、插件的错误处理、插件的性能优化等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr-X~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值