Co oznacza yield w PHP?

Ostatnio natknąłem się na ten kod:

function xrange($min, $max) 
{
    for ($i = $min; $i <= $max; $i++) {
        yield $i;
    }
}
Nigdy nie widziałem tego słowa kluczowego. Próbuję uruchomić kod, który otrzymuję

Parse error: syntax error, unexpected t_variable on line x

Więc co to jest yield słowo kluczowe? Czy to w ogóle poprawne PHP? A jeśli tak, to jak go używać?

Author: k0pernikus, 2013-07-05

5 answers

Co to jest yield?

Słowo kluczowe yield zwraca dane z funkcji generatora:

Sercem funkcji generatora jest słowo kluczowe yield. W swojej najprostszej formie, Instrukcja yield wygląda podobnie do instrukcji return, z tym wyjątkiem, że zamiast zatrzymywać wykonywanie funkcji i zwracać, yield zamiast tego dostarcza wartość do kodu zapętlającego generator i wstrzymuje wykonywanie funkcji generatora.

Co to jest generator funkcja?

Funkcja generatora jest efektywniejszym i bardziej zwartym sposobem zapisu iteratora. Pozwala na zdefiniowanie funkcji (Twojej xrange), która obliczy i zwróci wartości podczas gdy jesteś zapętlając ją :

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

Utworzyłoby to następujące wyjście:

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

Możesz również kontrolować $key w foreach za pomocą

yield $someKey => $someValue;

W funkcji generatora, $someKey jest to, co chcesz pojawić się dla $key i $someValue będące wartością w $val. W przykładzie pytania jest to $i.

Jaka jest różnica w stosunku do normalnych funkcji?

Teraz możesz się zastanawiać, dlaczego nie używamy tylko natywnego PHPrange funkcja do osiągnięcia tego wyniku. I masz rację. Wynik byłby taki sam. Różnica polega na tym, jak się tam znaleźliśmy.

Kiedy użyjemy range PHP, wykonamy go, stworzymy całą tablicę liczb w pamięci i return że cała tablica do pętla foreach, która następnie przejdzie przez nią i wyświetli wartości. Innymi słowy, foreach będzie działać na samej tablicy. Funkcja range i foreach "mówią" tylko raz. Pomyśl o tym jak o dostawie paczki pocztą. Dostawca odda ci paczkę i wyjdzie. A potem rozpakowujesz całą paczkę, wyjmując wszystko, co tam jest.

Kiedy użyjemy funkcji generatora, PHP wkroczy do funkcji i wykona ją, dopóki nie spełni końca lub słowa kluczowego yield. Gdy spełnia yield, zwróci dowolną wartość w tym czasie do zewnętrznej pętli. Następnie wraca do funkcji generatora i kontynuuje z miejsca, w którym uległ. Ponieważ twoja xrange posiada pętlę for, będzie ona wykonywana i generowana do momentu osiągnięcia $max. Pomyśl o tym jak o foreach i generatorze grającym w ping ponga.

Po co mi to?

Oczywiście generatory mogą być używane do pracy z limitami pamięci. W zależności od otoczenia, Robienie range(1, 1000000) będzie fatal Twój skrypt, podczas gdy to samo z generatorem będzie po prostu działać dobrze. Albo jak to ujął Wikipedysta:

Ponieważ Generatory obliczają uzyskane wartości tylko na żądanie, są one przydatne do przedstawiania sekwencji, które byłyby kosztowne lub niemożliwe do obliczenia na raz. Obejmują one np. nieskończone sekwencje i strumienie danych na żywo.

Generatory również powinny być dość szybkie. Ale należy pamiętać, że kiedy mówimy o szybkim, zwykle mówimy w bardzo małe liczby. Więc zanim teraz uruchomić i zmienić cały kod do korzystania z generatorów, zrobić benchmark, aby zobaczyć, gdzie to ma sens.

Innym przypadkiem użycia generatorów są asynchroniczne koroutiny. Słowo kluczowe yield nie tylko zwraca wartości, ale także je akceptuje. Aby uzyskać szczegółowe informacje na ten temat, zobacz dwa doskonałe posty na blogu połączone poniżej.

Od kiedy mogę używać yield?

Generatory zostały wprowadzone w PHP 5.5 . Próba użycia yield zanim ta wersja będzie powoduje to różne błędy parsowania, w zależności od kodu, który następuje po słowie kluczowym. Więc Jeśli pojawi się błąd parsowania z tego kodu, zaktualizuj swoje PHP.

