Что означает yield в PHP?

Я недавно наткнулся на этот код:

function xrange($min, $max) 
{
    for ($i = $min; $i <= $max; $i++) {
        yield $i;
    }
}

Я никогда не видел этого yield ключевое слово раньше. Попытка запустить код, который я получаю

Parse error: синтаксическая ошибка, непредвиденная T_VARIABLE в строке x

что это yield сайта? Это даже действительный PHP? И если да, то как мне его использовать?

5 ответов


что это yield?

на yield ключевое слово возвращает данные из функции генератора:

сердцем функции генератора является ключевое слово yield. В своей простейшей форме оператор yield очень похож на оператор return, за исключением того, что вместо остановки выполнения функции и возврата yield вместо этого предоставляет значение циклу кода над генератором и приостанавливает выполнение генератора функция.

что такое функция генератора?

функция генератора является более компактным и эффективным способом записи итератор. Он позволяет определить функцию (your xrange), что составит вычислить и возвратить значения пока вы петля над ним:

foreach (xrange(1, 10) as $key => $value) {
    echo "$key => $value", PHP_EOL;
}

это создаст следующий вывод:

0 => 1
1 => 2
…
9 => 10

вы также можете контролировать $key на foreach С помощью

yield $someKey => $someValue;

в функции генератора, $someKey это то, что вы хотите появиться для $key и $someValue быть значением в $val. В примере вопроса это $i.

в чем разница с обычными функциями?

теперь вы можете задаться вопросом, почему мы не просто используем родной PHP range функции для достижения этого вывода. И ты прав. Результат будет таким же. Разница как мы туда попали.

при использовании range PHP, выполнит его, создаст весь массив чисел в памяти и return это весь массив до foreach цикл, который затем перейдет через него и выведет значения. Другими словами,foreach будет работать на сам массив. The NikiC это: кооперативная многозадачность, используя сопрограммы в PHP

  • совместная многозадачность PHP
  • что такое разница между генератором и массивом?
  • Википедия на генераторах в целом

  • yield ключевое слово служит для определения "генераторов" в PHP 5.5. Хорошо, тогда что такое генератор?

    от php.net:

    генераторы обеспечивают простой способ реализации простых итераторов без накладных расходов или сложности реализации класса, реализующего интерфейс итератора.

    генератор позволяет писать код, который использует foreach для итерации по набору данных без необходимости создания массива в памяти, что может привести к превышению лимита памяти или потребовать значительного времени обработки для генерации. Вместо этого вы можете написать функцию генератора, которая такая же, как и обычная функция, за исключением того, что вместо возврата один раз генератор может давать столько раз, сколько ему нужно, чтобы предоставить значения, которые будут повторяться.

    из этого места: генераторы = генераторы, Другие функции (просто простые функции) = функции.

    Итак, они полезны когда:

    • вам нужно делать простые вещи (или простые вещи);

      генератор действительно намного проще, чем реализация интерфейса итератора. другая рука, ofcource, что генераторы менее функциональны. сравнивать их.

    • вам нужно генерировать большие объемы памяти для сохранения данных;

      фактически для сохранения памяти мы можем просто генерировать необходимые данные с помощью функций для каждая итерация цикла и после итерации используют мусор. Итак, здесь основные моменты-четкий код и, вероятно, производительность. посмотрим, что лучше для ваших нужд.

    • вам нужно сгенерировать последовательность, которая зависит от промежуточных значений;

      Это продолжение предыдущей мысли. генераторы могут сделать вещи проще по сравнению с функциями. проверка пример Фибоначчи, и постараемся сделать последовательность без генератора. Также генераторы могут работать быстрее в этом случае, по крайней мере, из-за хранения промежуточных значений в локальных переменных;

    • вам нужно улучшить представление.

      в некоторых случаях они могут работать быстрее, чем функции (см. предыдущее преимущество);


    простой пример:

    <?php
    echo '#start main# ';
    function a(){
        echo '{start[';
        for($i=1; $i<=9; $i++)
            yield $i;
        echo ']end} ';
    }
    foreach(a() as $v)
        echo $v.',';
    echo '#end main#';
    ?>
    

    выход

    #start main# {start[1,2,3,4,5,6,7,8,9,]end} #end main#
    

    эта функция с использованием yield:

    function a($items) {
        foreach ($items as $item) {
            yield $item + 1;
        }
    }
    

    почти то же самое, что и этот без:

    function b($items) {
        $result = [];
        foreach ($items as $item) {
            $result[] = $item + 1;
        }
        return $result;
    }
    

    С одной только разницей, что a() возвращает генератор и b() простой массив. Вы можете повторить оба.

    кроме того, первый не выделяет полный массив и поэтому требует меньше памяти.


    С yield вы можете легко описать точки останова между несколькими задачами в одной функции. Вот и все, ничего особенного.

    $closure = function ($injected1, $injected2, ...){
        $returned = array();
        //task1 on $injected1
        $returned[] = $returned1;
    //I need a breakpoint here!!!!!!!!!!!!!!!!!!!!!!!!!
        //task2 on $injected2
        $returned[] = $returned2;
        //...
        return $returned;
    };
    $returned = $closure($injected1, $injected2, ...);
    

    если task1 и task2 сильно связаны, но вам нужна точка останова между ними, чтобы сделать что-то еще:

    • свободная память между обработкой строк базы данных
    • выполнить другие задачи, которые обеспечивают зависимость от следующей задачи, но которые не связаны с пониманием текущего код
    • выполнение асинхронных вызовов и ожидание результатов
    • и так далее ...

    тогда генераторы-лучшее решение, потому что вам не нужно разбивать свой код на множество замыканий или смешивать его с другим кодом или использовать обратные вызовы и т. д... Вы просто используете yield чтобы добавить точку останова, и вы можете продолжить с этой точки останова, если вы готовы.

    добавить точку останова без генераторов:

    $closure1 = function ($injected1){
        //task1 on $injected1
        return $returned1;
    };
    $closure2 = function ($injected2){
        //task2 on $injected2
        return $returned1;
    };
    //...
    $returned1 = $closure1($injected1);
    //breakpoint between task1 and task2
    $returned2 = $closure2($injected2);
    //...
    

    добавить точку останова с генераторы

    $closure = function (){
        $injected1 = yield;
        //task1 on $injected1
        $injected2 = (yield($returned1));
        //task2 on $injected2
        $injected3 = (yield($returned2));
        //...
        yield($returnedN);
    };
    $generator = $closure();
    $returned1 = $generator->send($injected1);
    //breakpoint between task1 and task2
    $returned2 = $generator->send($injected2);
    //...
    $returnedN = $generator->send($injectedN);
    

    Примечание: легко ошибиться с генераторами, поэтому всегда пишите модульные тесты, прежде чем реализовать их! note2: использование генераторов в бесконечном цикле похоже на запись замыкания, которое имеет бесконечную длину...