我正在编写一本书,其中包含一章处理C中的递归。它将99瓶歌曲打印到日志中。这是代码:
void singTheSong (int numberOfBottles) {
if (numberOfBottles == 0) {
printf("There are no more bottles left.\n");
} else {
printf("%d bottles of bear 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", oneFewer);
singTheSong(oneFewer);
}
}
int main(int argc, const char * argv[])
{
singTheSong(99);
return 0;
}
输出就像歌曲的歌唱方式一样。我理解的是,numberOfBottles
变量如何改变其值?我看到它被oneFewer
变量中的一个减去了,但是我无法理解它是如何工作的。在我看来,原木上写着“墙上挂着99瓶啤酒,99瓶啤酒。拿下一瓶啤酒,然后四处走动,墙上挂着98瓶啤酒。”反复地,没有下降到98以下。我不确定numberOfBottles
值是如何变化的,因此oneFewer
如何跟踪瓶数。还有一个问题,我对这个主题的困惑是继续编程的一个坏兆头吗?我把它钉在了这一点上。
答案 0 :(得分:8)
关键在于:
int oneFewer = numberOfBottles - 1;
singTheSong(oneFewer);
生成对singTheSong
的新调用,其中numberOfBottles
为98而不是99.该函数获取numberOfBottles
的本地副本,值为98.
Stack numberOfBottles
------------------------------------------------------
singTheSong 99
singTheSong 98
singTheSong 97
singTheSong 96
... ...
singTheSong 1
singTheSong 0
当numberOfBottles
达到零时,对singTheSong
的100个嵌套调用位于堆栈上。最后,函数返回而不进行递归,并且等待堆栈的所有副本将一次返回一个。
答案 1 :(得分:2)
拿一张纸,每次调用绘制调用堆栈,记录参数的值和返回地址(这样你就可以看到它展开)。它应该更容易理解。
执行将始终进入else
块,除了最后一个(基本情况),它将进入if
块。这意味着该数字将递减,直到达到0
。
答案 2 :(得分:2)
第一次调用singTheSong
时,numberOfBottles
为99
。
然后调用singTheSong(numberOfBottles-1)
(99 - 1 = 98
),然后使用新的瓶数调用singTheSong(numberOfBottles-1)
,即98 - 1 = 97
。< / p>
重复此过程,直到达到基本案例。
答案 3 :(得分:2)
首先回答你的上一个问题 - 不,这并不一定意味着你的编程生涯注定失败。递归对于理解是很重要的,但是它的大部分功能用途(步行树和图形跳跃到脑海中)已经在库中实现,您不需要深入理解它。当你进入函数式编程时,它会更重要,但到那时,你将很好地掌握它。
它表明你不明白的是函数的本质和调用栈的作用。这对学习很重要,递归是一种很好的方法。
关于递归的事情是它导致在调用堆栈上多次调用相同的函数。在这种情况下,当你从99开始,是的,oneFewer
= 98.但是,在你完成这个函数之前,你调用singTheSong
传递一个更少的,即98.这将调用{{1 singTheSong
值为97。
这将导致您在调用堆栈中堆叠100个相同功能的实例。在第100首歌曲中,您打印“没有剩下的瓶子”,并且您的功能退出。然后“1瓶”的功能已经完成,所以它退出,允许“2瓶”功能完成,等等通过你的调用堆栈,直到原始功能完成。
答案 4 :(得分:2)
要理解的是,唱歌的功能就是自称。因此函数main调用singTheSong(99)。这个函数输出一些东西,然后在变量oneFewer中粘贴99 - 1,然后再次调用singTheSong,值为oneFewer。所以这一次,它传递的值不是99,它是98。
新功能是singTheSong的另一个实例。它对之前所谓的唱歌之歌一无所知。它只知道它被赋予了98的值,并且它应该打印出一些东西,将98-1粘在一个更少,然后再打电话给singTheSong。
当程序降到0时,实际上会有99个singTheSong实例。但是当它被调零时,而不是再次递归,它只是打印出“没有剩下的瓶子”。然后程序返回所有99个案例,最后结束。 0因此是基本情况,每个递归算法都有一个基本案例是至关重要的。
如果你想了解更多,请尝试输入printf(“%d%d \ n”,oneFewer,numberOfBottles);在递归调用之后。
如果您想让您的计算机崩溃(并查看此网站的名称是什么),请尝试删除if语句和“没有剩下的瓶子”基本案例。
答案 5 :(得分:1)
在“取下一个,传递它”声明之后,
singTheSong(oneFewer);
使用oneFewer调用,oneFewer是保存特定迭代中瓶数的值的变量。所以numberOfBottles最初是99,然后oneFewer被分配到98.然后,oneFewer = 98,被传递到singTheSong。这一次,被称为 numberOfBottles 的参数与前一次迭代中的一个相同的值具有相同的值。在此迭代中,oneFewer获得值97,并且此循环继续,直到达到基本情况,即当oneFewer为0并且传递到singTheSong时。 singTheSong获取该值并知道它已经到达 if 的情况所以它停止了。