官术网_书友最值得收藏!

4.4 生成器(Generator)與yield

前面在1.1.8小節講述過迭代器。一個迭代器是這樣一個容器,它實現了Traversable接口,從而能夠遍歷容器中的內部元素。PHP中最常見的迭代器是數組,能使用foreach來遍歷所有的元素。

與迭代器密切相關的概念是生成器(Generator)和yield。

4.4.1 生成器

為了解釋生成器,我們先看一個現實中的例子。

假設某工廠有5000名工人,為解決就餐問題開設了食堂,每餐每人有1碗米飯1盤菜。工人打飯時有兩個方案:

方案1:食堂工作人員把5000份套餐全部打包好,放在桌子上,依次發給工人。

方案2:工人排隊,食堂工作人員依次給工人打飯。

現實生活中第1種方案并不常見,因為事先把所有套餐全部打包好,需要占用餐廳5000個位置來放置這些飯盒,很浪費空間;而采用第2種方案,米飯和菜只需分別放在一個大桶里,隨用隨取即可。

將這個例子放到程序里可以有如下表示:

1.數組里有5000個元素

2.采用自定義的生成函數生成

(源碼文件:ch04/generator.php)

采用memory_get_usage方法打印出兩種方案所需的內存使用量:

方案1:968912

方案2:226504

可以看到方案2的內存使用量僅是方案1的1/4。方案2也有其他優點:實現簡單,1個人就可以打飯;方案1裝滿5000份套餐所需時間太長,后面裝好前面就涼了,方案2隨用隨取。

生成器是用來生成迭代器的函數,其優點有以下三個:

● 實現簡單。

● 避免分配大塊內存,防止程序超過內存限制。

● 避免生成迭代器的執行時間過長。

4.4.2 yield

yield應用于生成器函數里,類似于return,但略有不同:

● yield可以有多個。

● yield會記住上次返回的值,下次調用時會返回下一個yield。

例如以下示例中,有3個yield,每次遍歷時會依次返回1,2,3的值,從而實現遍歷。

(源碼文件:ch04/yield_demo)

4.4.3 生成器的設計

Generator可以生成一個可迭代的容器,顯然這個容器的容量是有限的。那么設計生成器時,就引出一個問題:是由Generator控制數目,還是在循環中控制呢?這里分享一個規則:

● 規則1:當數據有一定規則可循時,可以由Generator控制數目。例如生成奇數,計算階乘等。

● 規則2:當數據無規則可循或隨機時,一般在循環中控制。

為了便于理解,我們實現兩個Generator。

1.生成奇數

編寫一個函數,當輸入n時,輸出n個奇數。

程序代碼如下:(源碼文件:ch04/generator_odd.php)

2.生成uuid

uuid是通用唯一識別碼(Universally Unique Identifier),理論上每個uuid都是全局唯一的,這在生成不重復的標識符時非常有用,如訂單號、物流號。按照概率論計算,一個人每年被隕石擊中的概率大概是170億分之一,而兩個uuid重復的概率比這還小。

程序示例如下:(源碼文件:ch04/generator_uuid.php)

這個例子中,采用外部變量$num來控制遍歷的數目,一旦達到數量限制,遍歷過程會被break,從而控制了循環的流程。

4.4.4 面試題:用yield實現斐波那契數列

題目描述:輸入n,返回斐波那契數列的前n個數,要求用yield實現。

解答:斐波那契數列的每一項都是前兩個數之和,例如:

0,1,1,2,3,5,8,13,...

實現代碼如下:(源碼文件:ch04/yield_fibonacci.php)

引申思考:為什么不用遞歸實現斐波那契數列呢?

遞歸實現斐波那契數列的確比較簡單,但存在重復計算的問題;而yield實現(這種方法叫動態規劃)時,能夠暫存子問題的結果,因此效率更高些。

主站蜘蛛池模板: 新竹县| 岑巩县| 惠东县| 长治市| 宁武县| 襄垣县| 两当县| 陕西省| 洛南县| 蒲城县| 营山县| 武宣县| 永嘉县| 临高县| 花莲市| 蓬安县| 溆浦县| 广灵县| 新郑市| 绥中县| 桂平市| 淅川县| 启东市| 三江| 奉化市| 曲沃县| 恩施市| 托里县| 芦山县| 渭源县| 鄂托克旗| 西乡县| 定日县| 望奎县| 绥芬河市| 青田县| 政和县| 渑池县| 商河县| 田林县| 安仁县|