了解啤酒瓶示例中的递归

时间:2013-12-21 01:58:09

标签: c recursion procedural

我自己在C中练习递归,我在网上找到了这个例子。 但有一点我不明白。

void singSongFor(int numberOfBottles)
{
if (numberOfBottles == 0) {
    printf("There are simply no more bottles of beer on the wall.\n\n");
} 
else {
    printf("%d bottles of beer on the wall. %d bottles of beer.\n",
           numberOfBottles, numberOfBottles);
    int oneFewer = numberOfBottles - 1;
    printf("Take one down, pass it around, %d bottles of beer on the wall.\n\n",
           oneFewer);
    singSongFor(oneFewer); // This function calls itself!

    // Print a message just before the function ends
    printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",
             numberOfBottles);
   }
}    

然后我使用main方法:

 int main(int argc, const char * argv[])
{
  singSongFor(4);
  return 0;
}

输出就是这样:

墙上挂着4瓶啤酒。 4瓶啤酒。 拿下一个,将它传递给墙壁上的3瓶啤酒。

墙上挂着3瓶啤酒。 3瓶啤酒。 拿下一个,将它传递给墙壁上的2瓶啤酒。

墙上挂着2瓶啤酒。 2瓶啤酒。 拿下一个,将它传递给墙壁上的1瓶啤酒。

墙上挂着1瓶啤酒。 1瓶啤酒。 拿下一个,将它传递给墙上的0瓶啤酒。

墙上不再有啤酒瓶了。

将一个瓶子放入回收处,在垃圾箱中放入一个空瓶子。

将一个瓶子放入回收处,在垃圾箱中放入2个空瓶子。

在回收箱中放入一个瓶子,在垃圾箱中放入3个空瓶子。

将一个瓶子放入回收箱中,将4个空瓶放入垃圾箱。

我非常了解第一部分,直到我说到“墙上再也没有啤酒瓶。后来我不明白可变数量的瓶子从1增加到4。

7 个答案:

答案 0 :(得分:6)

较小的啤酒瓶(及其相应的回收)具有内在功能。您的功能树如下所示:

4 bottles of beer on the wall. 4 bottles of beer. Take one down, pass it around, 3 bottles of beer on the wall.
|   3 bottles of beer on the wall. 3 bottles of beer. Take one down, pass it around, 2 bottles of beer on the wall.
|   |   2 bottles of beer on the wall. 2 bottles of beer. Take one down, pass it around, 1 bottles of beer on the wall.
|   |   |   1 bottles of beer on the wall. 1 bottles of beer. Take one down, pass it around, 0 bottles of beer on the wall.
|   |   |   |   There are simply no more bottles of beer on the wall.
|   |   |   Put a bottle in the recycling, 1 empty bottles in the bin.
|   |   Put a bottle in the recycling, 2 empty bottles in the bin.
|   Put a bottle in the recycling, 3 empty bottles in the bin.
Put a bottle in the recycling, 4 empty bottles in the bin.

答案 1 :(得分:4)

逐步完成此函数在调试器中的作用,您将看到此递归的确切工作原理。我不是迂腐;我真的想不出更好的方式来说明这是做什么比参考这种互动方法。

答案 2 :(得分:4)

只是简单的图片说明:

enter image description here

答案 3 :(得分:3)

请注意,最后一个printf使用numberOfBottles变量,并且永远不会修改。因此,从打印oneFewer瓶返回后,它将使用numberOfBottles打印回收文本。请记住,每次调用函数时都会有一个局部变量的不同版本。

如果缩进对函数的调用,可以看得更容易:

4 bottles of beer on the wall...
  3 bottles of beer on the wall...
    2 bottles of beer on the wall...
      1 bottles of beer on the wall...
        There are simply no more bottles of beer on the wall.
      Put a bottle in the recycling, 1 empty bottles in the bin.
    Put a bottle in the recycling, 2 empty bottles in the bin.
  Put a bottle in the recycling, 3 empty bottles in the bin.
Put a bottle in the recycling, 4 empty bottles in the bin.

现在,从同一个函数调用中写入从同一列开始的每一行。你看到瓶子和回收coindice的数量如何?那是因为两者都使用相同的变量:numberOfBottles

答案 4 :(得分:2)

在递归调用返回后运行recycle语句。

每个递归调用最终都会完成,程序将继续执行后面的循环语句,每个语句都有自己的局部变量值。

答案 5 :(得分:2)

使用递归,您可以将递归调用之前的所有内容视为前向循环,并将调用后的所有内容视为后向循环。 (尾递归 - 在函数末尾再次调用函数 - 通常由编译器优化为简单的正向循环。)

这种方法的工作方式是将每个旧函数的参数压入堆栈,并在全部返回时弹出。请记住,堆栈是后进先出。因此,从4开始,它按4,然后是3,然后是2,然后是1.当函数返回时,堆栈开始展开,所以你再次以相反的顺序看到参数:1,2,3,4。

答案 6 :(得分:2)

它以这种方式工作的原因是,numberOfBottles大于1的singSongFor()的每次调用将依次递归调用singSongFor(),直到numberOfBottles为0.此时printf("There are simply no more bottles of beer on the wall.\n\n")到达并且该函数将完成,传递给调用函数,该函数将传入1的参数,然后到达printf("Put a bottle in the recycling, %d empty bottles in the bin.\n", numberOfBottles);并且自身完成,返回singSongFor(2) ...等等直到你回到你的原始号码,在这种情况下为4。