国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

PHP 生成器入門

IamDLY / 892人閱讀

摘要:執(zhí)行語句的唯一目的是結(jié)束生成器執(zhí)行。這就是需要生成器需要有返回值的意義,這也是為何我們將這個(gè)特性加入到中的原因,我們會(huì)將最后執(zhí)行的值作為返回值,但這不是一個(gè)好的解決方案。

本文首發(fā)于 入門 PHP 生成器,轉(zhuǎn)載請(qǐng)注明出處。

PHP 在 5.5 版本中引入了「生成器(Generator)」特性,不過這個(gè)特性并沒有引起人們的注意。在官方的 從 PHP 5.4.x 遷移到 PHP 5.5.x 中介紹說它能以一種簡(jiǎn)單的方式實(shí)現(xiàn)迭代器(Iterator)。

生成器實(shí)現(xiàn)通過 yield 關(guān)鍵字完成。生成器提供一種簡(jiǎn)單的方式實(shí)現(xiàn)迭代器,幾乎無任何額外開銷或需要通過實(shí)現(xiàn)迭代器接口的類這種復(fù)雜方式實(shí)現(xiàn)迭代。

文檔提供了一個(gè)簡(jiǎn)單的實(shí)例演示這個(gè)簡(jiǎn)單的迭代器,請(qǐng)看下面的代碼:

function xrange($start, $limit, $step = 1) {
    for ($i = $start; $i <= $limit; $i += $step) {
        yield $i;
    }
}

讓我們將它與無迭代器支持的數(shù)組進(jìn)行比較:

foreach xrange($start, $limit, $step = 1) {
    $elements = [];
    
    for ($i = $start; $i <= $limit; $i += $step) {
        $elements[] = $i;
    }

    return $elements;
}

這兩個(gè)版本的函數(shù)都支持 foreach 迭代獲取所有元素:

foreach (xrange(1, 100) as $i) {
    print $i . PHP_EOL;
}

所以除了一個(gè)更短的函數(shù)定義,我們還能獲取什么呢?yield 到底做了什么?為什么在第一個(gè)函數(shù)定義時(shí)依然可以返回?cái)?shù)據(jù),即使沒有 return 語句?

先從返回值說起。生成器是 PHP 中的一個(gè)很特別的函數(shù)。當(dāng)一個(gè)函數(shù)包含 yield,那么這個(gè)函數(shù)即不再是一個(gè)普通函數(shù),它永遠(yuǎn)返回一個(gè)「Generator(生成器)」實(shí)例。生成器實(shí)現(xiàn)了 Iterator 接口,這就是為何它能夠進(jìn)行 foreach 遍歷的原因。

接下來我使用 Iterator 接口中的方法,對(duì)之前的 foreach 循環(huán)進(jìn)行重寫。你可以在 3v4l.org 查看結(jié)果。

$generator = xrange(1, 100);

while($generator->valid()) {
    print $generator->current() . PHP_EOL;

    $generator->next();
}

我們可以清楚的看到生成器是更高級(jí)的技術(shù),現(xiàn)在讓我們編寫一個(gè)新的生成器示例來更好的理解到底在生成器內(nèi)部是如何進(jìn)行處理的吧。

function foobar() {
    print "foobar - start" . PHP_EOL;

    for ($i = 0; $i < 5; $i++) {
        print "foobar - yielding..." . PHP_EOL;
        yield $i;
        print "foobar - continued..." . PHP_EOL;
    }

    print "foobar - end" . PHP_EOL;
}

$generator = foobar();

print "Generator created" . PHP_EOL;

while ($generator->valid()) {
    print "Getting current value from the generator..." . PHP_EOL;

    print $generator->current() . PHP_EOL;

    $generator->next();
}
Generator created
foobar - start
foobar - yielding...
Getting current value from the generator...
1
foobar - continued
foobar - yielding...
Getting current value from the generator...
2
foobar - continued
foobar - yielding...
Getting current value from the generator...
3
foobar - continued
foobar - yielding...
Getting current value from the generator...
4
foobar - continued
foobar - yielding...
Getting current value from the generator...
5
foobar - continued
foobar - end

