Generator, içinde yield bulunan bir fonksiyondur. Onu çağırmak gövdeyi çalıştırmaz; foreach döngüsünün üzerinde dolaşabileceği bir Generator nesnesi döndürür. Her yield $value döngüye bir değer verir ve döngü bir sonrakini isteyene kadar fonksiyonu duraklatır, böylece değerler önce bir dizide toplanmak yerine birer birer üretilir.
Çıktı generator ile döngü arasında gidip gelir: her değer yalnızca döngü onu istediğinde üretilir.
Generator'lar bellek tasarrufu sağlar
range(1, 1000000) döngü başlamadan önce bir milyon tam sayıdan oluşan bir dizi oluşturur. Aynı sayıları yield eden bir generator ise hiçbir zaman birden fazlasını tutmaz. Çalıştırın ve her sürümün ihtiyaç duyduğu belleği karşılaştırın:
İki döngü de aynı toplamı, 500000500000 yazdırır, ama dizi sürümü extra memory 16.0 MB, generator ise extra memory 0.0 MB bildirir. Fark veriyle birlikte büyür. Generator'ları büyük dosyalar, veritabanı sonuçları ve API sayfaları için kullanışlı yapan da aynı fikirdir: bir öğeyi işleyin, bırakın, sonrakini alın.
Anahtarları ve değerleri yield etme
yield $key => $value foreach ($gen as $key => $value) ifadesinin alacağı anahtarı belirler. Anahtar olmadan bir generator değerlerini bir liste gibi 0'dan numaralar. Yaygın bir kullanım, bir dosyayı satır numaralarıyla satır satır okumaktır:
Bellekte her seferinde yalnızca bir satır olduğu için aynı fonksiyon 2 GB'lık bir log dosyasını da işler. Burada kullanılan dosya fonksiyonları fopen() ve fgets() sayfasında anlatılıyor.
Sonsuz diziler ve tembel işlem hatları
Bir generator sonsuza kadar dönebilir, çünkü yalnızca tüketicinin istediği kadar çalışır. break ile durdurun ya da onu ilk birkaç değeri alan başka bir generator ile sarın. Küçük generator'ları zincirlemek her adımın tembel olduğu bir işlem hattı verir:
fibonacci() kendi başına hiç bitmez, ama program biter: take() istenen sayıdan sonra istemeyi bırakır ve bunun ötesinde hiçbir şey hesaplanmaz.
yield from: başka bir generator'a devretme
yield from bir dizinin, bir yineleyicinin veya başka bir generator'ın her değerini iletir. Bir generator'ı daha küçüklerine bölmenin ya da iç içe bir yapıyı özyinelemeli dolaşmanın yolu budur:
yield from iç anahtarları korur, bu yüzden iki dizi de 0 ve 1 anahtarlarını yield eder. iterator_to_array() ardından a ve b değerlerinin üzerine yazar. Yalnızca değerler önemli olduğunda ikinci argüman olarak false verin.
send() ve getReturn()
Bir generator değer de alabilir. $x = yield; duraklar ve bekler; $gen->send($value) generator'ı o yield ifadesinin sonucu $value olacak şekilde devam ettirir. Ayrıca bir generator son bir değeri return ile döndürebilir; bitince onu getReturn() ile okursunuz.
Kodların çoğu send() gerektirmez; günlük kullanım foreach ile tembel okumaktır. Eşyordam (coroutine) kütüphanelerinin arkasındaki yapı taşı budur.
Bir generator üzerinde yalnızca bir kez dolaşılabilir
Generator'lar ileri gider ve geri sarılamaz. Bir foreach onu tükettikten sonra aynı nesne üzerindeki ikinci bir döngü hata fırlatır:
Çözüm, her geçiş için generator fonksiyonunu yeniden çağırmaktır, foreach (colors() as $c), ya da değerlere gerçekten iki kez ihtiyaç duyduğunuzda onları bir kez iterator_to_array(colors(), false) ile toplamaktır. Bunu sık yapıyorsanız o veri için doğru seçim düz bir diziydi.
Sıkça Sorulan Sorular
PHP'de yield ne işe yarar?
yield $value fonksiyon üzerinde dolaşan koda bir değer verir ve fonksiyonu tam orada duraklatır. Döngünün bir sonraki turu onu yield sonrasından devam ettirir. İçinde yield bulunan her fonksiyon çağrıldığında çalışmak yerine bir Generator nesnesi döndürür.
Neden dizi yerine generator kullanmalı?
Bir dizi her elemanı aynı anda bellekte tutar; bir generator ise her seferinde bir eleman üretir. range(1, 1000000) üzerinde dolaşmak bir milyon tam sayının hepsine yer ayırır, onları yield eden bir generator ise belleği sabit tutar. Generator'lar ayrıca sonsuz dizilere izin verir ve döngü bittiğinde işi erken durdurur.
PHP'de yield from nedir?
yield from $source başka bir generator'ın, bir dizinin veya bir Traversable nesnesinin her değerini, sanki mevcut generator onları kendisi yield etmiş gibi iletir. Ayrıca iç generator'ın dönüş değerini verir.
Bir generator üzerinde iki kez dolaşabilir miyim?
Hayır. Bir generator yalnızca ileri gider. Bir foreach bittikten sonra tekrar dolaşmak Exception: Cannot traverse an already closed generator fırlatır. Yeni bir generator almak için generator fonksiyonunu tekrar çağırın ya da değerlere iki kez ihtiyacınız varsa onları iterator_to_array() ile saklayın.
Bir generator'ı diziye nasıl çeviririm?
iterator_to_array($gen) generator'ın anahtarlarını koruyarak tüm değerleri toplar. Anahtarlar tekrarlanıyorsa, örneğin yield from sonrasında, sonraki değerler öncekilerin üzerine yazar; düz bir liste almak için ikinci argüman olarak false verin: iterator_to_array($gen, false).