了解recursion

我在学校学习recursion方面遇到了很大的麻烦。 每当教授谈论它,我似乎得到它,但只要我自己尝试它,它完全打击我的大脑。

我试图彻夜解决河内的塔楼,彻底打乱了我的思想。 我的教科书只有约30页的recursion,所以它不是太有用。 有谁知道可以帮助澄清这个话题的书籍或资源?

你如何清空含有五朵花的花瓶?

答案:如果花瓶不是空的,你拿出一朵花,然后你清空一个花瓶里有四朵花。

你如何清空一个装有四朵花的花瓶?

答:如果花瓶不是空的,你拿出一朵花,然后你倒空一个花瓶里有三朵花。

你如何清空含有三朵花的花瓶?

答:如果花瓶不是空的,你拿出一朵花,然后你倒空一个花瓶里有两个花。

你如何清空一个装着两朵花的花瓶?

答:如果花瓶不是空的,你拿出一朵花,然后你倒空一个花瓶花瓶。

你如何清空含有一朵花的花瓶?

答案:如果花瓶不是空的,你拿出一朵花,然后你倒空一个花瓶没有花瓶。

你如何清空不含鲜花的花瓶?

答:如果花瓶不是空的,你拿出一朵花,但花瓶是空的,所以你完成了。

这是重复的。 让我们来概括一下:

你如何清空含有N朵花的花瓶?

答:如果花瓶不是空的,你拿出一朵花,然后你清空一个花瓶含有N-1花。

嗯,我们可以在代码中看到吗?