嗯?為什么 Generator created 最先打印出來?這是因?yàn)樯善髟诒皇褂弥安粫?huì)執(zhí)行任何操作。在上例中就是$generator->valid()** 這句代碼才開始執(zhí)行生成器。我們看到生成器一直運(yùn)行到了第一個(gè) **yield** 時(shí),將控制流程交還給調(diào)用者 **$generator->valid()$generator->next() 調(diào)用時(shí)則恢復(fù)生成器執(zhí)行,到下一個(gè) yield 再次停止運(yùn)行,如此反復(fù)直到?jīng)]有更多的 yield 為止。我們現(xiàn)在擁有了可以在任何 yield 執(zhí)行暫停和回復(fù)的終端函數(shù)。這個(gè)特性允許編寫客戶端所需的延遲函數(shù)。

你可以創(chuàng)建一個(gè)從 GitHub API 讀取所有用戶的功能。支持分頁(yè)處理,但是你可以隱藏這些細(xì)節(jié)并且僅當(dāng)需要時(shí)再去獲取下一頁(yè)數(shù)據(jù)。你可以使用 yield 從當(dāng)前頁(yè)面獲取每個(gè)用戶數(shù)據(jù),直到當(dāng)前頁(yè)所有用戶獲取完成,你就可以再去獲取下一頁(yè)數(shù)據(jù)。

class GitHubClient {
    function getUsers(): Iterator {
        $uri = "/users";

        do {
            $response = $this->get($uri);
            foreach ($response->items as $user) {
                yield $user;
            }

            $uri = $response->nextUri;
        } while($uri !== null);
    }
}

客戶端可以迭代出所有用戶或者在任何時(shí)候停止遍歷。

把生成器當(dāng)?shù)魇褂谜媸菬o聊

是的,你的想法是對(duì)的。以上我給出的所有講解任何人都可以從 PHP 文檔中獲取到。但是作為迭代器這些使用,連它強(qiáng)大功能的一半都沒用到。生成器還提供了不屬于 Iterator 接口的 send()throw() 功能。我們前面談到了暫停和恢復(fù)生成器執(zhí)行功能。當(dāng)需要恢復(fù)生成器時(shí),不僅可以功過 Generator::next() 方法,還可以使用 Generator::send()Generator::throw()方法。

Generator::send() 允許你指定 yield 的返回值,而 Generator::throw() 允許向 yield 拋出異常。通過這些方法我們不僅可以從生成器中獲取數(shù)據(jù),還能向生成器中發(fā)送新數(shù)據(jù)。

讓我們看一個(gè)從 Cooperative multitasking using coroutines(強(qiáng)烈推薦閱讀本文)摘取的 Logger 日志示例。

function logger($filename) {
    $fileHandle = fopen($filename, "a");

    while (true) {
        fwrite($fileHandle, yield . "
");
    }
}

$logger = logger(__DIR__ . "/log");
$logger->send("Foo");
$logger->send("Bar");

yield 在這里是作為表達(dá)式使用的。當(dāng)我們發(fā)送數(shù)據(jù)時(shí),從 yield 返回?cái)?shù)據(jù)然后作為參數(shù)傳入到 fwrite()。

講真,這個(gè)示例在實(shí)際項(xiàng)目中沒毛用。它僅僅用于演示 Generator::send() 的使用原理,但是僅僅能夠發(fā)送數(shù)據(jù)并沒有太大作用。如果有一個(gè)類和普通函數(shù)支持的話就不一樣了。

使用生成器的樂趣來自于通過 yield 創(chuàng)建數(shù)據(jù),然后由「生成器執(zhí)行程序(generator runner)」依據(jù)這個(gè)數(shù)據(jù)來處理業(yè)務(wù),然后再繼續(xù)執(zhí)行生成器。這就是「協(xié)程(coroutines)」和「狀態(tài)流解析器(stateful streaming parsers)」實(shí)例。在講解協(xié)程和狀態(tài)流解析器之前,我們快速瀏覽一下如何在生成器中返回?cái)?shù)據(jù),我們還沒有將接觸這方面的知識(shí)。從 PHP 5.5 開始我們可以在生成器內(nèi)部使用 return; 語句,但是不能返回任何值。執(zhí)行 return; 語句的唯一目的是結(jié)束生成器執(zhí)行。

