Saya telah membaca beberapa artikel baru-baru ini (mis. Http://dailyjs.com/2012/09/14/functional-programming/ ) tentang aspek fungsional Javascript dan hubungan antara Skema dan Javascript (yang terakhir dipengaruhi oleh yang pertama, yang adalah bahasa fungsional, sedangkan aspek OO diwarisi dari Self yang merupakan bahasa berbasis prototyping).
Namun pertanyaan saya lebih spesifik: Saya bertanya-tanya apakah ada metrik tentang kinerja rekursi vs iterasi dalam Javascript.
Saya tahu bahwa dalam beberapa bahasa (di mana dengan iterasi desain berkinerja lebih baik) perbedaannya minimal karena interpreter / kompiler mengubah rekursi menjadi iterasi, namun saya kira mungkin ini bukan kasus Javascript karena, setidaknya sebagian, fungsional bahasa.
Jawaban:
JavaScript tidak melakukan optimisasi rekursi ekor , jadi jika rekursi Anda terlalu dalam, Anda mungkin mendapatkan stack stack overflow. Iterasi tidak memiliki masalah seperti itu. Jika Anda berpikir Anda akan berulang terlalu banyak, dan Anda benar-benar membutuhkan rekursi (misalnya, untuk mengisi banjir), ganti rekursi dengan tumpukan Anda sendiri.
Kinerja rekursi mungkin lebih buruk daripada kinerja iterasi, karena pemanggilan fungsi dan pengembalian membutuhkan pelestarian dan pemulihan keadaan, sementara iterasi hanya melompat ke titik lain dalam suatu fungsi.
sumber
var stack = []; var factorial = function(n) { if(n === 0) { return 1 } else { stack[n-1] = n * factorial(n - 1); return stack[n-1]; } }
else
fungsi itu denganelse if (stack[n-1]) { return stack[n-1]; } else
dan Anda memiliki memoisasi . Siapa pun yang menulis kode faktorial memiliki implementasi yang tidak lengkap (dan mungkin seharusnya digunakan distack[n]
mana-mana, bukanstack[n-1]
).Pembaruan : sejak ES2015, JavaScript memiliki TCO , jadi bagian dari argumen di bawah ini tidak berlaku lagi.
Meskipun Javascript tidak memiliki optimasi panggilan ekor, rekursi seringkali merupakan cara terbaik untuk melakukannya. Dan dengan tulus, kecuali dalam kasus tepi, Anda tidak akan mendapatkan panggilan stack overflow.
Kinerja adalah sesuatu yang perlu diingat, tetapi optimasi prematur juga. Jika Anda berpikir rekursi lebih elegan daripada iterasi, maka lakukanlah. Jika ternyata ini adalah hambatan Anda (yang mungkin tidak akan pernah terjadi), maka Anda dapat menggantinya dengan beberapa iterasi yang jelek. Tetapi sebagian besar waktu, bottleneck terletak pada manipulasi DOM atau lebih umum I / O, bukan kode itu sendiri.
Rekursi selalu lebih elegan 1 .
1 : Pendapat pribadi.
sumber
Saya sangat ingin tahu tentang kinerja ini dalam javascript juga, jadi saya melakukan beberapa percobaan (walaupun pada versi yang lebih tua dari node). Saya menulis kalkulator faktorial secara rekursif vs iterasi dan menjalankannya beberapa kali secara lokal. Hasilnya tampak sangat condong ke arah rekursi memiliki pajak (diharapkan).
Kode: https://github.com/j03m/trickyQuestions/blob/master/factorial.js
sumber
"use strict";
dan melihat apakah ada bedanya. (Ini akan menghasilkanjump
s alih-alih urutan panggilan standar)Sesuai permintaan OP, saya akan ikut serta (tanpa membodohi diri sendiri, semoga: P)
Saya pikir kita semua sepakat bahwa rekursi hanyalah cara pengkodean yang lebih elegan. Jika dilakukan dengan baik dapat membuat kode lebih mudah dikelola, yang merupakan IMHO sama pentingnya (jika tidak lebih) yang mengurangi 0,0001ms.
Sejauh argumen bahwa JS tidak melakukan optimasi Tail-call: itu tidak sepenuhnya benar lagi, menggunakan mode ketat ECMA5 memungkinkan TCO. Itu adalah sesuatu yang saya tidak terlalu senang tentang beberapa waktu yang lalu, tapi setidaknya sekarang saya tahu mengapa
arguments.callee
melempar kesalahan dalam mode ketat. Saya tahu tautan di atas tautan ke laporan bug, tetapi bug tersebut diatur ke WONTFIX. Selain itu, TCO standar akan datang: ECMA6 (Desember 2013).Secara naluriah, dan berpegang pada sifat fungsional JS, saya akan mengatakan bahwa rekursi adalah gaya pengkodean yang lebih efisien 99,99% dari waktu. Namun, Florian Margaine ada benarnya ketika dia mengatakan bahwa kemacetan kemungkinan akan ditemukan di tempat lain. Jika Anda memanipulasi DOM, Anda mungkin lebih baik memfokuskan pada penulisan kode Anda sebisa mungkin dipertahankan. API DOM adalah apa adanya: lambat.
Saya pikir hampir tidak mungkin untuk menawarkan jawaban yang pasti sebagai pilihan yang lebih cepat. Akhir-akhir ini, banyak jspref yang saya lihat menunjukkan bahwa mesin V8 Chrome sangat cepat di beberapa tugas, yang berjalan 4x lebih lambat pada SpiderMonkey FF dan sebaliknya. Mesin JS modern memiliki segala macam trik untuk mengoptimalkan kode Anda. Saya bukan ahli, tapi saya merasa bahwa V8, misalnya, sangat dioptimalkan untuk penutupan (dan rekursi), sedangkan mesin MS's JScript tidak. SpiderMonkey sering berkinerja lebih baik ketika DOM terlibat ...
Singkatnya: Saya akan mengatakan teknik mana yang akan lebih performant, seperti biasa di JS, hampir mustahil untuk diprediksi.
sumber
Tanpa mode ketat, kinerja iterasi biasanya sedikit lebih cepat daripada rekursi ( selain membuat JIT bekerja lebih banyak ). Optimasi rekursi ekor pada dasarnya menghilangkan perbedaan nyata karena mengubah seluruh urutan panggilan menjadi lompatan.
Contoh: Jsperf
Saya akan menyarankan lebih mengkhawatirkan tentang kejelasan dan kesederhanaan kode ketika datang untuk memilih antara rekursi dan iterasi.
sumber