PHP, C #, Python dan kemungkinan beberapa bahasa lain memiliki yield
kata kunci yang digunakan untuk membuat fungsi generator.
Dalam PHP: http://php.net/manual/en/language.generators.syntax.php
Dalam Python: https://www.pythoncentral.io/python-generators-and-yield-keyword/
Dalam C #: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield
Saya khawatir bahwa sebagai fitur / fasilitas bahasa, ada yield
beberapa konvensi. Salah satunya adalah apa yang saya sebut "kepastian". Ini adalah metode yang mengembalikan hasil yang berbeda setiap kali Anda menyebutnya. Dengan fungsi non-generator biasa Anda dapat memanggilnya dan jika diberi input yang sama, ia akan mengembalikan output yang sama. Dengan hasil, ia mengembalikan output yang berbeda, berdasarkan kondisi internal. Jadi jika Anda secara acak memanggil fungsi menghasilkan, tidak mengetahui keadaan sebelumnya, Anda tidak dapat mengharapkannya untuk mengembalikan hasil tertentu.
Bagaimana fungsi seperti ini cocok dengan paradigma bahasa? Apakah itu benar-benar melanggar konvensi? Apakah ide bagus untuk memiliki dan menggunakan fitur ini? (untuk memberikan contoh tentang apa yang baik dan apa yang buruk, goto
dulunya fitur banyak bahasa dan masih, tetapi dianggap berbahaya dan karena itu diberantas dari beberapa bahasa, seperti Jawa). Apakah kompiler / juru bahasa bahasa pemrograman harus keluar dari konvensi apa pun untuk mengimplementasikan fitur seperti itu, misalnya, apakah bahasa harus mengimplementasikan multi-threading agar fitur ini berfungsi, atau dapatkah itu dilakukan tanpa teknologi threading?
yield
pada dasarnya adalah mesin negara. Itu tidak dimaksudkan untuk mengembalikan hasil yang sama setiap kali. Apa yang akan dilakukannya dengan kepastian absolut adalah mengembalikan item berikutnya dalam jumlah setiap kali dipanggil. Thread tidak diperlukan; Anda perlu penutupan (lebih atau kurang), untuk mempertahankan kondisi saat ini.yield
kata kunci seperti halnya Python. Ini memiliki metode statisstd::this_thread::yield()
, tetapi itu bukan kata kunci. Jadi ituthis_thread
akan menambahkan hampir semua panggilan ke sana, membuatnya cukup jelas itu adalah fitur perpustakaan hanya untuk menghasilkan utas, bukan fitur bahasa tentang menghasilkan aliran kontrol secara umum.Jawaban:
Peringatan pertama - C # adalah bahasa yang saya tahu paling baik, dan meskipun memiliki
yield
yang tampaknya sangat mirip dengan bahasa lainyield
, mungkin ada perbedaan halus yang saya tidak sadari.Omong kosong. Apakah Anda benar-benar mengharapkan
Random.Next
atauConsole.ReadLine
mengembalikan hasil yang sama setiap kali Anda memanggil mereka? Bagaimana dengan Panggilan Istirahat? Autentikasi? Dapatkan Barang dari koleksi? Ada segala macam fungsi (baik, berguna) yang tidak murni.Ya,
yield
bermain sangat buruk dengantry/catch/finally
, dan tidak diizinkan ( https://blogs.msdn.microsoft.com/ericlippert/2009/07/16/iterator-blocks-part-three-why-no-yield-in-finally/ for Info lebih lanjut).Ini tentu ide yang baik untuk memiliki fitur ini. Hal - hal seperti LINQ C # benar - benar baik - mengevaluasi koleksi dengan malas memberikan manfaat kinerja yang besar, dan
yield
memungkinkan hal semacam itu dilakukan dalam sebagian kecil dari kode dengan sebagian kecil bug yang akan dilakukan oleh iterator linting tangan.Yang mengatakan, tidak ada satu ton kegunaan untuk di
yield
luar pemrosesan koleksi gaya LINQ. Saya telah menggunakannya untuk pemrosesan validasi, pembuatan jadwal, pengacakan, dan beberapa hal lainnya, tetapi saya berharap sebagian besar pengembang tidak pernah menggunakannya (atau menyalahgunakannya).Tidak persis. Compiler menghasilkan iterator mesin negara yang melacak di mana ia berhenti sehingga dapat mulai lagi di sana saat berikutnya disebut. Proses untuk pembuatan kode melakukan sesuatu yang mirip dengan Continuation Passing Style, di mana kode setelah
yield
ditarik ke dalam bloknya sendiri (dan jika adayield
s, sub-blok lain, dan sebagainya). Itu adalah pendekatan yang dikenal lebih sering digunakan dalam Pemrograman Fungsional dan juga muncul dalam kompilasi async / menunggu C #.Tidak diperlukan threading, tetapi memang membutuhkan pendekatan yang berbeda untuk pembuatan kode di sebagian besar kompiler, dan memang memiliki beberapa konflik dengan fitur bahasa lainnya.
Semua dalam semua,
yield
adalah fitur dampak yang relatif rendah yang benar-benar membantu dengan subset masalah tertentu.sumber
yield
kata kunci ini mirip dengan coroutine, ya, atau sesuatu yang berbeda? Jika demikian saya berharap saya punya satu di C! Saya dapat memikirkan setidaknya beberapa bagian kode yang layak yang akan jauh lebih mudah untuk ditulis dengan fitur bahasa seperti itu.async
/await
ditambahkan ke bahasa, seseorang menerapkannya menggunakanyield
.Saya ingin menjawab ini dari perspektif Python dengan ya tegas , itu ide bagus .
Saya akan mulai dengan membahas beberapa pertanyaan dan asumsi dalam pertanyaan Anda terlebih dahulu, kemudian menunjukkan kegunaan generator dan kegunaannya yang tidak masuk akal di Python nanti.
Ini salah. Metode pada objek dapat dianggap sebagai fungsi itu sendiri, dengan keadaan internal mereka sendiri. Dalam Python, karena semuanya adalah objek, Anda sebenarnya bisa mendapatkan metode dari objek, dan meneruskan metode itu (yang terikat pada objek asalnya, jadi ia mengingat kondisinya).
Contoh lain termasuk fungsi acak sengaja serta metode input seperti jaringan, sistem file, dan terminal.
Jika paradigma bahasa mendukung hal-hal seperti fungsi kelas satu, dan generator mendukung fitur bahasa lain seperti protokol Iterable, maka mereka cocok dengan mulus.
Tidak. Karena dimasukkan ke dalam bahasa, konvensi dibangun dan mencakup (atau mengharuskan!) Penggunaan generator.
Seperti halnya fitur lain, kompiler hanya perlu dirancang untuk mendukung fitur tersebut. Dalam kasus Python, fungsi sudah objek dengan negara (seperti argumen default dan penjelasan fungsi).
Fakta menyenangkan: Implementasi Python default tidak mendukung threading sama sekali. Ini fitur Global Interpreter Lock (GIL), jadi tidak ada yang benar-benar berjalan bersamaan kecuali Anda sudah memutar proses kedua untuk menjalankan instance Python yang berbeda.
catatan: contoh dalam Python 3
Di luar Yield
Meskipun
yield
kata kunci dapat digunakan dalam fungsi apa pun untuk mengubahnya menjadi generator, itu bukan satu-satunya cara untuk membuatnya. Python menampilkan Generator Expressions, cara yang ampuh untuk mengekspresikan generator dengan jelas dalam hal iterable lain (termasuk generator lain)Seperti yang Anda lihat, tidak hanya sintaksnya yang bersih dan mudah dibaca, tetapi fungsi-fungsi
sum
bawaannya seperti menerima generator.Dengan
Lihat Proposal Peningkatan Python untuk pernyataan With . Ini sangat berbeda dari yang Anda harapkan dari pernyataan With dalam bahasa lain. Dengan sedikit bantuan dari perpustakaan standar, generator Python bekerja dengan indah sebagai manajer konteks untuk mereka.
Tentu saja, mencetak sesuatu adalah hal paling membosankan yang dapat Anda lakukan di sini, tetapi hal itu menunjukkan hasil yang terlihat. Opsi yang lebih menarik termasuk pengelolaan sumber daya secara otomatis (membuka dan menutup file / stream / koneksi jaringan), mengunci konkurensi, membungkus sementara atau mengganti suatu fungsi, dan mendekompresi kemudian mengkompres ulang data. Jika fungsi panggilan seperti menyuntikkan kode ke dalam kode Anda, maka dengan pernyataan seperti membungkus bagian dari kode Anda dengan kode lain. Bagaimanapun Anda menggunakannya, ini adalah contoh kuat dari pengait yang mudah ke dalam struktur bahasa. Generator berbasis hasil bukan satu-satunya cara untuk membuat manajer konteks, tetapi mereka pasti yang nyaman.
Untuk dan Kelelahan Sebagian
Untuk loop di Python bekerja dengan cara yang menarik. Mereka memiliki format berikut:
Pertama, ekspresi yang saya panggil
<iterable>
dievaluasi untuk mendapatkan objek yang dapat diubah. Kedua, iterable telah__iter__
memanggilnya, dan iterator yang dihasilkan disimpan di belakang layar. Selanjutnya,__next__
dipanggil pada iterator untuk mendapatkan nilai untuk mengikat nama yang Anda masukkan<name>
. Langkah ini berulang sampai panggilan untuk__next__
melempar aStopIteration
. Pengecualian ditelan oleh for loop, dan eksekusi berlanjut dari sana.Kembali ke generator: ketika Anda memanggil
__iter__
generator, itu hanya mengembalikan sendiri.Artinya, Anda dapat memisahkan iterasi atas sesuatu dari hal yang ingin Anda lakukan dengannya, dan mengubah perilaku itu di tengah jalan. Di bawah ini, perhatikan bagaimana generator yang sama digunakan dalam dua loop, dan pada yang kedua generator mulai mengeksekusi dari yang ditinggalkannya dari yang pertama.
Evaluasi Malas
Salah satu kelemahan generator dibandingkan dengan daftar adalah satu-satunya hal yang dapat Anda akses dalam generator adalah hal berikutnya yang keluar darinya. Anda tidak dapat kembali dan untuk hasil sebelumnya, atau melompat ke depan untuk yang berikutnya tanpa melalui hasil antara. Sisi atas dari ini adalah generator dapat mengambil hampir tidak ada memori dibandingkan dengan daftar yang setara.
Generator juga dapat dirantai dengan malas.
Baris pertama, kedua, dan ketiga hanya mendefinisikan generator masing-masing, tetapi tidak melakukan pekerjaan nyata. Ketika baris terakhir dipanggil, jumlah meminta numericcolumn untuk suatu nilai, numericcolumn membutuhkan nilai dari lastcolumn, lastcolumn meminta nilai dari logfile, yang kemudian benar-benar membaca baris dari file. Tumpukan ini mengurai hingga jumlah mendapat bilangan bulat pertama. Kemudian, proses terjadi lagi untuk baris kedua. Pada titik ini, jumlah memiliki dua bilangan bulat, dan menambahkannya bersama. Perhatikan bahwa baris ketiga belum dibaca dari file. Sum kemudian melanjutkan meminta nilai dari numericcolumn (benar-benar tidak menyadari sisa rantai) dan menambahkannya, sampai numericcolumn habis.
Bagian yang sangat menarik di sini adalah bahwa garis-garisnya dibaca, dikonsumsi, dan dibuang secara individual. Pada titik tidak ada seluruh file dalam memori sekaligus. Apa yang terjadi jika file log ini, katakanlah, satu terabyte? Ini hanya berfungsi, karena hanya membaca satu baris pada satu waktu.
Kesimpulan
Ini bukan ulasan lengkap dari semua penggunaan generator di Python. Khususnya, saya melewatkan generator yang tidak terbatas, mesin negara, melewati nilai kembali, dan hubungan mereka dengan coroutine.
Saya percaya ini cukup untuk menunjukkan bahwa Anda dapat memiliki generator sebagai fitur bahasa yang terintegrasi dan bersih.
sumber
Jika Anda terbiasa dengan bahasa OOP klasik, generator dan
yield
mungkin tampak menggelegar karena keadaan yang dapat diubah ditangkap pada tingkat fungsi daripada tingkat objek.Pertanyaan tentang "kepastian" adalah herring merah. Biasanya disebut transparansi referensial , dan pada dasarnya berarti fungsi selalu mengembalikan hasil yang sama untuk argumen yang sama. Segera setelah Anda memiliki status yang bisa berubah, Anda kehilangan transparansi referensial. Dalam OOP, objek sering memiliki keadaan bisa berubah, yang berarti hasil pemanggilan metode tidak hanya bergantung pada argumen, tetapi juga keadaan internal objek.
Pertanyaannya adalah di mana menangkap keadaan yang bisa berubah. Dalam OOP klasik, keadaan bisa berubah ada di tingkat objek. Tetapi jika suatu bahasa mendukung penutupan, Anda mungkin memiliki status yang bisa berubah pada tingkat fungsi. Misalnya dalam JavaScript:
Singkatnya,
yield
adalah alami dalam bahasa yang mendukung penutupan, tetapi akan keluar dari tempatnya dalam bahasa seperti versi Jawa yang lebih lama di mana keadaan yang bisa berubah hanya ada di tingkat objek.sumber
Menurut pendapat saya, ini bukan fitur yang baik. Ini adalah fitur yang buruk, terutama karena itu perlu diajarkan dengan sangat hati-hati, dan semua orang mengajarkannya dengan salah. Orang-orang menggunakan kata "generator," menyamakan antara fungsi generator dan objek generator. Pertanyaannya adalah: hanya siapa atau apa yang menghasilkan yang sebenarnya?
Ini bukan semata pendapat saya. Bahkan Guido, dalam buletin PEP di mana dia mengatur hal ini, mengakui bahwa fungsi generator bukanlah generator tetapi "pabrik generator."
Itu agak penting, bukan begitu? Tetapi membaca 99% dokumentasi di luar sana, Anda akan mendapatkan kesan bahwa fungsi generator adalah generator yang sebenarnya, dan mereka cenderung mengabaikan fakta bahwa Anda juga membutuhkan objek generator.
Guido mempertimbangkan untuk mengganti "def" untuk "gen" untuk fungsi-fungsi ini dan berkata Tidak. Tapi saya berpendapat itu tidak akan cukup. Itu harus benar-benar:
sumber