不過從 PHP 7.0 起支持返回值。這個(gè)功能在用于迭代時(shí)可能有些奇怪,但是在其他使用場(chǎng)景如協(xié)程時(shí)將非常有用,例如,當(dāng)我們?cè)趫?zhí)行一個(gè)生成器時(shí)我們可以依據(jù)返回值處理,而無需直接對(duì)生成器進(jìn)行操作。下一節(jié)我們將講解 return 語句在協(xié)程中的使用。

異步生成器

Amp 是一款 PHP 異步編程的框架。支持異步協(xié)程功能,本質(zhì)上是等待處理結(jié)果的占位符。「生成器執(zhí)行程序」為 Coroutine類。它會(huì)訂閱異步生成器(yielded promise),當(dāng)有執(zhí)行結(jié)果可用時(shí)則繼續(xù)生成器處理。如果處理失敗,則會(huì)拋出異常給生成器。你可以到 amphp/amp 版本庫(kù)查看實(shí)現(xiàn)細(xì)節(jié)。在 Amp 中的 Coroutine 本身就是一個(gè) Promise。如果這個(gè)協(xié)程拋出未經(jīng)捕獲的異常,這個(gè)協(xié)程就執(zhí)行失敗了。如果解析成功,那么就返回一個(gè)值。這個(gè)值看起來和普通函數(shù)的返回值并無二致,只不過它處于異步執(zhí)行環(huán)境中。這就是需要生成器需要有返回值的意義,這也是為何我們將這個(gè)特性加入到 PHP 7.0 中的原因,我們會(huì)將最后執(zhí)行的yield 值作為返回值,但這不是一個(gè)好的解決方案。

Amp 可以像編寫阻塞代碼一樣編寫非阻塞代碼,同時(shí)允許在同一進(jìn)程中執(zhí)行其它非阻塞事件。一個(gè)使用場(chǎng)景是,同時(shí)對(duì)一個(gè)或多個(gè)第三方 API 并行的創(chuàng)建多個(gè) HTTP 請(qǐng)求,但不限于此。得益于事件循環(huán),可以同時(shí)處理多個(gè) I/O 處理,而不僅僅是只能處理多個(gè) HTTP請(qǐng)求這類操作。

Loop::run(function() {
    $uris = [
        "https://google.com/",
        "https://github.com/",
        "https://stackoverflow.com/",
    ];

    $client = new AmpArtaxDefaultClient;
    $promises = [];

    foreach ($uris as $uri) {
        $promises[$uri] = $client->request($uri);
    }

    $responses = yield $promises;

    foreach ($responses as $uri => $response) {
        print $uri . " - " . $response->getStatus() . PHP_EOL;
    }
});

但是,擁有異步功能的協(xié)程并非只能夠在 yield 右側(cè)出現(xiàn)變量,還可以在它的左側(cè)。這就是我們前面提到的解析器。

$parse = new Parser((function(){
    while (true) {
        $line = yield "
";

        if (trim($line) === "") {
            continue;
        }

        print "New item: {$line}" . PHP_EOL;
    }
})());

