Menu

Generator PHP: yield, yield from, dan Hemat Memori

Generator adalah fungsi yang memakai yield untuk menyerahkan nilai satu per satu, sehingga foreach bisa menelusuri sejuta item tanpa membuat array. Pelajari yield, key, penghematan memori, deret tanpa akhir, yield from, send(), dan getReturn().

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Generator adalah fungsi yang berisi yield. Memanggilnya tidak menjalankan body-nya; pemanggilan itu mengembalikan object Generator yang bisa ditelusuri dengan foreach. Setiap yield $value menyerahkan satu nilai ke loop dan menjeda fungsi sampai loop meminta nilai berikutnya, jadi nilai dihasilkan satu per satu alih-alih dikumpulkan dulu dalam array.

Output-nya bergantian antara generator dan loop: setiap nilai baru dibuat saat loop memintanya.

Generator menghemat memori

range(1, 1000000) membuat array berisi sejuta integer sebelum loop dimulai. Generator yang meng-yield angka yang sama tidak pernah menyimpan lebih dari satu angka. Jalankan kode ini dan bandingkan memori yang dibutuhkan masing-masing versi:

Kedua loop mencetak jumlah yang sama, 500000500000, tetapi versi array melaporkan extra memory 16.0 MB dan generator extra memory 0.0 MB. Selisihnya makin besar seiring bertambahnya data. Ide yang sama inilah yang membuat generator berguna untuk file besar, hasil database, dan halaman API: proses satu item, buang, lalu ambil yang berikutnya.

Yield key dan value

yield $key => $value menentukan key yang diterima oleh foreach ($gen as $key => $value). Tanpa key, generator memberi nomor pada nilainya mulai dari 0, seperti list. Pemakaian yang umum adalah membaca file baris per baris beserta nomor barisnya:

Hanya satu baris yang ada di memori setiap saat, jadi fungsi yang sama bisa menangani file log berukuran 2 GB. fopen() dan fgets() membahas fungsi file yang dipakai di sini.

Deret tanpa akhir dan pipeline lazy

Generator bisa berulang selamanya, karena ia hanya berjalan sejauh yang diminta oleh pemakainya. Hentikan dengan break, atau bungkus dengan generator lain yang mengambil beberapa nilai pertama. Merangkai generator-generator kecil menghasilkan pipeline yang setiap langkahnya lazy:

fibonacci() tidak pernah berhenti dengan sendirinya, tetapi program tetap selesai: take() berhenti meminta setelah jumlah yang diminta tercapai, dan tidak ada nilai lebih dari itu yang pernah dihitung.

yield from: mendelegasikan ke generator lain

yield from meneruskan setiap nilai dari array, iterator, atau generator lain. Dengan cara inilah kamu memecah generator menjadi generator yang lebih kecil, atau menelusuri struktur bersarang secara rekursif:

yield from mempertahankan key dari dalam, jadi kedua array meng-yield key 0 dan 1. iterator_to_array() kemudian menimpa a dan b. Berikan false sebagai argumen kedua setiap kali yang penting hanya nilainya.

send() dan getReturn()

Generator juga bisa menerima nilai. $x = yield; menjeda dan menunggu; $gen->send($value) melanjutkan generator dengan $value sebagai hasil dari yield itu. Generator juga bisa melakukan return dengan nilai akhir, yang kamu baca dengan getReturn() setelah generator selesai.

Sebagian besar kode tidak pernah butuh send(); membaca secara lazy dengan foreach adalah pemakaian sehari-hari. Fitur ini adalah fondasi di balik library coroutine.

Generator hanya bisa ditelusuri sekali

Generator berjalan maju dan tidak bisa diulang dari awal. Setelah sebuah foreach menghabiskannya, loop kedua atas object yang sama akan melempar exception:

Solusinya adalah memanggil lagi fungsi generator-nya untuk setiap putaran, foreach (colors() as $c), atau mengumpulkan nilainya sekali dengan iterator_to_array(colors(), false) saat kamu benar-benar membutuhkannya dua kali. Jika kamu sering melakukannya, berarti array biasa adalah pilihan yang tepat untuk data itu.

Pertanyaan yang sering diajukan

Apa fungsi yield di PHP?

yield $value menyerahkan satu nilai ke kode yang sedang melakukan perulangan atas fungsi itu, dan menjeda fungsi tepat di situ. Iterasi loop berikutnya melanjutkannya setelah yield. Fungsi apa pun yang berisi yield mengembalikan object Generator alih-alih langsung berjalan saat dipanggil.

Kenapa memakai generator alih-alih array?

Array menyimpan semua elemen di memori sekaligus; generator menghasilkan satu elemen setiap kali. Perulangan atas range(1, 1000000) mengalokasikan sejuta integer sekaligus, sedangkan generator yang meng-yield angka yang sama membuat pemakaian memori tetap datar. Generator juga memungkinkan deret tanpa akhir dan berhenti bekerja lebih awal saat loop di-break.

Apa itu yield from di PHP?

yield from $source meneruskan setiap nilai dari generator lain, array, atau Traversable, seolah-olah generator saat ini yang meng-yield nilai itu sendiri. Ekspresi ini juga bernilai sama dengan nilai return generator di dalamnya.

Bisakah saya melakukan perulangan atas generator dua kali?

Tidak. Generator hanya berjalan maju. Setelah sebuah foreach selesai, perulangan lagi melempar Exception: Cannot traverse an already closed generator. Panggil lagi fungsi generator-nya untuk mendapatkan generator baru, atau simpan nilainya dengan iterator_to_array() jika kamu membutuhkannya dua kali.

Bagaimana cara mengubah generator menjadi array?

iterator_to_array($gen) mengumpulkan semua nilai dan mempertahankan key dari generator. Jika key-nya berulang, misalnya setelah yield from, nilai yang belakangan menimpa nilai sebelumnya, jadi berikan false sebagai argumen kedua untuk mendapatkan list biasa: iterator_to_array($gen, false).

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai