Generator sözdizimi

Bir üreteç işlevi, normal bir işlev gibi görünür fakat bir üreteç tek bir değer döndürmek yerine ihtiyaç duyulduğu kadar çok değer üretir (yield).

Bir üreteç işlevi çağrıldığında üzerinde yineleme yapılabilecek bir nesne döndürür. Bu nesne üzerinde yineleme yaptığınızda (örneğin foreach ile), PHP bir değere her ihtiyaç duyuşunda nesnenin yineleme yöntemlerini çağırır ve üreteç bir değer ürettiğinde üretecin durumunu kaydeder, böylece yeni bir değere her ihtiyaç duyuluşunda üreteç kaldığı yerden devam edebilir.

Üretilecek değer kalmadığında, üreteç basitçe çıkar ve adeta bir dizi değerlerini tüketmiş gibi kod çağrılmaya devam eder.

Bilginize:

PHP 5'te, bir üreteç bir değer döndüremezdi: bu bir derleme hatasıyla sonuçlanırdı. Üreteç içinde geçerli sözdizimi boş bir return deyimi idi ve üreteci sonlandırırdı. PHP 7.0 itibariyle, üreteç değerleri döndürebilmekte ve Generator::getReturn() kullanarak bunlar alınabilmektedir.

yield sözcüğü

Üreteç işlevinin kalbi yield sözcüğüdür. En basit halinde, bir yield deyimi çoğunlukla bir return deyimi gibi görünür. İşlevin çalışmasını durdurmak ve değer döndürmek yerine, yield, üreteç üzerindeki kod döngüsüne bir değer verir ve üreteç işlevini bekletir.

Örnek 1 - Basit bir yield örneği

<?php
function gen_one_to_three() {
    for (
$i 1$i <= 3$i++) {
        
// yield'ler arasında $i korunur.
        
yield $i;
    }
}

$generator gen_one_to_three();
foreach (
$generator as $value) {
    echo 
"$value\n";
}
?>

Yukarıdaki örneğin çıktısı:

1
2
3

Bilginize:

Dahili olarak, ilişkisel olmayan dizilerdeki gibi sıralı tamsayı anahtarlar üretilen (yield) değerlerle çiftler oluşturur.

Dikkat

yield sözcüğünü bir ifade bağlamında kullanıyorsanız, (örneğin bir atamanın sağ tarfında) PHP5'te yield deyimini parantez içine almanız gerekir. Örneğin aşağıdaki geçerlidir:

$data = (yield $value);

Bu yapılmazsa PHP 5'te bir çözümleme hatası ortaya çıkar:

$data = yield $value;

Parantez kısıtlamaları PHP7'de uygulanmaz.

Generator::send() yöntemine $data değiştirgesine atanan değer ya da yerine Generator::next() çağılırsa NULL atanır.

Değerleri anahtarlarla üretmek

PHP ilişkisel dizileri de destekler ve üreteçlerin bir farkı yoktur. Basit değerlerin üretilmesinin yanında, yukarıda gösterildiği gibi, aynı anda bir anahtar da üretebilirsiniz.

Bir anahtar/değer çifti üretmenin (yielding) sözdizimi, aşağıda gösterildiği gibi, bir ilişkisel dizi tanımlamada kullanılan sözdizimine çok benzer.

Örnek 2 - Bir anahtar/değer çifti üretimi

<?php
/*
 * girdi noktalı virgülle ayrılmış alanlardır,
 * ilk alan anahtar olarak kullanılacak ID'dir
 */

$input = <<<'EOF'
1;PHP;Dolar imlerini sever
2;Python;Boşlukları sever
3;Ruby;Blokları sever
EOF;

function 
input_parser($input) {
    foreach (
explode("\n"$input) as $line) {
        
$fields explode(';'$line);
        
$id array_shift($fields);

        
yield $id => $fields;
    }
}

foreach (
input_parser($input) as $id => $fields) {
    echo 
"$id:\n";
    echo 
"    $fields[0]\n";
    echo 
"    $fields[1]\n";
}
?>

Yukarıdaki örneğin çıktısı:

1:
    PHP
    Dolar imlerini sever
2:
    Python
    Boşlukları sever
3:
    Ruby
    Blokları sever
Dikkat

Evvelce gösterilen basit değerli üretimler gibi, bir anahtar/değer çiftini bir ifade bağlamı içinde üretmek, yield deyimin parantez içine alınmasını gerektirir (PHP 5 için):

$data = (yield $key => $value);

null değerlerin üretini