void emptyVase( int flowersInVase ) { if( flowersInVase > 0 ) { // take one flower and emptyVase( flowersInVase - 1 ) ; } else { // the vase is empty, nothing to do } } 

嗯,我们不能在一个for循环中完成吗?

为什么是,recursion可以用迭代来代替,但是recursion往往更优雅。

我们来谈谈树木。 在计算机科学中, 是一个由节点组成的结构,每个节点都有一些子节点也是节点,或者是空的。 二叉树是一个由正好有两个孩子的节点组成的树,通常称为“左”和“右”。 孩子可以再次成为节点,或者是空的。 是不是任何其他节点的子节点。

想象一下,一个节点,除了它的子节点,还有一个值,一个数字,并且想象我们希望把所有的值加在一个树上。

要在任何一个节点中求和值,我们将节点本身的值添加到其左侧子元素的值(如果有的话)以及其右侧子元素(如果有的话)的值。 现在回想一下,孩子们,如果不是零,也是节点。

因此,要总结左边的孩子,我们会将孩子节点本身的值添加到其左侧孩子的值(如果有的话)以及其右侧孩子的值(如果有的话)。

因此,要总结左孩子的左孩子的值,我们将孩子节点本身的值添加到其左孩子的值(如果有)以及其右孩子的值(如果有的话)。

也许你已经预见了我要去哪里,并希望看到一些代码? 好:

 struct node { node* left; node* right; int value; } ; int sumNode( node* root ) { // if there is no tree, its sum is zero if( root == null ) { return 0 ; } else { // there is a tree return root->value + sumNode( root->left ) + sumNode( root->right ) ; } } 

请注意,不是显式地testing孩子,看他们是否为空或节点,我们只是使recursion函数为零节点返回零。

所以说,我们有一棵树,看起来像这样(数字是值,斜线指向儿童,和@意味着指针指向空):

  5 / \ 4 3 /\ /\ 2 1 @ @ /\ /\ @@ @@ 

如果我们在根节点(值为5的节点)上调用sumNode,我们将返回:

 return root->value + sumNode( root->left ) + sumNode( root->right ) ; return 5 + sumNode( node-with-value-4 ) + sumNode( node-with-value-3 ) ; 

我们来扩展一下。 到处都可以看到sumNode,我们用return语句的扩展来代替它:

 sumNode( node-with-value-5); return root->value + sumNode( root->left ) + sumNode( root->right ) ; return 5 + sumNode( node-with-value-4 ) + sumNode( node-with-value-3 ) ; return 5 + 4 + sumNode( node-with-value-2 ) + sumNode( node-with-value-1 ) + sumNode( node-with-value-3 ) ; return 5 + 4 + 2 + sumNode(null ) + sumNode( null ) + sumNode( node-with-value-1 ) + sumNode( node-with-value-3 ) ; return 5 + 4 + 2 + 0 + 0 + sumNode( node-with-value-1 ) + sumNode( node-with-value-3 ) ; return 5 + 4 + 2 + 0 + 0 + 1 + sumNode(null ) + sumNode( null ) + sumNode( node-with-value-3 ) ; return 5 + 4 + 2 + 0 + 0 + 1 + 0 + 0 + sumNode( node-with-value-3 ) ; return 5 + 4 + 2 + 0 + 0 + 1 + 0 + 0 + 3 + sumNode(null ) + sumNode( null ) ; return 5 + 4 + 2 + 0 + 0 + 1 + 0 + 0 + 3 + 0 + 0 ; return 5 + 4 + 2 + 0 + 0 + 1 + 0 + 0 + 3 ; return 5 + 4 + 2 + 0 + 0 + 1 + 3 ; return 5 + 4 + 2 + 1 + 3 ; return 5 + 4 + 3 + 3 ; return 5 + 7 + 3 ; return 5 + 10 ; return 15 ; 

现在看看我们如何征服一个任意深度的结构和“分支”,把它看作是一个复合模板的重复应用? 每一次通过我们的sumNode函数,我们只处理一个单独的节点,使用一个单一的if / then分支,以及两个简单的return语句,直接从我们的规范中写出来,

 How to sum a node: If a node is null its sum is zero otherwise its sum is its value plus the sum of its left child node plus the sum of its right child node 

是recursion的力量。


上面的花瓶例子是尾recursion的一个例子。 所有尾recursion的意思是,在recursion函数中,如果我们recursion(也就是说,如果我们再次调用函数),那是我们所做的最后一件事情。

树的例子并不是尾recursion,因为即使我们做的最后一件事是recursion正确的孩子,在我们做之前,我们recursion了左边的孩子。

实际上,我们称为子节点的顺序,加上当前节点的值根本就不重要,因为加法是可交换的。

现在让我们来看看订单确实重要的操作。 我们将使用节点的二叉树,但是这次所保存的值将是一个字符,而不是一个数字。

我们的树会有一个特殊的属性,对任何节点来说,它的angular色都是按照其左边的孩子所持有的angular色(按字母顺序),其右边的孩子所持有的angular色之前 (按字母顺序)。

我们要做的是打印树是按字母顺序排列的。 鉴于树木的特殊性质,这很容易做到。 我们只打印左边的孩子,然后是节点的angular色,然后右边的孩子。

我们不只是想打印,所以我们会通过我们的function来打印。 这将是一个打印(char)函数的对象; 我们不必担心它是如何工作的,只是在打印被调用时,它会打印某处。

让我们看看在代码中:

 struct node { node* left; node* right; char value; } ; // don't worry about this code class Printer { private ostream& out; Printer( ostream& o ) :out(o) {} void print( char c ) { out << c; } } // worry about this code int printNode( node* root, Printer& printer ) { // if there is no tree, do nothing if( root == null ) { return ; } else { // there is a tree printNode( root->left, printer ); printer.print( value ); printNode( root->right, printer ); } Printer printer( std::cout ) ; node* root = makeTree() ; // this function returns a tree, somehow printNode( root, printer ); 

除了现在的操作顺序之外,这个例子还说明了我们可以将事物传递给recursion函数。 我们唯一要做的就是确保每次recursion调用时,我们都会继续传递它。 我们传递了一个节点指针和一个打印机给函数,在每次recursion调用时,我们都将它们传递给“down”。

现在,如果我们的树看起来像这样:

  k / \ hn /\ /\ aj @ @ /\ /\ @@ i@ /\ @@ 

我们打印什么?

 From k, we go left to h, where we go left to a, where we go left to null, where we do nothing and so we return to a, where we print 'a' and then go right to null, where we do nothing and so we return to a and are done, so we return to h, where we print 'h' and then go right to j, where we go left to i, where we go left to null, where we do nothing and so we return to i, where we print 'i' and then go right to null, where we do nothing and so we return to i and are done, so we return to j, where we print 'j' and then go right to null, where we do nothing and so we return to j and are done, so we return to h and are done, so we return to k, where we print 'k' and then go right to n where we go left to null, where we do nothing and so we return to n, where we print 'n' and then go right to null, where we do nothing and so we return to n and are done, so we return to k and are done, so we return to the caller 

所以,如果我们只是看看我们打印的行:

  we return to a, where we print 'a' and then go right to we return to h, where we print 'h' and then go right to we return to i, where we print 'i' and then go right to we return to j, where we print 'j' and then go right to we return to k, where we print 'k' and then go right to we return to n, where we print 'n' and then go right to 

我们看到我们打印了“ahijkn”,确实按字母顺序排列。

我们设法按字母顺序打印整个树,只要知道如何按字母顺序打印单个节点即可。 只是(因为我们的树具有按照字母顺序排列值左边的值的特殊属性)知道在打印节点的值之前打印左边的子节点,打印节点的值之后打印右边的子节点。

就是recursion的力量:通过只知道如何完成整个的一部分(以及知道何时停止recursion),能够完成整个事情。

回顾在大多数语言中,运算符|| (“或”)当它的第一个操作数为真时发生短路,一般的recursion函数是:

 void recurse() { doWeStop() || recurse(); } 

Luc M评论:

所以应该为这种答案创造一个徽章。 恭喜!

谢谢,Luc! 但是,实际上,因为我编辑了这个答案四次以上(为了增加最后一个例子,但主要是为了纠正错别字,并打磨它 – 在一个小小的上网本键盘上打字很困难),我不能得到任何更多的观点。 这有点让我不愿意为未来的答案付出太多的努力。

在这里看到我的评论: https : //stackoverflow.com/questions/128434/what-are-community-wiki-posts-in-stackoverflow/718699#718699

你的大脑因为进入无限recursion而爆发了。 这是一个常见的初学者错误。

不pipe你信不信,你已经明白了recursion,你只是被一个普通的,但是错误的function比喻所拖垮:一个小东西进出。

思考而不是一个任务或过程,如“了解更多关于网上recursion”。 这是recursion的,你没有问题。 要完成此任务,您可能会:

 a)阅读Google的“recursion”结果页面
 b)一旦你读了它,按照它的第一个链接和...
 a.1)阅读关于recursion的新页面 
 b.1)一旦你读完了,请按照它的第一个链接和...
 a.2)阅读关于recursion的新页面 
 b.2)一旦你读完了,按照它的第一个链接和...

