赞
踩
程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
简单来说,递归,就是在运行的过程中调用自己。
1. 子问题须与原始问题为同样的事,且更为简单;
2. 不能无限制地调用本身,须有个出口,化简为非递归状况处理。
因为每调用一个方法就会在栈上创建一个栈帧,方法调用结束后就会弹出该栈帧,而栈的大小不是无限的,所以递归调用次数过多的话就会导致栈溢出。而递归调用的特点是每递归一次,就要创建一个新的栈帧,而且还要保留之前的环境(栈帧),直到遇到结束条件。
解决递归调用栈溢出的方法是通过尾递归优化,尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
代码示例:
- /**
- * 阶乘计算 -- 递归解决
- *
- * @param number 当前阶乘需要计算的数值
- * @return number!
- */
-
- public int factorialRecursion(final int number) {
-
- // 结束条件
-
- if (number == 1) return number;
-
- // 继续递归
-
- else return number * factorialRecursion(number - 1);
-
- }
-
- /**
- * 阶乘计算 -- 尾递归解决
- *
- * @param factorial 上一轮递归保存的值
- * @param number 当前阶乘需要计算的数值
- * @return number!
- */
-
- public static int factorialTailRecursion(final int factorial, final int number){
-
- if (number == 1) return factorial;
-
- else return factorialTailRecursion(factorial * number, number - 1);
-
- }
然而当你调用上文的尾递归写法之后,发现并没有什么作用,该栈溢出的还是会栈溢出,尾递归这样的写法本身并不会有什么用,依赖的是编译器对尾递归写法的优化,在很多语言中编译器都对尾递归有优化,然而这些语言中并不包括java。
因此在这里我们使用lambda的延迟加载机制来延迟递归的调用,从而实现栈帧的复用。
- /**
- * 尾递归函数接口
- * @author : martrix
- */
- @FunctionalInterface
- public interface TailRecursion<T> {
- /**
- * 用于递归栈帧之间的连接,惰性求值
- * @return 下一个递归栈帧
- */
- TailRecursion<T> apply();
-
- /**
- * 判断当前递归是否结束
- * @return 默认为false,因为正常的递归过程中都还未结束
- */
- default boolean isFinished(){
- return false;
- }
-
- /**
- * 获得递归结果,只有在递归结束才能调用,这里默认给出异常,通过工具类的重写来获得值
- * @return 递归最终结果
- */
- default T getResult() {
- throw new Error("递归还没有结束,调用获得结果异常!");
- }
-
- /**
- * 及早求值,执行者一系列的递归,因为栈帧只有一个,所以使用findFirst获得最终的栈帧,接着调用getResult方法获得最终递归值
- * @return 及早求值,获得最终递归结果
- */
- default T invoke() {
- return Stream.iterate(this, TailRecursion::apply)
- .filter(TailRecursion::isFinished)
- .findFirst()
- .get()
- .getResult();
- }
- }
- /**
- * 使用尾递归的类,目的是对外统一方法
- *
- * @author : Matrix
- */
- public class TailInvoke {
- /**
- * 统一结构的方法,获得当前递归的下一个递归
- *
- * @param nextFrame 下一个递归
- * @param <T> T
- * @return 下一个递归
- */
- public static <T> TailRecursion<T> call(final TailRecursion<T> nextFrame) {
- return nextFrame;
- }
-
- /**
- * 结束当前递归,重写对应的默认方法的值,完成状态改为true,设置最终返回结果,设置非法递归调用
- *
- * @param value 最终递归值
- * @param <T> T
- * @return 一个isFinished状态true的尾递归, 外部通过调用接口的invoke方法及早求值, 启动递归求值。
- */
- public static <T> TailRecursion<T> done(T value) {
- return new TailRecursion<T>() {
- @Override
- public TailRecursion<T> apply() {
- throw new Error("递归已经结束,非法调用apply方法");
- }
-
- @Override
- public boolean isFinished() {
- return true;
- }
-
- @Override
- public T getResult() {
- return value;
- }
- };
- }
- }
- /**
- * 阶乘计算 -- 使用尾递归接口完成
- * @param factorial 当前递归栈的结果值
- * @param number 下一个递归需要计算的值
- * @return 尾递归接口,调用invoke启动及早求值获得结果
- */
- public static TailRecursion<Integer> factorialTailRecursion(final int factorial, final int number) {
- if (number == 1)
- return TailInvoke.done(factorial);
- else
- return TailInvoke.call(() -> factorialTailRecursion(factorial + number, number - 1));
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。