Źródła i dalsze informacje:

 250
Author: Gordon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-14 14:42:33

yield słowo kluczowe służy do definicji "generatorów" w PHP 5.5. Ok, więc co to jest generator ?

Od php.net:

Generatory zapewniają łatwy sposób na implementację prostych iteratorów bez narzutu ani złożoności implementacji klasy, która implementuje interfejs iteratora.

Generator pozwala na pisanie kodu, który używa foreach do iteracji zbioru danych bez konieczności budowania tablicy w pamięci, co może spowodować przekroczenie pamięci ogranicz lub wymagaj znacznego czasu przetwarzania do wygenerowania. Zamiast tego, możesz napisać funkcję generatora, która jest taka sama jak normalna funkcja, z tym wyjątkiem, że zamiast zwracać jeden raz, generator może uzyskać tyle razy, ile musi, aby dostarczyć wartości do iteracji.

Z tego miejsca: generatory = generatory, inne funkcje ( tylko proste funkcje) = funkcje.

Są więc przydatne, gdy:

  • Musisz zrobić rzeczy proste (lub proste rzeczy);

    Generator jest znacznie prostszy niż implementacja interfejsu iteratora. z drugiej strony, ofcource, że generatory są mniej funkcjonalne. porównaj je .

  • Musisz wygenerować duże ilości pamięci zapisującej dane;

    W rzeczywistości, aby zapisać pamięć, możemy po prostu wygenerować potrzebne dane za pomocą funkcji dla każdej iteracji pętli, a po iteracji użyć śmieci. więc tutaj najważniejsze jest-jasny kod i prawdopodobnie występ. zobacz, co jest lepsze dla Twoich potrzeb.

  • Musisz wygenerować sekwencję, która zależy od wartości pośrednich;

    To rozszerzenie poprzedniej myśli. generatory mogą ułatwić życie w porównaniu z funkcjami. sprawdź przykład Fibonacciego i spróbuj wykonać sekwencję bez generatora. Również generatory mogą pracować szybciej jest w tym przypadku, przynajmniej ze względu na przechowywanie wartości pośrednich w lokalnym zmienne;
  • Musisz poprawić wydajność.

    Mogą pracować szybciej niż funkcje w niektórych przypadkach (patrz poprzednia korzyść);

 20
Author: QArea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-05 15:10:18

Prosty przykład

<?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#';
?>

Wyjście

#start main# {start[1,2,3,4,5,6,7,8,9,]end} #end main#
 15
Author: Think Big,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-28 12:29:06

Ta funkcja z uzyskiem:

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

Jest prawie taki sam jak ten Bez:

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

Z tylko jedną różnicą, że a() zwraca generator i {[3] } tylko prostą tablicę. Możesz iterować na obu.

Ponadto, pierwszy z nich nie przydziela pełnej tablicy i dlatego jest mniej wymagający dla pamięci.

 11
Author: tsusanka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-24 08:11:03

Za pomocą yield można łatwo opisać punkty przerwania między wieloma zadaniami w jednej funkcji. To wszystko, nie ma w tym nic specjalnego.

$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, ...);

Jeśli task1 i task2 są wysoce powiązane, ale potrzebujesz punktu przerwania między nimi, aby zrobić coś innego:

  • wolna pamięć pomiędzy wierszami bazy danych
  • uruchamianie innych zadań, które dostarczają zależności do następnego zadania, ale które nie są ze sobą powiązane przez zrozumienie bieżącego kodu
  • Robienie połączeń asynchronicznych i czekanie dla wyników
  • i tak dalej ...

Wtedy generatory są najlepszym rozwiązaniem, ponieważ nie musisz dzielić kodu na wiele zamknięć, mieszać go z innym kodem, używać wywołań zwrotnych itp... Po prostu użyj yield, aby dodać punkt przerwania i możesz kontynuować z tego punktu przerwania, jeśli jesteś gotowy.

Dodaj punkt przerwania bez generatorów:

$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);
//...

Dodaj punkt przerwania za pomocą generatorów

$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);

Uwaga: łatwo jest popełnić błąd z generatorami, więc zawsze napisz testy jednostkowe przed ich wdrożeniem! note2: używanie generatorów w nieskończonej pętli jest jak pisanie zamknięcia, które ma nieskończoną długość...

 6
Author: inf3rno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-16 01:21:49