PHP'de "Generator function" kullanımı ve hafızayı verimli kullanmada oynadığı rol



3 dakikalık okuma
September 13, 2019

PHP’de GENERATOR FUNCTION diye adlandırlan bir fonksiyon türü. PHP 5.5 sürümü ile camiyaya katılıyor. Normal bir fonksiyona çok benziyor; ama bu fonksiyonda “return” yerine “yield” kullanıyoruz. Böylece yield kullandığımız fonksiyonlar otomatik olarak “Generator Function” diye adlandırılıyor.

“Return” den farklı olarak, yield işlemi gerçekleştirdikten sonra yordamı sonlandırıp fonksiyondan çıkartmıyor bizi; tam tersi işlemi devam ettiriyor. Bu yüzden döngü içerisinde kullanımı aslında işlevini en iyi gerçekleştirdiği yer. Çünkü döngü içinde biz “return” kullanmış olsak ilk işlemden sonra yordamı sonlandırır ve değer döndürür. İşte burada “yield” kullandığımız takdirde işlem sonlanmıyor ve döngü bitene kadar işlem devam ediyor. Her bir döngü içerisindeki sonucu yield içerisine “array” tipindeymiş gibi koyuyor; ama “yield” bir array de değil. Tam manada “Allocated memory” kullanmıyor. Her bir işlemden sonra oluşturduğu iterator nesnesinin son pozisyonundan işleme devam ediyor. Daha da somutlaştırırsak örneğimizi, hani veritabanlarinda bulunan “Cursor” mantığı ile aynı şekilde çalışıyor. Hafızaya almaktansa cursor pozisyonunu next() şeklinde ilerletiyor. Bu yüzden bir hafıza limiti ile de karşılaşmayacağız.

Eğer büyük datasetler ile calışacaksanız “yield” kullanmalısınız. Mesela bir text dosyasını okuduğunuzu düşünelim. Bu iş için yield kullanılabilir.

Ayrıca tekrar belirtmeliyim ki “yield” bir fonksiyon içerisinde kullanılmak zorundadır. Bu yüzden yazının başında da dediğim gibi “yield” in kullanıldığı fonksiyonun adına da “Generator Function” diyoruz.

Örneklerle açıklamaya devam edelim:

function numbers() {
     for($i=0; $i<=10; $i++) {
        yield $i; 
    } 
}

Yukarıdaki örnekte görülmek üzere “yield” kısmında “$i” değişkeni iterator nesnesi şeklinde yield içerisinde oluyor ve işlem devam ediyor. En son işlemden sonra yordam sonlanıyor. Buradan şu sonucu da çıkartabiliriz: “yield” tek başına return den farklı olarak hiç bir değişken ya da dizi kullanmadan birden fazla değer tutabiliyor içinde.

“yield” içerisindeki veriyi okuyabilmek için dizilerde yaptığımız gibi şöyle yapmalıyız:

$numbers =  numbers();

foreach($numbers as $number) {
echo "$number \n";
}

Yield’in faydaları neler?

  1. Hafızadan belirli bir yer ayırma ihtiyacı duymadığı için hafıza limiti problemi yok. Çok geniş verilerle çalışabiliriz.

  2. Generator functionlar normal functionlardan daha hızlı çalışır.

  3. Kod karmaşıklığını daha da azaltıyor. Kodun okunabilirliğini artırıyor.

  4. Bu özellik PHP 5.5 sürümünde ekleniyor; Generator fonksyionlar normalde return değeri göndermezler; ama 7.0 ile gönderebilmeleri sağlanıyor ve “getReturn()” ile bu değeri alabiliyoruz. Şöyle:

    <?php

    $gen = (function() {
        yield 1;
        yield 2;

        return 3; // bu özellik 7.0 ile geldi
    })();

    foreach ($gen as $val) {
        echo $val, PHP_EOL;
    }

    echo $gen->getReturn(), PHP_EOL;

Çıktı:
    1
    2
    3

Şimdi hafıza kullanımı ile ilgili bir örnek yazalım. Böylece array ile arasında nasıl bir fark olduğunu görün:

<?php


function sample_array($value)
{
    $i = 0;
    $data = [];


    while ($i < $value) {
        $i++;
        $data[] = round(memory_get_usage(true) / 1024 / 1024, 2);
    }

    return $data;

}


function sample_generator($value)
{
    $i = 0;

    while ($i < $value) {
        $i++;
        yield round(memory_get_usage(true) / 1024 / 1024, 2);

    }


}


foreach (sample_array(150000) as $value) {
    $result = $value;
}
echo "Memory Consumption (function with array): $result MB \n";

echo "-----------------\n";

foreach (sample_generator(150000) as $value) {
    $result = $value;
}
echo "Memory Consumption (generator function with yield):  $result MB \n";
Çıktı:

Memory Consumption (function with array): 10 MB 
-----------------
Memory Consumption (generator function with yield):  2 MB 

Etiketler: