Baru-baru ini saya menulis sebuah fungsi untuk menghasilkan urutan tertentu dengan batasan nontrivial. Masalahnya datang dengan solusi rekursif alami. Sekarang kebetulan, bahkan untuk input yang relatif kecil, urutannya beberapa ribu, jadi saya lebih suka menggunakan algoritme saya sebagai generator daripada menggunakannya untuk mengisi daftar dengan semua urutan.
Berikut ini contohnya. Misalkan kita ingin menghitung semua permutasi string dengan fungsi rekursif. Algoritme naif berikut mengambil 'penyimpanan' argumen ekstra dan menambahkan permutasi padanya setiap kali menemukannya:
def getPermutations(string, storage, prefix=""):
if len(string) == 1:
storage.append(prefix + string) # <-----
else:
for i in range(len(string)):
getPermutations(string[:i]+string[i+1:], storage, prefix+string[i])
storage = []
getPermutations("abcd", storage)
for permutation in storage: print permutation
(Tolong jangan pedulikan inefisiensi, ini hanya sebuah contoh.)
Sekarang saya ingin mengubah fungsi saya menjadi generator, yaitu untuk menghasilkan permutasi daripada menambahkannya ke daftar penyimpanan:
def getPermutations(string, prefix=""):
if len(string) == 1:
yield prefix + string # <-----
else:
for i in range(len(string)):
getPermutations(string[:i]+string[i+1:], prefix+string[i])
for permutation in getPermutations("abcd"):
print permutation
Kode ini tidak berfungsi (fungsi berperilaku seperti generator kosong).
Apakah saya melewatkan sesuatu? Apakah ada cara untuk mengubah algoritma rekursif di atas menjadi generator tanpa menggantinya dengan yang berulang ?
yield from getPermutations(string[:i] + string[i+1:])
, yang lebih efisien dalam banyak hal!yield from
akan mengharuskan Anda menggunakan argumen akumulator (prefix
).string[i],string[:i]+string[i+1:]
pasangan. Maka akan menjadi:for letter,rest in first_letter_options(string): for perm in getPermuations(rest): yield letter+perm
Ini menghindari
len(string)
rekursi -deep, dan secara umum merupakan cara yang bagus untuk menangani generator-inside-generator:flatten
memungkinkan kita untuk melanjutkan kemajuan di generator lain hanyayield
dengan melakukannya, alih-alih mengulanginya danyield
setiap item secara manual.Python 3.3 akan menambahkan
yield from
sintaks, yang memungkinkan pendelegasian alami ke sub-generator:sumber
Panggilan interior ke getPermutations - ini juga generator.
Anda perlu mengulanginya dengan for-loop (lihat posting @MizardX, yang membuat saya keluar beberapa detik!)
sumber