正如你所看到的,你一直在做recursion的东西很长一段时间没有任何问题。

多久你会继续做这个任务? 永远,直到你的大脑爆炸? 当然不是,只要你相信你已经完成了任务,你就会停下来。

当你要求你“在网上发现更多关于recursion”时,没有必要指定这个,因为你是一个人,你可以自己推断。

计算机不能推算出千斤顶,所以你必须包含一个明确的结尾:“在网上find更多关于recursion的东西, 直到你理解了,或者你最多读了10页 ”。

你也推断你应该从谷歌的“recursion”结果页面开始,这也是计算机无法做到的。 我们的recursion任务的完整描述还必须包括明确的起点:

“了解更多有关网上recursion的内容, 直到您了解它,或者您已经阅读了最多10页,从www.google.com/search?q=recursion开始

为了讨论整个事情,我build议你尝试一下这些书:

  • Common Lisp:象征性计算的简单介绍。 这是recursion最可爱的非math解释。
  • 小诡计。

为了理解recursion,你只需要看你的洗发水瓶子的标签:

 function repeat() { rinse(); lather(); repeat(); } 

问题在于没有终止条件,并且recursion将无限期地重复,或者直到用完洗发水或热水(外部终止条件,类似于吹捧堆栈)。

如果你想要一本能很好地解释recursion的书,可以看一下Douglas Hofstadter的“ 哥德尔,埃舍尔,巴赫:一条永恒的黄金辫子” ,特别是第五章。除了recursion之外,它还能很好地解释计算机科学和math方面的一些复杂概念是可以理解的,一个解释就是另一个解释。 如果你之前没有太多的接触这些概念的话,那可能是一本相当不错的书。

我认为这个非常简单的方法应该可以帮助你理解recursion。 该方法将调用自己,直到某个条件为真,然后返回:

 function writeNumbers( aNumber ){ write(aNumber); if( aNumber > 0 ){ writeNumbers( aNumber - 1 ); } else{ return; } } 

这个函数会打印出第一个数字的所有数字,直到0为止。因此:

 writeNumbers( 10 ); //This wil write: 10 9 8 7 6 5 4 3 2 1 0 //and then stop because aNumber is no longer larger then 0 

writeNumbers(10)将写入10,然后调用writeNumbers(9),它将写入9,然后调用writeNumber(8)等。直到writeNumbers(1)写入1,然后调用writeNumbers(0),它将写入0 butt将不会调用writeNumbers(-1);

这段代码基本上是一样的:

 for(i=10; i>0; i--){ write(i); } 

