Co to jest yield
?
Do yield
kluczowych danych wraca z funkcji generującej:
Sercem funkcji generatora jest słowo kluczowe wydajności. W najprostszej formie instrukcja return wygląda bardzo podobnie do instrukcji return, z tą różnicą, że zamiast zatrzymywać wykonywanie funkcji i zwracać, wydajność zamiast tego podaje wartość pętli kodu nad generatorem i wstrzymuje wykonywanie funkcji generatora.
Co to jest funkcja generatora?
Funkcja generatora jest bardziej kompaktowym i wydajnym sposobem na napisanie Iteratora . To pozwala zdefiniować funkcję (twój xrange
), które będą obliczenia i zwrotu wartości podczas jesteś zapętlenie nad nim :
foreach (xrange(1, 10) as $key => $value) {
echo "$key => $value", PHP_EOL;
}
Spowoduje to utworzenie następującego wyniku:
0 => 1
1 => 2
…
9 => 10
Można również kontrolować $key
w foreach
za pomocą
yield $someKey => $someValue;
W funkcji generatora $someKey
jest to, czego szukasz $key
i $someValue
która ma być wartością $val
. W przykładzie pytania to jest $i
.
Jaka jest różnica w stosunku do normalnych funkcji?
Teraz możesz się zastanawiać, dlaczego nie używamy po prostu natywnej range
funkcji PHP do osiągnięcia tego wyniku. I masz rację. Wynik byłby taki sam. Różnica polega na tym, jak się tam dostaliśmy.
Gdy używamy range
PHP, będzie go wykonać, należy utworzyć całą tablicę liczb w pamięci i return
że cała tablica do foreach
pętli, która będzie następnie przejść nad nim i wyjście wartości. Innymi słowy, foreach
będzie działać na samej tablicy. range
Funkcja i foreach
tylko „rozmowa” raz. Pomyśl o tym jak o otrzymaniu paczki pocztą. Doręczyciel wręczy ci paczkę i wyjdzie. A następnie rozpakowujesz całą paczkę, wyjmując wszystko, co jest w środku.
Kiedy użyjemy funkcji generatora, PHP wejdzie do funkcji i wykona ją, dopóki nie napotka końca lub yield
słowa kluczowego. Po napotkaniu znaku a yield
, zwróci dowolną wartość w tym czasie do zewnętrznej pętli. Następnie wraca do funkcji generatora i kontynuuje od miejsca, w którym ustąpił. Ponieważ Twoja pętla xrange
zawiera for
pętlę, będzie ona wykonywana i ustępowała, dopóki nie $max
zostanie osiągnięta. Pomyśl o tym jak foreach
o generatorze grającym w ping ponga.
Dlaczego tego potrzebuję?
Oczywiście, generatory mogą być używane do obejścia limitów pamięci. W zależności od środowiska robienie range(1, 1000000)
skryptu zakończy się śmiercią, podczas gdy to samo z generatorem będzie działać dobrze. Lub, jak ujęła to Wikipedia:
Ponieważ generatory obliczają uzyskane wartości tylko na żądanie, są przydatne do reprezentowania sekwencji, które byłyby drogie lub niemożliwe do obliczenia na raz. Należą do nich np. Sekwencje nieskończone i strumienie danych na żywo.
Generatory również powinny być dość szybkie. Pamiętaj jednak, że kiedy mówimy o szybkim czasie, zwykle rozmawiamy bardzo małymi liczbami. Zanim więc wybiegniesz i zmienisz cały kod na generatory, wykonaj test porównawczy, aby zobaczyć, gdzie ma to sens.
Innym przypadkiem użycia generatorów są asynchroniczne coroutines. Słowo yield
kluczowe 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
przed tą wersją spowoduje różne błędy analizy, w zależności od kodu występującego po słowie kluczowym. Jeśli więc wystąpi błąd analizy składni z tego kodu, zaktualizuj PHP.
Źródła i dalsze czytanie:
yeild
, powiedzmy, takie rozwiązanie: ideone.com/xgqevMreturn range(1,100000000)
ifor ($i=0; $i<100000000; $i++) yield $i
Ta funkcja używa wydajności:
jest prawie taki sam jak ten bez:
Jedyną różnicą jest to, że
a()
zwraca generator ib()
tylko prostą tablicę. Możesz iterować w obu przypadkach.Ponadto, pierwszy nie przydziela pełnej tablicy, a zatem wymaga mniej pamięci.
źródło
prosty przykład
wynik
zaawansowany przykład
wynik
źródło
yield
słowo kluczowe służy do definicji „generatorów” w PHP 5.5. Ok, to co to jest generator ?Z php.net:
Z tego miejsca: generatory = generatory, inne funkcje (tylko proste funkcje) = funkcje.
Są więc przydatne, gdy:
musisz robić proste rzeczy (lub proste);
generator jest naprawdę znacznie prostszy niż implementacja interfejsu Iterator. z drugiej strony generatory są mniej funkcjonalne. porównaj je .
musisz wygenerować DUŻĄ ilość danych - oszczędzając pamięć;
aby zaoszczędzić pamięć, możemy wygenerować potrzebne dane za pomocą funkcji dla każdej iteracji pętli, a po iteracji wykorzystać śmieci. więc tutaj główne punkty to - czysty kod i prawdopodobnie wydajność. zobacz, co jest lepsze dla twoich potrzeb.
musisz wygenerować sekwencję, która zależy od wartości pośrednich;
jest to rozszerzenie poprzedniej myśli. generatory mogą ułatwić pracę w porównaniu z funkcjami. sprawdź przykład Fibonacciego i spróbuj utworzyć sekwencję bez generatora. Także generatory mogą pracować szybciej, przynajmniej z powodu przechowywania wartości pośrednich w zmiennych lokalnych;
musisz poprawić wydajność.
w niektórych przypadkach mogą pracować szybciej niż działają (patrz poprzednia korzyść);
źródło
Za pomocą
yield
można łatwo opisać punkty przerwania między wieloma zadaniami w jednej funkcji. To wszystko, nie ma w tym nic specjalnego.Jeśli zadania 1 i zadanie 2 są ze sobą ściśle powiązane, ale potrzebujesz między nimi punktu przerwania, aby zrobić coś innego:
wtedy generatory są najlepszym rozwiązaniem, ponieważ nie musisz dzielić kodu na wiele zamknięć lub mieszać go z innym kodem, ani używać wywołań zwrotnych itp. Po prostu używasz
yield
dodajesz punkt przerwania i możesz kontynuować od tego punkt przerwania, jeśli jesteś gotowy.Dodaj punkt przerwania bez generatorów:
Dodaj punkt przerwania za pomocą generatorów
Uwaga: Łatwo jest pomylić się z generatorami, więc zawsze pisz testy jednostkowe przed ich wdrożeniem! Uwaga 2: Używanie generatorów w nieskończonej pętli jest jak pisanie zamknięcia, które ma nieskończoną długość ...
źródło
Żadna z powyższych odpowiedzi nie pokazuje konkretnego przykładu z użyciem ogromnych tablic zapełnionych członkami nienumerycznymi. Oto przykład użycia tablicy wygenerowanej przez
explode()
na dużym pliku .txt (w moim przypadku 262 MB):Wynik był:
Teraz porównaj to do podobnego skryptu, używając
yield
słowa kluczowego:Dane wyjściowe dla tego skryptu były następujące:
Wyraźnie oszczędności w zużyciu pamięci były znaczne (ΔMemoryUsage -----> ~ 270,5 MB w pierwszym przykładzie, ~ 450B w drugim przykładzie).
źródło
Ciekawym aspektem, który warto tutaj omówić, jest ustępowanie . Za każdym razem, gdy musimy zmienić parametr tak, aby był odbijany poza funkcją, musimy przekazać ten parametr przez odniesienie. Aby zastosować to do generatorów, po prostu wstawiamy znak ampersand
&
do nazwy generatora i zmiennej używanej w iteracji:Powyższy przykład pokazuje, jak zmiana iterowanych wartości w
foreach
pętli zmienia$from
zmienną w generatorze. Jest tak, ponieważ$from
jest uzyskiwany przez odniesienie ze względu na znak ampersand przed nazwą generatora. Z tego powodu$value
zmienna wforeach
pętli jest odniesieniem do$from
zmiennej w funkcji generatora.źródło
Poniższy kod ilustruje, w jaki sposób użycie generatora zwraca wynik przed zakończeniem, w przeciwieństwie do tradycyjnego podejścia bez generatora, które zwraca pełną tablicę po pełnej iteracji. Poniższy generator zwraca wartości, gdy są gotowe, nie trzeba czekać na całkowite wypełnienie tablicy:
źródło