与另一个相同的递归函数相比,是什么导致这个递归函数崩溃?

JCC*_*CCS 19 java recursion

这会导致堆栈溢出错误.我只需要帮助解释为什么这个崩溃与正确的相似.我使用过调试器,但我仍然不清楚.

public static void main(String[] args) {
    countForwards(5);
}
public static void countForwards( int num ) {
    if (num >= 0){
        countForwards(num--);
    }
    System.out.print(num + " ");
}
Run Code Online (Sandbox Code Playgroud)

我知道这是解决方案,但我不明白为什么会有所不同

public static void countForwards( int num ) {
    if (num >= 0){
        countForwards(num - 1);
    }
    System.out.print(num + " ");
}
Run Code Online (Sandbox Code Playgroud)

Era*_*ran 44

countForwards(num--)将原始值传递num给递归调用,这意味着递归永远不会结束.

countForwards(--num) 将允许递归结束.

在看到这个问题得到的所有流量之后,我认为将这个答案扩展一点是值得的.

正如paxdiablo评论的那样,即使countForwards(--num)允许递归终止,它的行为也不同于countForwards(num-1).

这两种变体都会导致以下一系列递归调用:

countForwards(5)
countForwards(4)
countForwards(3)
countForwards(2)
countForwards(1)
countForwards(0)
countForwards(-1)
Run Code Online (Sandbox Code Playgroud)

但是当递归展开时,它们将输出一系列不同的数字:

num - 1    --num

-1            -1
 0            -1
 1             0
 2             1
 3             2 
 4             3 
 5             4 
Run Code Online (Sandbox Code Playgroud)

区别的原因是num-1不会改变numwhile --num减量的值num.

  • 工作一样吗?不,它不会.在递归传递的内容中,你是对的.但是就`num`设置为_after_递归调用(例如将其打印出来)而言,并非如此.`recur(num-1)`根本不会改变`num`,但`recur( - num)`_will._ (8认同)
  • @Lawtonfogle如果你避免使用任何可能被误解的语言,你将会有一个非常艰难的职业生涯.我不认为这是不使用前/后递增/递减的非常好的理由. (5认同)
  • 人们经常会混淆`x - `和`x - 1`是在生产代码中避免`--`(和`++`)的一个原因. (2认同)

Pra*_*kar 17

num--使用后缀运算符--,这意味着原始值ie num被传递,并且其值在传递后递减.

关于后缀运算符(即我们在此示例中使用的运算符)的有趣之处在于执行操作,然后值递增或递减.请参阅java中有关运算符的官方文档

class PrePostDemo {
    public static void main(String[] args){
        int i = 3;

        i++;
        // prints 4
        System.out.println(i);

        ++i;               
        // prints 5
        System.out.println(i);

        // prints 6
        System.out.println(++i);

        // prints 6
        System.out.println(i++);

        // prints 7
        System.out.println(i);
    }
}
Run Code Online (Sandbox Code Playgroud)


Slo*_*rop 6

后减

Post-Decrement采用变量名称 运算符的形式.这告诉编译器首先使用原始值并且afterawrds递减它,所以如果我们有这样的东西:

for (int i = 10; i > 0; i--) 
{ 
   System.out.println(i); 

}
Run Code Online (Sandbox Code Playgroud)

输出如下

1: 10
2: 9 
3: 8 
4: 7 
5: 6
6: 5 
7: 4
8: 3 
9: 2
10: 1 
Run Code Online (Sandbox Code Playgroud)

预减

Pre-Decrement采用表单运算符 变量名称,并在您使用该值之前减少使用.上面的相同代码将终止于0而不是1.这是因为函数在使用该值之前递减了该值.

这如何适用于递归调用?

每个递归调用都是它自己的堆栈,所以当你将num--传递给递归函数时,你实际上是传递num的原始值,当子调用终止时(在这种情况下永远不会),父调用将递减num .由于您没有正确终止调用的另一个基本案例,因此会导致无限递归.


Sag*_*out 5

实际上它是由于该方法中的后减量运算符而发生的.

   public static void countForwards( int num ) {
    if (num >= 0){

        countForwards(num--);
    }
    System.out.print(num + " ");
}
Run Code Online (Sandbox Code Playgroud)

现在当函数再次调用countForwards时,由于方法中的post减量,它总是将num的值取为5,请尝试更改为pre-decrement

    public static void countForwards( int num ) {
    if (num >= 0){

        countForwards(--num);
    }
    System.out.print(num + " ");
}
Run Code Online (Sandbox Code Playgroud)

这将是有效的,因为值首先递减,然后该值使用方法.

函数再次调用,这些是原始的并存储在堆栈中.这就是为什么它显示堆栈溢出.

    public static void countForwards( int num ) {
if (num >= 0){
    countForwards(num - 1);
}
System.out.print(num + " ");
Run Code Online (Sandbox Code Playgroud)

}

这是有效的,因为这是一个尝试首先解决自己的表达式,然后该函数可以使用该表达式的值.我希望它能回答你的问题.