循环和递归的区别在于是否在迭代运算体内部决定迭代结束。
就像两个小孩子玩球。玩一轮如果很开心,就再玩一轮。在某一轮两个人吵起来了,打起来了。
递归是两个小孩子自己决定,哼,玩不下去了,不玩了,滚。
循环是旁边一直坐着个大人,看到两个小孩子一轮玩得开心,就让他们继续玩下一轮。看到他们吵起来了,就把他们分开,结束玩球游戏。
尾递归就是在每一轮游戏的“完全结束”才(调用自己)开始下一轮游戏。每一轮游戏都是完结的,相关数据(比分之类的)在每一轮都已经结算清楚。
不能够改写成尾递归的递归是因为每一轮都只玩到一半就玩下一轮了。等游戏的最后一轮完成后,要一轮又一轮地往回走,把之前没完成的游戏玩完。为什么这样呢?因为每一轮要玩好,需要下一轮游戏的数据(第n轮的游戏需要n+1轮的数据来决定第n轮的结果)。这玩意儿是比较难理解,因为我们现实生活的时间是不可倒退的。可以打个比喻——
一个女孩子在考虑是否和一个男生在一起,男生说即使在他死那天,他还会爱着她。女生想:我能相信他明天还会爱我,如果他后天还会爱我。我能相信他后天还会爱我,如果他大后天还爱我。即,因为相信他第n+1天会爱我,我就能相信他第n天是会爱我的。既然这样,那从他死那天往回走,他每天都是爱我的,那他后天,明天,今天都是爱我的。
再换回两个小孩子玩球的游戏里。在两个人吵起来的最后一轮,如果是循环(大人喊结束游戏)或者尾递归(小孩子自己决定结束游戏),整个玩球活动都到处结束,如果需要,可以直接返回比分。
而在不能重构成尾递归的递归里,两个小孩子在最后一轮决定不继续玩后,想起上一轮还没玩完“哎,上一轮你还欠我两个球,来来来,我们返回到上一轮的环境里继续玩”。这样一直返回,直到开局的第一轮,最终得出最后比分。
这也是为什么递归比循环更耗资源,因为每一轮玩球的上下文(命名空间)都要保存下来,以便在最后一轮结束后往回走的时候可以继续之前没玩完的流程继续走下去玩完当轮游戏。