Saya punya beberapa contoh kode Python yang perlu saya tiru di C ++. Saya tidak memerlukan solusi khusus apa pun (seperti solusi hasil berbasis rutin bersama, meskipun itu akan menjadi jawaban yang dapat diterima juga), saya hanya perlu mereproduksi semantik dengan beberapa cara.
Python
Ini adalah generator urutan dasar, yang jelas terlalu besar untuk menyimpan versi material.
def pair_sequence():
for i in range(2**32):
for j in range(2**32):
yield (i, j)
Tujuannya adalah untuk mempertahankan dua contoh dari urutan di atas, dan mengulanginya dalam semi-lockstep, tetapi dalam potongan. Pada contoh di bawah ini first_pass
menggunakan urutan pasangan untuk menginisialisasi buffer, dan second_pass
meregenerasi urutan yang sama persis dan memproses buffer lagi.
def run():
seq1 = pair_sequence()
seq2 = pair_sequence()
buffer = [0] * 1000
first_pass(seq1, buffer)
second_pass(seq2, buffer)
... repeat ...
C ++
Satu-satunya hal yang dapat saya temukan untuk solusi di C ++ adalah meniru yield
dengan C ++ coroutines, tetapi saya belum menemukan referensi yang bagus tentang cara melakukan ini. Saya juga tertarik dengan solusi alternatif (non umum) untuk masalah ini. Saya tidak memiliki cukup anggaran memori untuk menyimpan salinan urutan di antara gerakan printhead.
Jawaban:
Generator ada di C ++, tepat di bawah nama lain: Input Iterators . Misalnya, membaca dari
std::cin
mirip dengan memiliki generatorchar
.Anda hanya perlu memahami apa yang dilakukan generator:
Dalam contoh sepele Anda, itu cukup mudah. Secara konseptual:
Tentu saja, kami membungkus ini sebagai kelas yang tepat:
So hum yeah ... mungkin C ++ sedikit lebih bertele-tele :)
sumber
Di C ++ ada iterator, tetapi mengimplementasikan iterator tidaklah mudah: kita harus berkonsultasi dengan konsep iterator dan dengan hati-hati merancang kelas iterator baru untuk mengimplementasikannya. Untungnya, Boost memiliki template iterator_facade yang akan membantu mengimplementasikan iterator dan generator yang kompatibel dengan iterator.
Terkadang coroutine tanpa tumpukan dapat digunakan untuk mengimplementasikan iterator .
PS Lihat juga artikel ini yang menyebutkan
switch
peretasan oleh Christopher M. Kohlhoff dan Boost.Coroutine oleh Oliver Kowalke. Karya Oliver Kowalke adalah tindak lanjut dari Boost.Coroutine oleh Giovanni P. Deretta.PS Saya pikir Anda juga dapat menulis semacam generator dengan lambda :
Atau dengan functor:
PS Berikut adalah generator yang diimplementasikan dengan coroutines Mordor :
sumber
Sejak Boost.Coroutine2 sekarang mendukungnya dengan sangat baik (saya menemukannya karena saya ingin menyelesaikan masalah yang persis sama
yield
), saya memposting kode C ++ yang sesuai dengan niat asli Anda:Dalam contoh ini,
pair_sequence
tidak mengambil argumen tambahan. Jika perlu,std::bind
atau lambda harus digunakan untuk menghasilkan objek fungsi yang hanya membutuhkan satu argumenpush_type
, ketika diteruskan kecoro_t::pull_type
konstruktor.sumber
Semua jawaban yang melibatkan penulisan iterator Anda sendiri sepenuhnya salah. Jawaban seperti itu sepenuhnya kehilangan inti dari generator Python (salah satu fitur bahasa terbesar dan unik). Hal yang paling penting tentang generator adalah eksekusi melanjutkan dari bagian yang ditinggalkannya. Ini tidak terjadi pada iterator. Sebagai gantinya, Anda harus menyimpan informasi status secara manual sedemikian rupa sehingga ketika operator ++ atau operator * dipanggil lagi, informasi yang benar sudah ada di awal. pemanggilan fungsi berikutnya. Inilah sebabnya mengapa menulis iterator C ++ Anda sendiri sangat merepotkan; padahal, generator itu elegan, dan mudah dibaca + ditulis.
Saya tidak berpikir ada analog yang baik untuk generator Python di C ++ asli, setidaknya belum (ada rumor bahwa hasil akan mendarat di C ++ 17 ). Anda bisa mendapatkan sesuatu yang serupa dengan menggunakan pihak ketiga (misalnya saran Peningkatan Yongwei), atau menggulirkan milik Anda sendiri.
Saya akan mengatakan hal terdekat dalam bahasa asli C ++ adalah utas. Sebuah utas dapat mempertahankan sekumpulan variabel lokal yang ditangguhkan, dan dapat melanjutkan eksekusi di tempat yang ditinggalkannya, sangat mirip dengan generator, tetapi Anda perlu menggulung sedikit infrastruktur tambahan untuk mendukung komunikasi antara objek generator dan pemanggilnya. Misalnya
Solusi ini memiliki beberapa kelemahan:
sumber
Anda mungkin harus memeriksa generator di std :: eksperimental di Visual Studio 2015 misalnya: https://blogs.msdn.microsoft.com/vcblog/2014/11/12/resumable-functions-in-c/
Saya pikir itulah yang Anda cari. Generator keseluruhan harus tersedia dalam C ++ 17 karena ini hanya fitur Microsoft VC eksperimental.
sumber
Jika Anda hanya perlu melakukan ini untuk sejumlah kecil generator tertentu, Anda dapat mengimplementasikan masing-masing sebagai kelas, di mana data anggotanya setara dengan variabel lokal dari fungsi generator Python. Kemudian Anda memiliki fungsi berikutnya yang mengembalikan hal berikutnya yang akan dihasilkan generator, memperbarui status internal saat melakukannya.
Ini pada dasarnya mirip dengan bagaimana generator Python diimplementasikan, saya percaya. Perbedaan utamanya adalah mereka dapat mengingat offset ke dalam bytecode untuk fungsi generator sebagai bagian dari "status internal", yang berarti generator dapat ditulis sebagai loop yang berisi hasil. Anda harus menghitung nilai berikutnya dari sebelumnya. Dalam kasus Anda
pair_sequence
, itu sangat sepele. Ini mungkin bukan untuk generator yang kompleks.Anda juga membutuhkan cara untuk menunjukkan penghentian. Jika apa yang Anda kembalikan adalah "seperti penunjuk", dan NULL seharusnya bukan nilai hasil yang valid, Anda dapat menggunakan penunjuk NULL sebagai indikator penghentian. Jika tidak, Anda memerlukan sinyal out-of-band.
sumber
Sesuatu seperti ini sangat mirip:
Menggunakan operator () hanyalah pertanyaan tentang apa yang ingin Anda lakukan dengan generator ini, Anda juga dapat membuatnya sebagai aliran dan memastikannya menyesuaikan dengan istream_iterator, misalnya.
sumber
Menggunakan range-v3 :
sumber
Sesuatu seperti ini :
Contoh penggunaan:
Akan mencetak angka dari 0 hingga 99
sumber
Nah, hari ini saya juga sedang mencari implementasi pengumpulan yang mudah di bawah C ++ 11. Sebenarnya saya kecewa, karena semua yang saya temukan terlalu jauh dari hal-hal seperti generator python, atau operator C # yield ... atau terlalu rumit.
Tujuannya adalah untuk membuat koleksi yang akan mengeluarkan itemnya hanya jika diperlukan.
Saya ingin menjadi seperti ini:
Saya menemukan posting ini, jawaban terbaik IMHO adalah tentang boost.coroutine2, oleh Yongwei Wu . Karena itu paling dekat dengan apa yang diinginkan penulis.
Layak untuk mempelajari kursus dorongan .. Dan saya mungkin akan melakukannya pada akhir pekan. Tapi sejauh ini saya menggunakan implementasi saya yang sangat kecil. Semoga bisa membantu orang lain.
Di bawah ini adalah contoh penggunaan, dan kemudian implementasi.
Example.cpp
Generator.h
sumber
Jawaban ini berfungsi di C (dan karenanya saya pikir berfungsi di c ++ juga)
Ini adalah cara sederhana dan tidak berorientasi objek untuk meniru generator. Ini bekerja seperti yang diharapkan untuk saya.
sumber
Sama seperti fungsi yang mensimulasikan konsep tumpukan, generator mensimulasikan konsep antrian. Sisanya adalah semantik.
Sebagai catatan tambahan, Anda selalu dapat mensimulasikan antrian dengan tumpukan menggunakan tumpukan operasi alih-alih data. Apa artinya secara praktis adalah bahwa Anda dapat mengimplementasikan perilaku seperti antrian dengan mengembalikan pasangan, nilai keduanya memiliki fungsi selanjutnya untuk dipanggil atau menunjukkan bahwa kita kehabisan nilai. Tapi ini lebih umum daripada apa hasil vs pengembalian. Ini memungkinkan untuk mensimulasikan antrian nilai apa pun daripada nilai homogen yang Anda harapkan dari generator, tetapi tanpa menjaga antrian internal penuh.
Lebih khusus lagi, karena C ++ tidak memiliki abstraksi alami untuk antrian, Anda perlu menggunakan konstruksi yang mengimplementasikan antrian secara internal. Jadi jawaban yang memberi contoh dengan iterator adalah implementasi konsep yang layak.
Artinya secara praktis adalah bahwa Anda dapat mengimplementasikan sesuatu dengan fungsionalitas antrian tanpa tulang jika Anda hanya menginginkan sesuatu yang cepat dan kemudian menggunakan nilai antrian sama seperti Anda akan menggunakan nilai yang dihasilkan dari generator.
sumber