国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C# > 正文

C#函數式編程中的遞歸調用之尾遞歸詳解

2020-01-24 02:10:02
字體:
來源:轉載
供稿:網友

關于遞歸相信大家已經熟悉的不能再熟悉了,所以筆者在這里就不多費口舌,不懂的讀者們可以在博客園中找到很多與之相關的博客。下面我們直接切入正題,開始介紹尾遞歸。

尾遞歸

普通遞歸和尾遞歸如果僅僅只是從代碼的角度出發來看,我們可能發現不了他的特點,所以筆者利用兩張堆棧上的圖來展示具體的差距在哪,首先我們來看看普通的遞歸調用的情況,如下圖1.1所示:

假設這里執行的函數是Func1,并且Func1中通過遞歸調用了自己,那么我們可以看到棧上在每次調用Func1的時候都會重新將函數返回地址等其他參數放入棧中,在遞歸次數較少的情況下,這樣是不會有問題的。但如果遞歸調用次數達到一定的數量級,則會將棧空間消耗光。因此,就提出了尾遞歸。而尾遞歸的棧圖如1.2所示:

一樣還是遞歸,但是每次執行自身的時候并不會在棧空間中申請新的空間,類似于for循環的效果,面對遞歸次數很多的情況下也不會出現什么問題。但是新的問題就出來了,在C#中編譯器不會做到這一步優化,而是在jit編譯器執行時才會進行優化。并且只有64位才進行優化。在語言的層面上我們也要遵守一定的原則,才能讓編譯器知道去優化。當然有些喜歡看博客的人可能早就知道尾遞歸就是在最后return的時候調用自身。我們可以通過一串示意代碼來看尾調用:

復制代碼 代碼如下:

int Func1()
{
  return Func1();
}

當然上面這串代碼會形成一個死循環,因為這里我們沒有基線條件。下面我們舉一個例子:

這個函數想必應該會比較熟悉,就是計算階乘的。但是我們可以發現函數sunfc最后的返回語句并不是直接調用函數本身,而是x*sfunc(x -1),恰恰就是因為前面這個x*就會導致編譯器無法優化,從而只能采用普通的遞歸調用的方式去執行,那么我們就需要利用一些模式去改變,首先我們先介紹的是“累加器傳遞模式”,可能名字比較懸乎,其實就是將當前的計算結果傳遞給下一次調用函數中,這樣當到達基線條件后直接根據上次計算的結果算出最終結果返回即可,如果將上面的代碼采用這個模式就是下面這個樣子:

采用這個模式之后我們就變回了尾遞歸了,當執行到基線條件時,直接返回y的值即可。根本不需要回溯到以前。除了利用這種模式,我們還可以利用一種“后繼傳遞模式”,跟累加器傳遞模式一樣也需要修改函數簽名,增加一個參數,我們繼續修改上面這串代碼:

 

相比累加器傳遞模式,這種方式比較難理解,其實sfunc在到達基線條件時y就等同于下面這個lambda表達式:a => a*4*3*2,然后就是調用y(1)就直接計算最終的結果了。在簡單點就是y這個函數被包裝了了好幾層,比如上面這段函數執行結束時y的調用順序:

a為1傳遞給y(2 * a),結果就是y(2)。

a為2傳遞給y(3 * a),結果就是y(6)。

a為6傳遞給y(4 * a),結果就是y(24)。

a為24傳遞給x => x,輸出24。


如果還是不理解只能下斷點,調試自己琢磨琢磨了,實在不懂的可以Q問。

 

在滿足必要的經濟的條件下,研究更加高深的技術.滿足自己的野心

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 土默特右旗| 湛江市| 呈贡县| 教育| 郯城县| 临朐县| 达州市| 太康县| 景宁| 双江| 巴中市| 广德县| 来凤县| 涪陵区| 彭山县| 临安市| 乐昌市| 灵寿县| 鹰潭市| 阜康市| 长武县| 都昌县| 澄迈县| 拉孜县| 鸡泽县| 梨树县| 台湾省| 教育| 西充县| 阿鲁科尔沁旗| 无极县| 北碚区| 德保县| 崇义县| 兖州市| 盘锦市| 平顶山市| 布尔津县| 富民县| 高唐县| 平阴县|