ÜreteçlerÜreteçlere giriş
Üreteçler, Iterator arayüzünü gerçekleyen bir sınıfı
gerçeklemenin karmaşıklığı ve giderleri olmaksızın basit
yineleyicileri
gerçeklemek için kolay bir yol sağlar.
Bir üreteç, önemli miktarda işlem süresi gerektirmeden ve bellek
sınırını aşabilecek bir dizi oluşturmaya gerek kalmadan bir dizi veriyi
yinelemek için &foreach; kullanan bir kod yazmanızı sağlar. Bunun için
bir üreteç işlevini normal bir işlev yazar gibi yazabilirsiniz,
siz bir kez değer döndürmeyi,
beklerken bir üreteç üzerinde yinelenecek değerleri sağlamak için gerektiği
kadar çok kez değer döndürebilir.
Buna basit bir örnek range işlevini bir üreteç olarak
tekrar gerçeklemektir. Standard range işlevi her
değeri ve onun dönen değerini içeren devasa boyutlara ulaşabilen bir dizi
üretmek zorundadır. Örneğin, range(0, 1000000) çağrısı
100MB üzerinde bellek kullanımıyla sonuçlanabilir.
Bir seçenek olarak, bir xrange() üreteci gerçekleyebiliriz,
tek ihtiyacımız bir Iterator nesnesi oluşturmaya yetecek
bellek ayırmak ve 1 kilobayttan azıyla dönmesi için üretecin dahili durumunu
izlemektir.
- range işlevini bir üreteç olarak gerçeklemek
= 0) {
throw new LogicException('Step must be negative');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
/*
* range() ve xrange() işlevlerinin ikisi de aşağıda
* benzer çıktıyı verecektir.
*/
echo 'range() içindeki bir haneli tek sayılar: ';
foreach (range(1, 9, 2) as $number) {
echo "$number ";
}
echo "\n";
echo 'xrange() içindeki bir haneli tek sayılar: ';
foreach (xrange(1, 9, 2) as $number) {
echo "$number ";
}
?>
]]>
&example.outputs;
Generator nesneleri
Bir üreteç işlevi çağrıldığında dahili Generator
sınıfının yeni bir nesnesi döner. Bu nesne Iterator
arayüzünü gerçekler. Bu, sadece ileri doğru yineleme yapan bir yineleme
nesnesinin yaptığını yapacak ve değer gönderme ve döndürme dahil, üretecin
durumunu da değiştirmeye yarayacak çağırılabilir yöntemler sağlayacaktır.
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;).
&yield; içeren her işlev bir üreteç işlevidir.
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 geri döner ve adeta bir dizi
değerlerini tüketmiş gibi kod çağrılmaya devam eder.
Bir üreteç değer döndürebilir ve
Generator::getReturn kullanılarak alınabilir.
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.
- Basit bir yield örneği
]]>
&example.outputs;
Dahili olarak, ilişkisel olmayan dizilerdeki gibi sıralı tamsayı
anahtarlar üretilen (yield) değerlerle çiftler oluşturur.
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.
- Bir anahtar/değer çifti üretimi
$fields;
}
}
foreach (input_parser($input) as $id => $fields) {
echo "$id:\n";
echo " $fields[0]\n";
echo " $fields[1]\n";
}
?>
]]>
&example.outputs;
null değerlerin üretini
Yield deyimini bağımsız değişkensiz kullanmak &null; değerinin otomatik bir
anahtarla birlikte üretilmesini sağlar.
- &null; üretmek
]]>
&example.outputs;
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:
- Değerleri başvuruya göre üretmek
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).'... ';
}
?>
]]>
&example.outputs;
yield from üzerinden üreteç ihalesi
Üreteç ihalesi, değerlerin başka bir üreteçte,
Traversable nesnesinden veya
yield from anahtar sözcüğü kullanılarak bir diziden ü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 edecektir.
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.
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 bağımsız değişkene
sahiptir: Generator tarafından döndürülen
anahtarları yoksayarken tüm değerleri toplamak için &false; atanabilen
preserve_keys bağımsız değişkeni.
- iterator_to_array ile
yield from
]]>
&example.outputs;
int(1)
[1]=>
int(4)
[2]=>
int(3)
}
]]>
- yield from için temel kullanım
]]>
&example.outputs;
- yield from ve dönen değerler
getReturn();
?>
]]>
&example.outputs;
Üreteçlerin Iterator nesneleriyle karşılaştırılması
Üreteçlerin başlıca getirisi basitlikleridir.
Iterator sınıfının gerçeklenmesine kıyasla çok
daha az kod yazılır ve kod genelde çok daha okunabilirdir.
Örneğin aşağıdaki işlev ve sınıf eşdeğerdir:
fileHandle = fopen($fileName, 'r')) {
throw new RuntimeException('Couldn\'t open file "' . $fileName . '"');
}
}
public function rewind() {
fseek($this->fileHandle, 0);
$this->line = fgets($this->fileHandle);
$this->i = 0;
}
public function valid() {
return false !== $this->line;
}
public function current() {
return $this->line;
}
public function key() {
return $this->i;
}
public function next() {
if (false !== $this->line) {
$this->line = fgets($this->fileHandle);
$this->i++;
}
}
public function __destruct() {
fclose($this->fileHandle);
}
}
?>
]]>
Bu esnekliğin bir bedeli vardır, üreteçler sadece ileri sayan yineleyiciler
olduklarından yineleme bir kere başladı mı bir daha başa sarılamazlar.
Bunun bir diğer anlamı, aynı üretecin tekrar tekrar kullanılamamasıdır;
üreteç işlevi tekrar çağrılarak üretecin yeniden oluşturulması gerekir.
&reftitle.seealso;
Nesne Yineleme