Yield deyimini değiştirgesiz kullanmak NULL değerinin özdevinimli bir anahtarla birlikte üretilmesini sağlar.

Örnek 3 - NULL üretmek

<?php
function gen_three_nulls() {
    foreach (
range(13) as $i) {
        
yield;
    }
}

var_dump(iterator_to_array(gen_three_nulls()));
?>

Yukarıdaki örneğin çıktısı:

array(3) {
  [0]=>
  NULL
  [1]=>
  NULL
  [2]=>
  NULL
}

Başvuruya göre üretim

Üreteç işlevleri üretimi, değerlerle yapabildiği gibi başvurularla da yapabilir. Bu, işlev isminin önüne bir '&' imi yerleştirip işlevlerden başvurları döndürerek yapılabilir:

Örnek 4 - Değerleri başvuruya göre üretmek

<?php
function &gen_reference() {
    
$value 3;

    while (
$value 0) {
        
yield $value;
    }
}

/*
 * $number döngü içinde değiştirilebilir,
 * üreteç başvuruları ürettiğinden, 
 * gen_reference() içindeki $value değişir.
 */
foreach (gen_reference() as &$number) {
    echo (--
$number).'... ';
}
?>

Yukarıdaki örneğin çıktısı:

2... 1... 0... 

yield from üzerinden üreteç ihalesi

PHP 7'de, üreteç ihalesi, değerlerin başka bir üreteçte (yield from deyimini kullanarak array veya Traversable nesnesinden) üretilmesini sağlar. Dış üreteç tüm değerleri iç üreteç, nesne veya diziden geçerliliğini yitirene kadar ürettikten sonra üretime dış üreteçte devam edilecektir.

Bir üreteç yield from ile kullanılırsa, yield from ifadesi ayrıca, iç üreteçten döndürülen değerleri de döndürecektir.

Dikkat

Bir dizide saklama (örn, iterator_to_array() ile)

yield from anahtarları sıfırlamaz, Traversable nesnesinden veya diziden döndürülen anahtarları korur. Böylece, bazı değerler başka bir yield veya yield from ile ortaklaşılan bir anahtarla paylaşılır (bir diziye değer girilmesiyle, anahtar ilk değerlerin üzerine yazılmasına sebep olur).

Bu konuda alışılmış durum, iterator_to_array() işlevinin öntanımlı olarak bir anahtarlı dizi döndürmesidir (muhtemelen beklenmedik sonuçlara yol açarak). iterator_to_array() ikinci bir değiştirgeye sahiptir: Generator tarafından döndürülen anahtarları yoksayarken tüm değerleri toplamak için FALSE atanabilen use_keys değiştirgesi.

Örnek 5 - iterator_to_array() ile yield from

<?php
function from() {
    
yield 1// key 0
    
yield 2// key 1
    
yield 3// key 2
}
function 
gen() {
    
yield 0// key 0
    
yield from from(); // keys 0-2
    
yield 4// key 1
}
// [0, 1, 2, 3, 4] dizisini almak için ikinci değiştirgeye false aktaralım
var_dump(iterator_to_array(gen()));
?>

Yukarıdaki örneğin çıktısı:

array(3) {
  [0]=>
  int(1)
  [1]=>
  int(4)
  [2]=>
  int(3)
}

Örnek 6 - yield from için temel kullanım

<?php
function count_to_ten() {
    
yield 1;
    
yield 2;
    
yield from [34];
    
yield from new ArrayIterator([56]);
    
yield from seven_eight();
    
yield 9;
    
yield 10;
}

function 
seven_eight() {
    
yield 7;
    
yield from eight();
}

function 
eight() {
    
yield 8;
}

foreach (
count_to_ten() as $num) {
    echo 
"$num ";
}
?>

Yukarıdaki örneğin çıktısı:

1 2 3 4 5 6 7 8 9 10 

Örnek 7 - yield from ve dönen değerler

<?php
function count_to_ten() {
    
yield 1;
    
yield 2;
    
yield from [34];
    
yield from new ArrayIterator([56]);
    
yield from seven_eight();
    return 
yield from nine_ten();
}

function 
seven_eight() {
    
yield 7;
    
yield from eight();
}

function 
eight() {
    
yield 8;
}

function 
nine_ten() {
    
yield 9;
    return 
10;
}

$gen count_to_ten();
foreach (
$gen as $num) {
    echo 
"$num ";
}
echo 
$gen->getReturn();
?>

Yukarıdaki örneğin çıktısı:

1 2 3 4 5 6 7 8 9 10