Sebuah benang reddit dibesarkan pertanyaan rupanya menarik:
Fungsi rekursif ekor dapat dengan mudah diubah menjadi fungsi berulang. Yang lain, dapat ditransformasikan dengan menggunakan tumpukan eksplisit. Bisakah setiap rekursi diubah menjadi iterasi?
Contoh (penghitung?) Dalam posting adalah pasangan:
(define (num-ways x y)
(case ((= x 0) 1)
((= y 0) 1)
(num-ways2 x y) ))
(define (num-ways2 x y)
(+ (num-ways (- x 1) y)
(num-ways x (- y 1))
choose
x = (x + y)! / (X! Y!), Yang tidak memerlukan rekursi.Jawaban:
Bisakah Anda selalu mengubah fungsi rekursif menjadi fungsi berulang? Ya, tentu saja, dan tesis Gereja-Turing membuktikannya jika ingatanku bermanfaat. Dalam istilah awam, ini menyatakan bahwa apa yang dapat dihitung dengan fungsi rekursif dapat dihitung dengan model iteratif (seperti mesin Turing) dan sebaliknya. Tesis ini tidak memberi tahu Anda dengan tepat bagaimana melakukan konversi, tetapi ia mengatakan bahwa itu pasti mungkin.
Dalam banyak kasus, mengonversi fungsi rekursif itu mudah. Knuth menawarkan beberapa teknik dalam "The Art of Computer Programming". Dan seringkali, sesuatu yang dihitung secara rekursif dapat dihitung dengan pendekatan yang sama sekali berbeda dalam waktu dan ruang yang lebih sedikit. Contoh klasik dari ini adalah angka Fibonacci atau urutannya. Anda pasti telah menemui masalah ini dalam rencana gelar Anda.
Di sisi lain dari koin ini, kita tentu bisa membayangkan sistem pemrograman yang begitu canggih sehingga memperlakukan definisi rumus dari rekursif sebagai undangan untuk memoize hasil sebelumnya, sehingga menawarkan manfaat kecepatan tanpa kerumitan memberi tahu komputer secara tepat langkah mana yang harus dilakukan. ikuti perhitungan formula dengan definisi rekursif. Dijkstra hampir pasti membayangkan sistem seperti itu. Dia menghabiskan waktu lama mencoba memisahkan implementasi dari semantik bahasa pemrograman. Kemudian lagi, bahasa pemrograman non-deterministik dan multi-pemrosesan berada di liga di atas programmer profesional berlatih.
Dalam analisis akhir, banyak fungsi yang lebih mudah dimengerti, dibaca, dan ditulis dalam bentuk rekursif. Kecuali jika ada alasan kuat, Anda mungkin seharusnya tidak (secara manual) mengonversi fungsi-fungsi ini ke algoritma iteratif yang eksplisit. Komputer Anda akan menangani pekerjaan itu dengan benar.
Saya bisa melihat satu alasan kuat. Misalkan Anda memiliki sistem prototipe dalam bahasa tingkat super tinggi seperti Skema [ mengenakan pakaian asbes ], Lisp, Haskell, OCaml, Perl, atau Pascal. Misalkan kondisinya sedemikian rupa sehingga Anda memerlukan implementasi di C atau Java. (Mungkin itu politik.) Maka Anda tentu bisa memiliki beberapa fungsi yang ditulis secara rekursif tetapi yang, diterjemahkan secara harfiah, akan meledak sistem runtime Anda. Sebagai contoh, rekursi ekor tak terbatas dimungkinkan dalam Skema, tetapi idiom yang sama menyebabkan masalah bagi lingkungan C yang ada. Contoh lain adalah penggunaan fungsi bersarang secara leksikal dan ruang lingkup statis, yang didukung Pascal tetapi C tidak.
Dalam keadaan ini, Anda mungkin mencoba mengatasi perlawanan politik terhadap bahasa asli. Anda mungkin menemukan diri Anda menerapkan Lisp dengan buruk, seperti dalam hukum kesepuluh (lidah-di-pipi) Greenspun. Atau Anda mungkin hanya menemukan pendekatan yang sama sekali berbeda untuk solusi. Tapi bagaimanapun juga, pasti ada jalan.
sumber
Iya. Sebuah bukti formal sederhana adalah untuk menunjukkan bahwa rekursi μ dan kalkulus non-rekursif seperti GOTO keduanya Turing lengkap. Karena semua kalkulus lengkap Turing sepenuhnya setara dalam kekuatan ekspresifnya, semua fungsi rekursif dapat diimplementasikan oleh kalkulus lengkap Turing-lengkap non-rekursif.
Sayangnya, saya tidak dapat menemukan definisi GOTO online yang bagus dan formal jadi berikut ini:
Program GOTO adalah urutan perintah P yang dijalankan pada a mesin register sehingga P adalah salah satu dari yang berikut:
HALT
, yang menghentikan eksekusir = r + 1
dimanar
ada registerr = r – 1
dimanar
ada registerGOTO x
dimanax
labelIF r ≠ 0 GOTO x
dimanar
ada register danx
labelNamun, konversi antara fungsi rekursif dan non-rekursif tidak selalu sepele (kecuali dengan penerapan kembali tumpukan panggilan secara manual).
Untuk informasi lebih lanjut lihat jawaban ini .
sumber
Rekursi diimplementasikan sebagai tumpukan atau konstruksi serupa dalam penerjemah atau kompiler yang sebenarnya. Jadi, Anda tentu dapat mengonversi fungsi rekursif ke mitra berulang karena itulah yang selalu dilakukan (jika secara otomatis) . Anda hanya akan menduplikasi pekerjaan kompiler dalam ad-hoc dan mungkin dengan cara yang sangat jelek dan tidak efisien.
sumber
Pada dasarnya ya, pada dasarnya apa yang Anda akhirnya harus lakukan adalah mengganti panggilan metode (yang secara implisit mendorong negara ke tumpukan) ke tumpukan eksplisit mendorong untuk mengingat di mana 'panggilan sebelumnya' telah mencapai, dan kemudian jalankan 'metode yang disebut' sebagai gantinya.
Saya akan membayangkan bahwa kombinasi loop, stack dan state-machine dapat digunakan untuk semua skenario dengan mensimulasikan pemanggilan metode. Apakah ini akan menjadi 'lebih baik' (baik lebih cepat, atau lebih efisien dalam beberapa hal) tidak benar-benar mungkin untuk dikatakan secara umum.
sumber
Alur eksekusi fungsi rekursif dapat direpresentasikan sebagai pohon.
Logika yang sama dapat dilakukan oleh loop, yang menggunakan struktur data untuk melintasi pohon itu.
Traversal mendalam-pertama dapat dilakukan menggunakan tumpukan, luas traversal-pertama dapat dilakukan dengan menggunakan antrian.
Jadi jawabannya adalah ya. Mengapa: https://stackoverflow.com/a/531721/2128327 .
sumber
Ya, menggunakan tumpukan secara eksplisit (tapi rekursi jauh lebih menyenangkan untuk dibaca, IMHO).
sumber
Ya, selalu memungkinkan untuk menulis versi non-rekursif. Solusi sepele adalah dengan menggunakan struktur data stack dan mensimulasikan eksekusi rekursif.
sumber
Pada prinsipnya selalu mungkin untuk menghapus rekursi dan menggantinya dengan iterasi dalam bahasa yang memiliki status tak terbatas baik untuk struktur data maupun untuk tumpukan panggilan. Ini adalah konsekuensi dasar dari tesis Gereja-Turing.
Mengingat bahasa pemrograman yang sebenarnya, jawabannya tidak sejelas itu. Masalahnya adalah sangat mungkin untuk memiliki bahasa di mana jumlah memori yang dapat dialokasikan dalam program terbatas tetapi di mana jumlah panggilan stack yang dapat digunakan tidak terbatas (32-bit C di mana alamat variabel stack tidak dapat di akses). Dalam hal ini, rekursi lebih kuat hanya karena memiliki lebih banyak memori yang dapat digunakan; tidak ada memori yang dapat dialokasikan secara eksplisit untuk meniru tumpukan panggilan. Untuk diskusi terperinci tentang ini, lihat diskusi ini .
sumber
Semua fungsi yang dapat dihitung dapat dihitung dengan Mesin Turing dan karenanya sistem rekursif dan mesin Turing (sistem iteratif) adalah setara.
sumber
Terkadang mengganti rekursi jauh lebih mudah dari itu. Rekursi dulunya adalah hal yang modis yang diajarkan di CS pada 1990-an, dan banyak pengembang pada waktu itu memperkirakan jika Anda memecahkan sesuatu dengan rekursi, itu adalah solusi yang lebih baik. Jadi mereka akan menggunakan rekursi alih-alih mengulang ke belakang untuk membalik urutan, atau hal-hal konyol seperti itu. Jadi, kadang-kadang menghilangkan rekursi adalah jenis latihan "duh, itu sudah jelas".
Ini kurang masalah sekarang, karena fashion telah bergeser ke teknologi lainnya.
sumber
Menghapus rekursi adalah masalah yang kompleks dan layak dalam kondisi yang terdefinisi dengan baik.
Kasus-kasus di bawah ini adalah yang mudah:
sumber
Appart dari tumpukan eksplisit, pola lain untuk mengubah rekursi menjadi iterasi adalah dengan penggunaan trampolin.
Di sini, fungsi mengembalikan hasil akhir, atau penutupan panggilan fungsi yang seharusnya dilakukan. Kemudian, fungsi inisiasi (trampolin) terus memohon penutupan yang dikembalikan sampai hasil akhir tercapai.
Pendekatan ini berfungsi untuk fungsi rekursif yang saling menguntungkan, tetapi saya khawatir ini hanya berfungsi untuk panggilan ekor.
http://en.wikipedia.org/wiki/Trampoline_(computers)
sumber
Saya akan mengatakan ya - panggilan fungsi tidak lain adalah operasi goto dan stack (secara kasar). Yang perlu Anda lakukan adalah meniru tumpukan yang dibangun saat menjalankan fungsi dan melakukan sesuatu yang mirip dengan goto (Anda dapat meniru goto dengan bahasa yang juga tidak memiliki kata kunci ini secara eksplisit).
sumber
Lihat entri berikut di wikipedia, Anda dapat menggunakannya sebagai titik awal untuk menemukan jawaban lengkap untuk pertanyaan Anda.
Mengikuti paragraf yang mungkin memberi Anda beberapa petunjuk tentang di mana untuk memulai:
Lihat juga paragraf terakhir dari entri ini .
sumber
Rincian Lebih Lanjut: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
sumber
tazzego, rekursi berarti bahwa suatu fungsi akan memanggil dirinya sendiri apakah Anda suka atau tidak. Ketika orang berbicara tentang apakah sesuatu dapat dilakukan tanpa rekursi, mereka memaksudkan ini dan Anda tidak dapat mengatakan "tidak, itu tidak benar, karena saya tidak setuju dengan definisi rekursi" sebagai pernyataan yang valid.
Dengan mengingat hal itu, hampir semua yang Anda katakan tidak masuk akal. Satu-satunya hal lain yang Anda katakan bukan omong kosong adalah gagasan bahwa Anda tidak dapat membayangkan pemrograman tanpa callstack. Itu adalah sesuatu yang telah dilakukan selama beberapa dekade sampai menggunakan callstack menjadi populer. FORTRAN versi lama tidak memiliki callstack dan berfungsi dengan baik.
By the way, ada bahasa Turing-lengkap yang hanya menerapkan rekursi (misalnya SML) sebagai sarana perulangan. Ada juga bahasa Turing-complete yang hanya mengimplementasikan iterasi sebagai alat perulangan (misalnya FORTRAN IV). Tesis Church-Turing membuktikan bahwa segala sesuatu yang mungkin dalam bahasa rekursi hanya dapat dilakukan dalam bahasa non-rekursif dan vica-versa oleh fakta bahwa mereka berdua memiliki sifat kelengkapan turing.
sumber
Berikut ini adalah algoritma berulang:
sumber