Coroutines sebagai iterator
9.3 Coroutines sebagai iterator
Kita dapat melihat loop iterator sebagai sebuah contoh spesifik bentuk producer-consumer sebuah iterator memproduksi barang untuk dipakai oleh badan loop. Oleh karena itu,ini terlihat aneh menggunakan coroutines untuk menulis iterator. Nyatanya,coroutine memberikan sebuah alat yang kuat untuk pekerjaan ini. Kembali,kunci dari fitur ini adalah kemampuan merubah hubungan antara pemanggil dan terpanggil. Dengan fitur ini,kita dapat menulis iterator tanpa masalah dengan bagaimana menjaga bagian antara pemanggilan successive ke iterator.
Untuk mengilustrasikan macam kegunaannya,tuliskan sebuah iterator untuk merubah seluruh permutasi pada array yang diberikan. Ini bukan pekerjaan mudah untuk menulis secara langsung sebuah iterator,tetapi ini tidak sulit untuk menulis sebuah fungsi rekursif yang menghasilkan seluruh permutasi. Idenya simple,letakkan setiap elemen array di posisi terakhir,tukar dan hasilkan seluruh permutasi dari masing-masing elemen secara rekursif. Kode seperti dibawah ini:
function permgen (a, n) if n == 0 then
printResult(a)
else
for i=1,n do
-- put i-th element as the last one a[n], a[i] = a[i], a[n]
-- generate all permutations of the other elements permgen(a, n - 1)
-- restore i-th element a[n], a[i] = a[i], a[n]
end
end end
Untuk melihat cara kerjanya,kita seharusnya mendefinisikan sebuah fungsi printResult dan memanggil permgen dengan beberapa argumen:
function printResult (a) for i,v in ipairs(a) do
io.write(v, " ")
end io.write("\n")
end
permgen ({1,2,3,4}, 4)
Setelah generator yang kita punya sudah siap. Generator akan mengerjakan perubahan ke sebuah iterator secara otomatis. Pertama,kita merubah printResult ke yield:
function permgen (a, n) if n == 0 then
coroutine.yield(a)
else
Kemudian kita definiskan sebuah induk yang mengatur generator untuk berjalan dalam coroutine,dan kemudian membuat fungsi iterator. Iterator sederhana memanggil coroutine untuk menghasilkan permutasi selanjutnya.
function perm (a) local n = table.getn(a) local co = coroutine.create(function () permgen(a, n)
end) return function () -- iterator
local code, res = coroutine.resume(co) return res
end end
Dengan mesin-mesin ditempat,ini biasa untuk mengiterasi semua permutasi dalam array dengan statemen for:
for p in perm{"a", "b", "c"} do printResult(p) end --> b c a --> c b a --> c a b --> a c b --> b a c --> a b c
Fungsi perm menggunakan bentuk biasa dalam Lua,yang mana membungkus panggilan ke resume dengan coroutine yang saling berhubungan kedalam sebuah fungsi. Bentuk ini,biasanya Lua berikan dalam fungsi special: coroutine.wrap. Seperti create,wrap membuat sebuah coroutine baru. Tidak seperti create,wrap tidak mengembalikan coroutine sendiri: Ia mengembalikan sebuah fungsi,yang ketika dipanggil,melanjutkan coroutine. Tidak seperti resume asli,fungsi ini tidak mengembalikan sebuah kode error sebagai hasil pertama; memunculkan kesalahan jika terjadi kesalahan menggunakan wrap. Kita dapat menulis perm seperti dibawah ini:
function perm (a) local n = table.getn(a) return coroutine.wrap(function () permgen(a, n) end)
end
Biasanya coroutine.wrap lebih sederhana untuk digunakan dibandingkan dengan coroutine.create. Ini memberikan kita kenyataan apa yang kita butuhkan dari sebuah coroutine: Sebuah fungsi untuk memanggil ini. Tetapi,ini juga kurang fleksibel. Kita tidak bisa memeriksa status dari coroutine yang dibuat dengan wrap. Dan lagi,kita tidak dapat memeriksa kesalahan.