for ($i = 0; $i < 100; $i++) {
    $parser->push("bar
");
    $parser->push("
foo");
}

解析器會(huì)緩存所有輸入直到接收的是 rn。這類生成器解析器并不能簡(jiǎn)化簡(jiǎn)單協(xié)議處理(如換行分隔符協(xié)議),但是對(duì)于復(fù)雜的解析器,如在服務(wù)器解析 HTTP 請(qǐng)求的 Aerys。

小結(jié)

生成器的功能遠(yuǎn)超多數(shù)人的認(rèn)知范圍。對(duì)于一些朋友來說可能是首次接觸生成器相關(guān)知識(shí),一些朋友可能已經(jīng)將它作為迭代器來使用,僅有很少一部分朋友使用生成器處理更多的事情。獲取你有一些很贊的想法?我很樂意進(jìn)一步探討這些項(xiàng)目,并且希望你能從中學(xué)習(xí)到一些知識(shí)。:)

如果你需要更多資料,我推薦你閱讀 nikic 寫的 使用生成器處理多任務(wù)。

原文

An Introduction to Generators in PHP

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/29054.html

相關(guān)文章

  • laravel入門

    摘要:開發(fā)根目錄測(cè)試分為單元測(cè)試和功能測(cè)試創(chuàng)建一個(gè)文件執(zhí)行測(cè)試測(cè)試前清除配置緩存運(yùn)行單個(gè)測(cè)試用例小提示在開發(fā)與進(jìn)行交互的第三方擴(kuò)展包時(shí),最好選擇注入契約而不使用。 參考https://laravelacademy.org/ 概念 單詞 契約Contract 就是接口 repository 倉(cāng)庫(kù)(封裝數(shù)據(jù)訪問,可以搜索:repository模式) Container 容器 ServicePr...

    韓冰 評(píng)論0 收藏0
  • PHP 成器Generators的入門理解和學(xué)習(xí)

    摘要:例子代碼上午內(nèi)存消耗量環(huán)境執(zhí)行命令結(jié)果返回內(nèi)存消耗量結(jié)果返回內(nèi)存消耗量測(cè)試結(jié)果論述通過代碼的執(zhí)行,比較除了標(biāo)準(zhǔn)函數(shù)和自定義函數(shù)之間的異同。標(biāo)準(zhǔn)函數(shù),該函數(shù)將轉(zhuǎn)換為一個(gè)。使用場(chǎng)景,可查閱參考資料參考資料實(shí)際生產(chǎn)中的使用 什么是生成器Generators 生成器允許你在 foreach 代碼塊中寫代碼來迭代一組數(shù)據(jù)而不需要在內(nèi)存中創(chuàng)建一個(gè)數(shù)組, 那會(huì)使你的內(nèi)存達(dá)到上限,或者會(huì)占據(jù)可觀的處理...

    Shimmer 評(píng)論0 收藏0
  • 最適合入門的Laravel初級(jí)教程(四)

    摘要:最適合入門的初級(jí)教程四路由可以分發(fā)請(qǐng)求路由中還可以引入頁(yè)面我們可以在中搞定一切了但是如果把業(yè)務(wù)邏輯都寫入到路由中那路由將龐大的難以維護(hù)于是控制器就有了很明顯的存在價(jià)值把業(yè)務(wù)邏輯寫在控制器中路由只負(fù)責(zé)轉(zhuǎn)發(fā)請(qǐng)求到指定的控制器即可那我們開始創(chuàng)建控 最適合入門的Laravel初級(jí)教程(四) 路由可以分發(fā)請(qǐng)求; 路由中還可以引入 html 頁(yè)面;我們可以在 route/web.php 中搞定一切...

    gplane 評(píng)論0 收藏0
  • 5分鐘PHP擴(kuò)展開發(fā)快速入門

    摘要:簡(jiǎn)單點(diǎn),先來實(shí)現(xiàn)一個(gè)擴(kuò)展的。接下來我們將使用它來生成我們的擴(kuò)展的基本骨架。注意不要添加任何分號(hào)。有興趣的同學(xué)可以自行研究一下靜態(tài)編譯是什么鬼在擴(kuò)展目錄中執(zhí)行命令。一定要在擴(kuò)展的目錄執(zhí)行才有效,否則將得到一個(gè)錯(cuò)誤提示。 簡(jiǎn)單點(diǎn),先來實(shí)現(xiàn)一個(gè)PHP擴(kuò)展的hello world。注意,以下所有操作都是基于linux系統(tǒng)(推薦centos和ubuntu, Mac系統(tǒng)應(yīng)該類似 ),PHP5.5以...

    RobinTang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<