循環和遞歸的區別在于是否在迭代運算體內部決定迭代結束。
就像兩個小孩子玩球。玩一輪如果很開心,就再玩一輪。在某一輪兩個人吵起來了,打起來了。
遞歸是兩個小孩子自己決定,哼,玩不下去了,不玩了,滾。
循環是旁邊一直坐著個大人,看到兩個小孩子一輪玩得開心,就讓他們繼續玩下一輪。看到他們吵起來了,就把他們分開,結束玩球游戲。
尾遞歸就是在每一輪游戲的“完全結束”才(調用自己)開始下一輪游戲。每一輪游戲都是完結的,相關數據(比分之類的)在每一輪都已經結算清楚。
不能夠改寫成尾遞歸的遞歸是因為每一輪都只玩到一半就玩下一輪了。等游戲的最后一輪完成后,要一輪又一輪地往回走,把之前沒完成的游戲玩完。為什么這樣呢?因為每一輪要玩好,需要下一輪游戲的數據(第n輪的游戲需要n+1輪的數據來決定第n輪的結果)。這玩意兒是比較難理解,因為我們現實生活的時間是不可倒退的。可以打個比喻——
一個女孩子在考慮是否和一個男生在一起,男生說即使在他死那天,他還會愛著她。女生想:我能相信他明天還會愛我,如果他后天還會愛我。我能相信他后天還會愛我,如果他大后天還愛我。即,因為相信他第n+1天會愛我,我就能相信他第n天是會愛我的。既然這樣,那從他死那天往回走,他每天都是愛我的,那他后天,明天,今天都是愛我的。
再換回兩個小孩子玩球的游戲里。在兩個人吵起來的最后一輪,如果是循環(大人喊結束游戲)或者尾遞歸(小孩子自己決定結束游戲),整個玩球活動都到處結束,如果需要,可以直接返回比分。
而在不能重構成尾遞歸的遞歸里,兩個小孩子在最后一輪決定不繼續玩后,想起上一輪還沒玩完“哎,上一輪你還欠我兩個球,來來來,我們返回到上一輪的環境里繼續玩”。這樣一直返回,直到開局的第一輪,最終得出最后比分。
這也是為什么遞歸比循環更耗資源,因為每一輪玩球的上下文(命名空間)都要保存下來,以便在最后一輪結束后往回走的時候可以繼續之前沒玩完的流程繼續走下去玩完當輪游戲。