Saya belajar pemrograman fungsional dan saya mengalami kesulitan memahami bagaimana beberapa skenario tertentu diimplementasikan tanpa menggunakan penugasan. Masalah sederhana berikut ini cukup meringkas kebingungan saya.
Tulis sebuah program yang menerima peristiwa tentang perubahan dalam struktur data yang diberikan dan memancarkan peristiwa ketika struktur data ini mencapai keadaan tertentu.
Jadi saya punya salinan datastructure yang saya pelihara
datastructure_copy::DataStructure
Saya memiliki aliran peristiwa yang dipecat saat perubahan:
datastructure_changes::Stream Change
Saya memiliki fungsi yang menerapkan perubahan pada struktur data dan mengembalikan salinan baru itu:
apply_change::Change -> DataStructure -> DataStructure
Dan saya memiliki predikat yang memeriksa apakah status data telah mencapai kondisi yang diinginkan.
is_ready::DataStructure ->Boolean
Dengan kata lain saya membutuhkan sesuatu seperti 'mengurangi' yang berfungsi pada aliran.
Saya tahu bahwa salah satu cara untuk mengimplementasikan ini adalah menghitung ulang keadaan setiap kali ada perubahan, namun ini tampaknya tidak praktis. Saya bermain sedikit dengan State Monad, tetapi bagi saya sepertinya itu dimaksudkan untuk menyelesaikan masalah yang berbeda.
Jadi adakah cara lain untuk melakukan itu?
Perhatikan bahwa pertanyaan saya murni konseptual dan saya tidak terlalu akrab dengan Haskell.
sumber
Jawaban:
Jika perubahan yang diterapkan ketika suatu peristiwa terjadi tidak distributif, dengan satu atau lain cara, Anda harus menghitung ulang keadaan setiap kali suatu peristiwa terjadi, karena keadaan akhir tidak lain adalah keadaan awal, ditambah perubahan berturut-turut. Dan bahkan jika perubahannya bersifat distributif, Anda biasanya ingin mengubah keadaan secara berurutan menjadi yang berikutnya, karena Anda ingin menghentikan proses Anda secepat keadaan yang dicapai, dan karena Anda harus menghitung keadaan selanjutnya untuk menentukan apakah yang baru adalah negara yang dicari.
Dalam pemrograman fungsional, perubahan status biasanya diwakili oleh panggilan fungsi dan / atau parameter fungsi.
Karena Anda tidak dapat memprediksi kapan kondisi akhir akan dihitung, Anda tidak boleh menggunakan fungsi rekursif non-ekor. Aliran negara, di mana setiap negara didasarkan pada yang sebelumnya, bisa menjadi alternatif yang baik.
Jadi dalam kasus Anda, saya akan menjawab pertanyaan dengan kode berikut, di Scala:
Yang bisa memberi, misalnya (saya menyederhanakan output):
val states: Stream[Double] = ...
adalah garis di mana negara berturut-turut dihitung.Elemen pertama dari aliran ini adalah keadaan awal sistem.
zip
menggabungkan aliran negara dengan aliran peristiwa ke dalam aliran tunggal pasangan elemen, masing-masing pasangan menjadi (negara, peristiwa).map
mengubah setiap pasangan menjadi nilai tunggal menjadi keadaan baru, dihitung sebagai fungsi dari keadaan lama dan peristiwa terkait. Karenanya, keadaan baru adalah keadaan yang dihitung sebelumnya, ditambah peristiwa terkait yang "mengubah" keadaan.Jadi pada dasarnya, Anda mendefinisikan aliran negara yang berpotensi tidak terbatas, setiap negara bagian baru merupakan fungsi dari negara bagian yang terakhir dihitung, dan acara baru. Karena aliran malas di Scala (antara lain), hanya ada yang dihitung berdasarkan permintaan, sehingga Anda tidak harus menghitung status tidak berguna, dan Anda dapat menghitung sebanyak negara yang Anda inginkan.
Jika Anda hanya tertarik pada status pertama yang menghormati predikat, ganti baris kode terakhir dengan:
Yang mengambil:
sumber
val states: Stream[Double]...
Anda mengatakan Anda memiliki 2 fungsi:
dan jika saya mengerti Anda saat
is_ready
itu agak mahal sehingga Anda tidak ingin melakukan itu untuk setiap acara perubahan berulang kali.Apa yang Anda butuhkan adalah fungsi mengambil DataStructure awal dan mengembunnya menjadi keadaan sederhana dan fungsi yang mengambil keadaan terkondensasi, Perubahan dan output keadaan terkondensasi baru.
Katakanlah DataStructure adalah triplet
x,y,z
dan Anda menunggu x, y dan z menjadi bilangan prima. Keadaan terkondensasi Anda kemudian bisa menjadi set yang x, y, z tidak prima Perubahan yang membuat x prime menghapus x dari set. Perubahan yang membuat x tidak prima menambahkan x ke set (jika tidak ada). Struktur Data siap maka set kosong.Idenya adalah bahwa memperbarui keadaan kental jauh lebih murah daripada memperbarui DataStructure dan komputasi sudah dari awal.
Catatan: Pendekatan yang lebih baik lagi adalah melacak x, y, z mana yang diperiksa sebagai prima dan jika mereka di mana. Untuk setiap Perubahan Anda menandai bidang yang relevan sebagai tidak dicentang. Kemudian ketika is_ready dipanggil Anda memeriksa dan mengingat. Ini lebih baik jika Anda tidak memeriksa is_ready setelah setiap Perubahan karena x mungkin berubah beberapa kali dan Anda hanya memeriksa perdana sekali.
sumber