那么为什么使用recursion可能会问,如果一个for循环基本相同。 那么你大多使用recursion的时候,你将不得不嵌套for循环,但不知道他们嵌套多深。 例如,从嵌套数组中打印项目时:

 var nestedArray = Array('Im a string', Array('Im a string nested in an array', 'me too!'), 'Im a string again', Array('More nesting!', Array('nested even more!') ), 'Im the last string'); function printArrayItems( stringOrArray ){ if(typeof stringOrArray === 'Array'){ for(i=0; i<stringOrArray.length; i++){ printArrayItems( stringOrArray[i] ); } } else{ write( stringOrArray ); } } printArrayItems( stringOrArray ); //this will write: //'Im a string' 'Im a string nested in an array' 'me too' 'Im a string again' //'More nesting' 'Nested even more' 'Im the last string' 

这个函数可以把一个数组嵌套到100个级别,而你编写一个for循环会要求你嵌套100次:

 for(i=0; i<nestedArray.length; i++){ if(typeof nestedArray[i] == 'Array'){ for(a=0; i<nestedArray[i].length; a++){ if(typeof nestedArray[i][a] == 'Array'){ for(b=0; b<nestedArray[i][a].length; b++){ //This would be enough for the nestedAaray we have now, but you would have //to nest the for loops even more if you would nest the array another level write( nestedArray[i][a][b] ); }//end for b }//endif typeod nestedArray[i][a] == 'Array' else{ write( nestedArray[i][a] ); } }//end for a }//endif typeod nestedArray[i] == 'Array' else{ write( nestedArray[i] ); } }//end for i 

正如你所看到的,recursion方法好得多。

这不仅仅是一个问题,而是一个投诉。 你有recursion更具体的问题吗? 像乘法一样,这不是人们写的很多东西。

说到乘法,想想这个。

题:

什么是* b?

回答:

如果b是1,那就是a。 否则,它是一个+ a *(b-1)。

什么是*(b-1)? 看到上面的问题的方式来解决。

其实你使用recursion来减less你的问题的复杂性。 你申请recursion,直到你到达一个简单的基本情况,可以很容易地解决。 有了这个,你可以解决最后一个recursion的步骤。 用这个所有其他recursion步骤来解决你原来的问题。

recursion

方法A,调用方法A调用方法A.最终这些方法A中的一个不会调用并退出,但是它是recursion的,因为某些东西自己调用。

recursion的例子,我想打印出硬盘驱动器上的每个文件夹名称:(在C#中)

 public void PrintFolderNames(DirectoryInfo directory) { Console.WriteLine(directory.Name); DirectoryInfo[] children = directory.GetDirectories(); foreach(var child in children) { PrintFolderNames(child); // See we call ourself here... } } 

我会试着用一个例子来解释它。

你知道吗? 手段? 如果不是: http : //en.wikipedia.org/wiki/Factorial

3! = 1 * 2 * 3 = 6

这里有一些伪代码

 function factorial(n) { if (n==0) return 1 else return (n * factorial(n-1)) } 

那么让我们试试看:

 factorial(3) 

是n 0?

没有!

所以我们深入挖掘我们的recursion:

 3 * factorial(3-1) 

3-1 = 2

是2 == 0?

没有!

所以我们更深入! 3 * 2 *阶乘(2-1)2-1 = 1

是1 == 0?

没有!

所以我们更深入! 3 * 2 * 1 *阶乘(1-1)1-1 = 0

是0 == 0?

是!

我们有一个微不足道的例子

所以我们有3 * 2 * 1 * 1 = 6

我希望帮助你

你正在使用哪本书?

实际上algorithm的标准教科书是Cormen&Rivest。 我的经验是,它很好地教授recursion。

recursion是编程难以掌握的部分之一,虽然它确实需要本能,但可以学习。 但它确实需要一个很好的描述,很好的例子和很好的插图。

另外,一般来说30页是很多的,单个编程语言的30页是令人困惑的。 不要试图学习C或Java中的recursion,在你理解一般书籍的recursion之前。

recursion函数只是一个函数,它可以根据需要多次调用它自己。 如果您需要多次处理某些内容,这是非常有用的,但是您不确定实际需要多less次。 在某种程度上,您可以将recursion函数想象为一种循环types。 但是,像循环一样,您需要指定进程的条件被打破,否则将会变成无限的。

http://javabat.com是一个有趣和令人兴奋的练习recursion的地方。; 他们的例子开始相当轻,通过广泛的工作(如果你想采取这一点)。 注意:他们的做法是通过练习来学习。 这是我写的简单replacefor循环的recursion函数。

for循环:

 public printBar(length) { String holder = ""; for (int index = 0; i < length; i++) { holder += "*" } return holder; } 

这是recursion做同样的事情。 (注意我们重载第一个方法,以确保它像上面一样使用)。 我们还有另一种方法来维护我们的指数(类似于上面的for语句对你的方式)。 recursion函数必须保持自己的索引。

 public String printBar(int Length) // Method, to call the recursive function { printBar(length, 0); } public String printBar(int length, int index) //Overloaded recursive method { // To get a better idea of how this works without a for loop // you can also replace this if/else with the for loop and // operationally, it should do the same thing. if (index >= length) return ""; else return "*" + printBar(length, index + 1); // Make recursive call } 

长话短说,recursion是编写更less代码的好方法。 在后面的printBar通知我们有一个if语句。 如果我们的条件已经达到,我们将退出recursion并返回到前一个方法,返回到前一个方法等。如果我发送一个printBar(8),我得到********。 我希望用一个简单的函数的例子,做一个for循环,这可能会有所帮助。 尽pipe你可以在Java Bat上多练习一下。

看看构buildrecursion函数的真正的math方法如下:

1:假设你有一个对f(n-1)正确的函数,build立f使得f(n)是正确的。 2:构buildf,这样f(1)是正确的。

这就是你如何certificate函数是正确的,在math上,它被称为感应 。 它相当于具有不同的基本情况,或者在多个variables上具有更复杂的function)。 也相当于设想f(x)对于所有x是正确的

现在是一个“简单”的例子。 build立一个function,可以确定是否有可能有一个硬币组合5美分和7美分,使X美分。 例如,可能有17美分×2×5 + 1×7,但不可能有16美分。

现在假设你有一个函数,告诉你是否可以创buildx美分,只要x <n。 调用这个函数can_create_coins_small。 想象如何为n做function应该相当简单。 现在build立你的function:

 bool can_create_coins(int n) { if (n >= 7 && can_create_coins_small(n-7)) return true; else if (n >= 5 && can_create_coins_small(n-5)) return true; else return false; } 

这里的技巧是认识到can_create_coins适用于n的事实,意味着您可以将can_create_coins_smallreplace为can_create_coins,从而给出:

 bool can_create_coins(int n) { if (n >= 7 && can_create_coins(n-7)) return true; else if (n >= 5 && can_create_coins(n-5)) return true; else return false; } 

最后要做的就是有一个基本的例子来停止无限recursion。 请注意,如果您尝试创build0美分,那么可以通过没有硬币。 添加这个条件给出:

 bool can_create_coins(int n) { if (n == 0) return true; else if (n >= 7 && can_create_coins(n-7)) return true; else if (n >= 5 && can_create_coins(n-5)) return true; else return false; } 

可以certificate,这个函数总是会返回,使用一种称为无限下降的方法,但在这里并不是必须的。 你可以想象f(n)只调用n的较低值,并且总是最终达到0。

要使用这些信息来解决您的河内塔问题,我想诀窍是假设您有一个function,将n-1个平板电脑从a移动到b(对于任何a / b),尝试将n个表格从a移动到b 。

Common Lisp中的简单recursion示例:

MYMAP将一个函数应用于列表中的每个元素。

1)一个空的列表没有元素,所以我们返回空列表 – ()和NIL都是空列表。

2)将函数应用到第一个列表中,为列表的其余部分(recursion调用)调用MYMAP并将两个结果合并到一个新列表中。

 (DEFUN MYMAP (FUNCTION LIST) (IF (NULL LIST) () (CONS (FUNCALL FUNCTION (FIRST LIST)) (MYMAP FUNCTION (REST LIST))))) 

让我们看看被追踪的执行。 进入一个function,参数被打印。 在退出function时,打印结果。 对于每个recursion调用,输出都将在级别上缩进。

本例在列表中的每个数字上调用SIN函数(1 2 3 4)。

 Command: (mymap 'sin '(1 2 3 4)) 1 Enter MYMAP SIN (1 2 3 4) | 2 Enter MYMAP SIN (2 3 4) | 3 Enter MYMAP SIN (3 4) | | 4 Enter MYMAP SIN (4) | | 5 Enter MYMAP SIN NIL | | 5 Exit MYMAP NIL | | 4 Exit MYMAP (-0.75680256) | 3 Exit MYMAP (0.14112002 -0.75680256) | 2 Exit MYMAP (0.9092975 0.14112002 -0.75680256) 1 Exit MYMAP (0.841471 0.9092975 0.14112002 -0.75680256) 

这是我们的结果

 (0.841471 0.9092975 0.14112002 -0.75680256) 

孩子隐式地使用recursion,例如:

迪士尼世界之旅

我们到了吗?(不)

我们到了吗?(不久)

我们到了吗?(几乎…)

我们到了吗?(SHHHH)

我们到了吗?(!!!!!)

小孩睡着了

这个倒计时function就是一个简单的例子:

 function countdown() { return (arguments[0] > 0 ? ( console.log(arguments[0]),countdown(arguments[0] - 1)) : "done" ); } countdown(10); 

在使用recursion解决scheme时,我总是试着:

  • 首先build立基本情况,即在parsing阶乘n = 1时
  • 试着为其他案件提出一条通用的规则

也有不同types的recursion解决scheme,有分而治之的方法,这对于分形和许多其他方面很有用。

如果你可以先解决简单的问题,那么也可以帮助你解决问题。 一些例子是求解阶乘和生成第n个斐波纳契数。

对于参考文献,我强烈推荐Robert Sedgewick的algorithm。

希望有所帮助。 祝你好运。

哎哟。 去年我试图找出河内的塔楼。 关于TOH棘手的事情是它不是recursion的一个简单的例子 – 你有嵌套的recursion,这也改变了在每个电话塔的angular色。 我唯一能够理解的方法就是从头脑中将环的运动形象地expression出来,并将recursion调用的内容expression出来。 我会从一个单一的环开始,然后是两个,然后是三个。 我其实在网上订了游戏 我花了两三天的时间才弄明白了。

为了解释recursion到六岁,先解释给一个五岁,然后等一年。

实际上,这是一个有用的反例,因为recursion调用应该更简单,而不是更难。 将recursion解释为五岁更困难,虽然你可以在0时停止recursion,但是你没有简单的解决recursion到一个零岁的解决scheme。

要使用recursion来解决问题,首先将其分解成一个或多个可以用同样的方法解决的简单问题,然后当问题很简单而不需要进一步recursion求解时,就可以返回到更高的层次。

实际上,这是recursion定义如何解决recursion问题。

recursion函数就像每次调用时压缩一下弹簧。 在每一步中,您都将一些信息(当前上下文)放在堆栈上。 当最后一步到达时,弹簧被释放,立即收集所有的值(上下文)!

不确定这个比喻是否有效… 🙂

无论如何,除了经典的例子(最差的例子,因为效率低下,易于扁平化,斐波纳契,河内等),这些都是有点人为的(我很less在实际的编程案例中使用它们)有趣的是看看它真的在哪里使用。

一个非常普遍的情况是走树(或者图,但是树通常是更普遍的)。
例如,一个文件夹层次结构:列出文件,你迭代它们。 如果您find一个子目录,列出这些文件的函数会以新文件夹作为参数调用它自己。 当从列出这个新文件夹(及其子文件夹!)回来时,它恢复其上下文到下一个文件(或文件夹)。
Another concrete case is when drawing a hierarchy of GUI components: it is common to have containers, like panes, to hold components which can be panes too, or compound components, etc. The painting routine calls recursively the paint function of each component, which calls the paint function of all the components it holds, etc.

Not sure if I am very clear, but I like to show real world use of teaching material, as it was something I was stumbling upon in the past.

Think a worker bee. It tries to make honey. It does its job and expects other worker bees to make rest of the honey. And when the honeycomb is full, it stops.

Think it as magic. You have a function that has the same name with the one you are trying to implement and when you give it the subproblem, it solves it for you and the only thing you need to do is to integrate the solution of your part with the solution it gave you.

For example, we want to calculate the length of a list. Lets call our function magical_length and our magical helper with magical_length We know that if we give the sublist which does not have the first element, it will give us the length of the sublist by magic. Then only thing we need to think is how to integrate this information with our job. The length of the first element is 1 and magic_counter gives us the length of sublist n-1, therefore total length is (n-1) + 1 -> n

 int magical_length( list ) sublist = rest_of_the_list( list ) sublist_length = magical_length( sublist ) // you can think this function as magical and given to you return 1 + sublist_length 

However this answer is incomplete because we didn't consider what happens if we give an empty list. We thought that the list we have always has at least one element. Therefore we need to think about what should be the answer if we are given an empty list and answer is obviously 0. So add this information to our function and this is called base/edge condition.

 int magical_length( list ) if ( list is empty) then return 0 else sublist_length = magical_length( sublist ) // you can think this function as magical and given to you return 1 + sublist_length