成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

大家都知道遞歸,尾遞歸呢?什么又是尾遞歸優化?

開發 前端
今天,我們來聊聊遞歸函數。為啥突然想到遞歸?其實就從電影名字《恐怖游輪》《盜夢空間》想到了。

 今天,我們來聊聊遞歸函數。為啥突然想到遞歸?其實就從電影名字《恐怖游輪》《盜夢空間》想到了。

[[327759]]

 

遞歸是啥?

 

遞歸函數大家肯定寫過,學校上課的時候,估計最開始的例子就是斐波拉契數列了吧。例如:

  1. int Fibonacci(n) { 
  2.     if (n < 2) return n; 
  3.     return Fibonacci(n - 1) + Fibonacci(n - 2); 

遞歸函數簡而言之就是在一個函數中,又“遞歸”調用自己。在寫遞歸函數的時候,需要注意的地方就是遞歸函數的結束條件。用遞歸函數確實能簡化很多算法的實現,比如常見的二叉樹遍歷等。但往往在寫遞歸函數的時候,最容易出現的問題就是所謂的“棧溢出”。

為什么會有“棧溢出”呢?因為函數調用的過程,都要借助“棧”這種存儲結構來保存運行時的一些狀態,比如函數調用過程中的變量拷貝,函數調用的地址等等。而“棧”往往存儲空間是有限的,當超過其存儲空間后,就會拋出著名的異常/錯誤“StackOverflowError”。

我們以一個簡單的加法為例,例如:

  1. int sum(int n) { 
  2.     if (n <= 1) return n; 
  3.     return n + sum(n-1); 
  4.  
  5. std::cout << sum(100) << std::endl; 
  6. std::cout << sum(1000000) << std::endl; 

很簡答,編譯運行后,比較小的數字,能得到正確的答案,當數字擴大后,就會直接發生“segmentation fault”。

尾遞歸又是啥?

 

我得知這個概念,最開始還是因為很多年前一次面試,面試官問我“你知道什么是尾遞歸嗎?”,我以為是“偽”遞歸,難道是假的遞歸???當初我也是懵逼狀態(當初面試官忍住沒笑也是厲害了)。從“尾”字可看出來即若函數在尾巴的地方遞歸調用自己。上面的例子寫成尾遞歸,就變成了如下:

  1. int tailsum(int n, int sum) { 
  2.     if (n == 0) return sum
  3.     return tailsum(n-1, sum+n); 

可以試試結果,計算從 1 加到 1000000,仍然是segmentation fault。為什么呢?因為這種寫法,本質上還是有多層的函數嵌套調用,中間仍然有壓棧、出棧等占用了存儲空間(只不過能比前面的方法會省部分空間)。

尾遞歸優化

 

當你給編譯選項開了優化之后,見證奇跡的時刻到了,居然能算出正確結果。如圖所示:

 

 

C++ 默認 segmentation fault, 開啟編譯優化后,能正常計算結果。

 

原因就是因為編譯器幫助做了尾遞歸優化,可以打開匯編代碼看看(這里就不展示 C++的了)。后面我用大家比較熟悉的 JVM based 語言 Scala 來闡述這個優化過程。(好像 Java 的編譯器沒做這方面的優化,至少我實驗我本地 JDK8 是沒有的,不清楚最新版本的有木有)(scala 本身提供了一個注解幫助編譯器強制校驗是否能夠進行尾遞歸優化@tailrec)

  1. object TailRecObject { 
  2.  
  3.    def tailSum(n: IntsumInt): Int = { 
  4.         if (n == 0) return sum
  5.         return tailSum(n-1, n+sum); 
  6.    } 
  7.  
  8.    def main(args: Array[String]) { 
  9.       println(tailSum(100, 0)) 
  10.       println(tailSum(1000000, 0)) 
  11.    } 
  12.  

結果如下圖所示,默認情況下 scalac 做了尾遞歸優化,能夠正確計算出結果,當通過 -g:notailcalls 編譯參數去掉尾遞歸優化后,就發生了 Exception in thread "main" java.lang.StackOverflowError了。

 

默認啟用尾遞歸優化正常計算結果,禁用尾遞歸優化則“StackOverflow”。

 

我們來看看生成的字節碼有什么不同。

 

包含尾遞歸優化的字節碼,直接 goto 循環。

 

禁用尾遞歸優化的字節碼,方法調用。

 

從上面可以看出,尾遞歸優化后,變成循環了(前面的 C++ 類似)。

好了,尾遞歸咱們就了解到這里。個人看法,我們知道有“尾遞歸”這個點就好了,有時候我們寫遞歸就是為了方便,代碼可讀性好,如果確實是出于性能考慮,我們可以自己用迭代的方式去實現,不依賴于具體的編譯器實現。當然對于像 scala 這樣,有一些語法糖能夠幫助校驗和驗證,也是一個不錯的選擇。但遞歸轉迭代的能力,我們能具備豈不更好。

本文轉載自微信公眾號「 程序猿石頭」,可以通過以上二維碼關注。轉載本文請聯系 程序猿石頭公眾號。

 

責任編輯:武曉燕 來源: 程序猿石頭
相關推薦

2010-09-17 13:01:44

Python

2009-07-22 07:44:00

Scala尾遞歸

2021-03-24 10:00:32

Python遞歸函數Python基礎

2020-09-30 08:07:46

如何優化尾調用

2022-10-10 08:13:16

遞歸通用代碼

2019-09-16 08:32:59

遞歸算法編程

2020-07-10 08:15:19

遞歸算法函數

2022-03-31 08:15:59

遞歸代碼非遞歸

2019-09-18 10:12:37

遞歸數據結構

2024-11-29 14:50:45

2009-11-17 16:53:24

PHP遞歸算法

2009-06-06 19:20:08

java麻將和牌程序遞歸

2012-02-22 14:12:08

算法

2013-11-11 10:03:29

遞歸路由

2010-04-02 15:04:14

Oracle遞歸查詢

2018-03-05 08:52:57

2015-07-08 16:06:15

iOS遞歸鎖

2020-02-21 16:43:00

C語言編程語言程序員

2021-09-15 07:40:50

二叉樹數據結構算法

2019-03-26 08:15:45

iOS尾調用Objective-C
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久最新 | 黄网站免费在线看 | 欧美日韩亚洲一区 | 日韩在线视频一区 | 国产一区二区在线视频 | 色网在线观看 | h小视频| 亚洲欧美日韩高清 | 99爱国产| 午夜小视频免费观看 | 国产一区二区小视频 | 九九伦理电影 | 99re在线视频观看 | 亚洲欧美高清 | 日韩欧美在线播放 | 成人一区二区在线 | 自拍偷拍欧美 | 国产激情视频网站 | 日本三级线观看 视频 | 91在线精品视频 | 黄色片视频免费 | 秋霞电影院午夜伦 | 亚洲精品观看 | 91在线中文字幕 | 精品在线观看一区 | 一区二区在线免费观看 | 亚洲高清在线观看 | 免费电影av | 二区av | 久久久久久久国产 | 超碰国产在线 | 日韩精品 电影一区 亚洲 | 日韩在线免费 | 国产日韩欧美 | 超碰520| 久久久高清 | 成人一区二区三区 | 人人做人人澡人人爽欧美 | 欧美一区二区大片 | 精品一二区 | av网站在线看|