function singleDigit(num) {
let counter = 0
let number = [...num + ''].map(Number).reduce((x, y) => {return x * y})
if(number <= 9){
console.log(number)
}else{
console.log(number)
return singleDigit(number), counter += 1
}
}
singleDigit(39)
Kode di atas mengambil integer dan menguranginya menjadi satu digit dengan mengalikannya dengan digitnya sendiri.
Contohnya adalah 39.
3 x 9 = 27.
2 x 7 = 14.
1 x 4 = 4.
Konsol akan masuk:
27
14
4
Bagaimana saya melacak bahwa fungsi rekursif dipanggil 3 kali?
Saya telah mencoba menambahkan penghitung tetapi gagal memperbarui. Akan menghargai bantuan apa pun
javascript
recursion
chs242
sumber
sumber
.map(Number)
redundan karena*
operator memaksa nilai ke angka. ;-)-57
benar-benar a-50
dan-7
.. ketika dilihat dengan cara ini, ia akan melakukan pengurangan-5
x-7
menghasilkan bilangan positif35
. Atau apakah Anda ingin hanya melihat tanda negatif dengan5
dan bukan7
, bahkan yang7
sebenarnya negatif juga. 2) Bagaimana Anda berniat berurusan dengan angka yang termasuk nol? karena ini akan secara otomatis nol pengurangan. Oleh karena itu, semakin besar angka yang Anda berikan, semakin besar kemungkinannya nol. Pilihan lain adalah dengan melewatkan nolJawaban:
Ini adalah varian yang hampir murni akademis, tetapi Anda dapat menggunakan kombinator titik tetap yang dimodifikasi untuk tujuan ini.
Mari kita persingkat dan tingkatkan fungsi asli Anda sedikit:
Dari varian ini, kita dapat memfaktorkan dan mengeringkan panggilan rekursif:
Fungsi ini sekarang dapat digunakan dengan kombinator titik tetap; khusus saya menerapkan kombinator Y yang diadaptasi untuk JavaScript (ketat) sebagai berikut:
dimana kita miliki
Ynormal(singleDigitF, 123234234) == 0
.Sekarang tibalah kiatnya. Karena kami telah memperhitungkan rekursi ke kombinator Y, kami dapat menghitung jumlah rekursi di dalamnya:
Pemeriksaan cepat di Node REPL memberi:
Combinator ini sekarang akan berfungsi untuk menghitung jumlah panggilan dalam fungsi rekursif apa pun yang ditulis dengan gaya
singleDigitF
.(Perhatikan bahwa ada dua sumber untuk mendapatkan nol sebagai jawaban yang sangat sering: pelimpahan numerik (
123345456999999999
menjadi123345457000000000
dll.), Dan fakta bahwa Anda hampir pasti akan mendapatkan nol sebagai nilai perantara di suatu tempat, ketika ukuran input bertambah.)sumber
Anda harus menambahkan argumen balasan ke definisi fungsi Anda:
sumber
counter
akan dihapus dan diatur ke0
, kecuali jika Anda secara eksplisit meneruskannya dalam panggilan rekursif Anda seperti yang dilakukan Sheraff. AesingleDigit(number, ++counter)
++counter
kecounter+1
. Mereka setara secara fungsional, tetapi yang terakhir menentukan niat lebih baik, tidak (tidak perlu) bermutasi dan parameter, dan tidak memiliki kemungkinan secara tidak sengaja pasca-kenaikan. Atau lebih baik lagi, karena ini adalah buntut, gunakan loop sebagai gantinya.Solusi tradisional adalah melewatkan hitungan sebagai parameter ke fungsi seperti yang disarankan oleh jawaban lain.
Namun, ada solusi lain di js. Beberapa jawaban lain yang disarankan hanya menyatakan penghitungan di luar fungsi rekursif:
Ini tentu saja berhasil. Namun ini membuat fungsi ini tidak reentrant (tidak dapat dipanggil dua kali dengan benar). Dalam beberapa kasus, Anda dapat mengabaikan masalah ini dan cukup memastikan Anda tidak menelepon
singleDigit
dua kali (javascript adalah utas tunggal sehingga tidak terlalu sulit untuk dilakukan) tetapi ini adalah bug yang menunggu untuk terjadi jika Anda memperbaruisingleDigit
kemudian menjadi asinkron dan juga terasa jelek.Solusinya adalah mendeklarasikan
counter
variabel di luar tetapi tidak secara global. Ini dimungkinkan karena javascript memiliki penutupan:Ini mirip dengan solusi global tetapi setiap kali Anda menelepon
singleDigit
(yang sekarang bukan fungsi rekursif) itu akan membuat contoh baru daricounter
variabel.sumber
singleDigit
fungsi dan menyediakan cara bersih alternatif untuk melakukan ini tanpa melewati argumen imo. +1recursion
sekarang sepenuhnya terisolasi itu harus benar-benar aman untuk melewati penghitung sebagai parameter terakhir. Saya tidak berpikir membuat fungsi batin itu perlu. Jika Anda tidak menyukai gagasan memiliki parameter untuk satu-satunya manfaat rekursi (saya memang mengambil titik bahwa pengguna dapat mengacaukannya) maka kunci mereka denganFunction#bind
fungsi yang diterapkan sebagian.the traditional solution is to pass the count as a parameter
. Ini adalah solusi alternatif dalam bahasa yang memiliki penutup. Dalam beberapa hal lebih mudah untuk diikuti karena hanya satu variabel daripada jumlah instance variabel yang mungkin tak terbatas. Dengan cara lain mengetahui solusi ini membantu ketika hal yang Anda lacak adalah objek bersama (bayangkan membangun peta unik) atau objek yang sangat besar (seperti string HTML)counter--
akan menjadi cara tradisional untuk memecahkan klaim Anda "tidak dapat dipanggil dua kali dengan benar"Pendekatan lain, karena Anda menghasilkan semua angka, adalah menggunakan generator.
Elemen terakhir adalah jumlah Anda
n
dikurangi menjadi satu digit angka dan untuk menghitung berapa kali Anda mengulanginya, cukup baca panjang array.Pikiran terakhir
Anda mungkin ingin mempertimbangkan untuk mengembalikan kondisi awal dalam fungsi Anda. Setiap angka dengan nol di dalamnya akan mengembalikan nol.
Hal yang sama dapat dikatakan untuk angka apa pun yang dibuat
1
hanya.Akhirnya, Anda tidak mengklarifikasi apakah Anda hanya menerima bilangan bulat positif. Jika Anda menerima bilangan bulat negatif, maka melemparkannya ke string dapat berisiko:
Solusi yang mungkin:
sumber
Ada banyak jawaban menarik di sini. Saya pikir versi saya menawarkan alternatif menarik tambahan.
Anda melakukan beberapa hal dengan fungsi yang diminta. Anda menguranginya menjadi satu digit secara rekursif. Anda mencatat nilai perantara, dan Anda ingin menghitung panggilan rekursif yang dilakukan. Salah satu cara untuk menangani semua ini adalah dengan menulis fungsi murni yang akan mengembalikan struktur data yang berisi hasil akhir, langkah-langkah yang diambil dan panggilan dihitung semuanya menjadi satu:
Anda kemudian dapat mencatat langkah-langkah jika diinginkan, atau menyimpannya untuk diproses lebih lanjut.
Berikut adalah versi yang melakukan itu:
Perhatikan bahwa kami melacak
steps
tetapi menurunkancalls
. Meskipun kami dapat melacak jumlah panggilan dengan parameter tambahan, hal itu sepertinya tidak menghasilkan apa-apa. Kami juga melewatkanmap(Number)
langkah - ini akan dipaksa ke angka dalam hal apa pun oleh penggandaan.Jika Anda khawatir tentang
steps
parameter default yang diekspos sebagai bagian dari API Anda, cukup mudah untuk menyembunyikannya dengan menggunakan fungsi internal seperti ini:Dan dalam kedua kasus itu, mungkin sedikit lebih bersih untuk mengekstrak perkalian digit menjadi fungsi pembantu:
sumber
digitProduct
akan kembaliNaN
(-39 ~> ('-' * '3') * '9'
). Jadi, Anda mungkin ingin menggunakan nilai absolut dari n dan menggunakan-1
atau1
sebagai nilai awal dari pengurangan Anda.{"digit":-39,"steps":[-39],"calls":0}
, karena-39 < 9
. Meskipun saya setuju bahwa ini mungkin dilakukan dengan beberapa pengecekan kesalahan: apakah parameternya angka? - apakah bilangan bulat positif? - dll. Saya pikir saya tidak akan memperbarui untuk memasukkan itu. Ini menangkap algoritme, dan penanganan kesalahan sering khusus untuk basis kode seseorang.Jika Anda hanya mencoba menghitung berapa kali berkurang dan tidak peduli tentang rekursi secara khusus ... Anda bisa menghapus rekursi itu. Kode di bawah ini tetap setia ke Posting Asli karena tidak dianggap
num <= 9
perlu dikurangi. Oleh karena itu,singleDigit(8)
akan memilikicount = 0
, dansingleDigit(39)
akan memilikicount = 3
, seperti OP dan jawaban yang diterima menunjukkan:Tidak perlu memproses angka 9 atau kurang (mis.
num <= 9
). Sayangnya kode OP akan diprosesnum <= 9
walaupun tidak dihitung. Kode di atas tidak akan memproses atau menghitungnum <= 9
sama sekali. Itu hanya melewati itu.Saya memilih untuk tidak menggunakan
.reduce
karena melakukan matematika sebenarnya jauh lebih cepat untuk dieksekusi. Dan, bagi saya, lebih mudah dipahami.Pemikiran lebih lanjut tentang kecepatan
Saya merasa kode yang baik juga cepat. Jika Anda menggunakan jenis pengurangan ini (yang banyak digunakan dalam numerologi), Anda mungkin perlu menggunakannya pada sejumlah besar data. Dalam hal ini, kecepatan akan menjadi yang terpenting.
Menggunakan keduanya
.map(Number)
danconsole.log
(pada setiap langkah reduksi) keduanya sangat panjang untuk dieksekusi dan tidak perlu. Cukup menghapus.map(Number)
dari OP, mempercepatnya sekitar 4,38x. Menghapus begituconsole.log
cepat sehingga hampir tidak mungkin untuk menguji dengan benar (saya tidak ingin menunggu untuk itu).Jadi, mirip dengan jawaban customcommander , tidak menggunakan
.map(Number)
atauconsole.log
mendorong hasil ke dalam array dan menggunakan.length
untukcount
jauh lebih cepat. Sayangnya untuk jawaban customcommander , menggunakan fungsi generator benar-benar lambat (jawaban itu sekitar 2.68x lebih lambat daripada OP tanpa.map(Number)
danconsole.log
)Juga, alih-alih menggunakan
.reduce
saya hanya menggunakan matematika yang sebenarnya. Perubahan tunggal ini saja mempercepat versi fungsi saya dengan faktor 3,59x.Akhirnya, rekursi lebih lambat, membutuhkan ruang stack, menggunakan lebih banyak memori, dan memiliki batas berapa kali dapat "berulang". Atau, dalam hal ini, berapa banyak langkah reduksi yang dapat digunakan untuk menyelesaikan reduksi penuh. Meluncurkan rekursi Anda ke loop berulang membuat semuanya berada di tempat yang sama di tumpukan dan tidak memiliki batasan teoritis tentang berapa banyak langkah reduksi yang dapat digunakan untuk menyelesaikannya. Dengan demikian, fungsi-fungsi ini di sini dapat "mengurangi" hampir semua bilangan bulat ukuran, hanya dibatasi oleh waktu eksekusi dan berapa lama array dapat.
Semua ini dalam pikiran ...
Fungsi di atas berjalan sangat cepat. Ini sekitar 3.13x lebih cepat dari OP (tanpa
.map(Number)
danconsole.log
) dan sekitar 8.4x lebih cepat dari jawaban customcommander . Ingatlah bahwa menghapusconsole.log
OP mencegahnya menghasilkan angka pada setiap langkah pengurangan. Oleh karena itu, kebutuhan di sini untuk mendorong hasil ini ke dalam array.PT
sumber
I feel good code is also fast.
Saya akan mengatakan bahwa kualitas kode telah diukur terhadap standar set persyaratan. Jika kinerja bukan salah satunya maka Anda tidak mendapatkan apa-apa dengan mengganti kode yang dapat dipahami siapa pun dengan kode "cepat". Anda tidak akan percaya jumlah kode yang saya lihat yang telah dire-refactored menjadi performant sampai tidak ada yang bisa memahaminya lagi (untuk beberapa alasan kode optimal cenderung juga tidak berdokumen;). Akhirnya ketahuilah bahwa daftar yang dibuat dengan malas memungkinkan seseorang untuk mengkonsumsi barang sesuai permintaan.[...num+''].map(Number).reduce((x,y)=> {return x*y})
atau bahkan[...String(num)].reduce((x,y)=>x*y)
yang saya lihat di sebagian besar jawaban di sini. Jadi, bagi saya, ini memiliki manfaat tambahan dari pemahaman yang lebih baik tentang apa yang terjadi pada setiap iterasi dan lebih cepat. Ya, kode yang diperkecil (yang memiliki tempatnya) sangat sulit dibaca. Tetapi dalam kasus-kasus itu, seseorang umumnya secara sadar tidak memedulikan keterbacaannya melainkan hasil akhir untuk memotong dan menempel dan melanjutkan.digit = num%10;
num /= 10;
? Harus melakukannum - x
lebih dulu untuk menghapus digit trailing sebelum membaginya kemungkinan akan memaksa kompiler JIT untuk melakukan pembagian terpisah dari yang ia lakukan untuk mendapatkan sisanya.var
(JS tidak memilikiint
). Karenanya,n /= 10;
akan dikonversin
menjadi float jika diperlukan.num = num/10 - x/10
mungkin mengubahnya menjadi pelampung, yang merupakan bentuk panjang dari persamaan. Oleh karena itu, saya harus menggunakan versi refactorednum = (num-x)/10;
untuk menjaganya tetap bilangan bulat. Tidak ada cara yang bisa saya temukan dalam JavaScript yang dapat memberi Anda hasil bagi dan sisa operasi divisi tunggal. Juga,digit = num%10; num /= 10;
dua pernyataan terpisah dan dengan demikian dua operasi divisi terpisah. Sudah lama sejak saya menggunakan C, tapi saya pikir itu benar juga.Mengapa tidak melakukan panggilan ke
console.count
dalam fungsi Anda?Edit: Cuplikan untuk dicoba di browser Anda:
Saya memilikinya bekerja di Chrome 79 dan Firefox 72
sumber
Anda dapat menggunakan penutupan untuk ini.
Cukup simpan saja
counter
ke dalam penutupan fungsi.Berikut ini contohnya:
sumber
singleDigitDecorator()
akan terus menambah penghitung yang sama setiap kali dipanggil.Berikut ini adalah versi Python yang menggunakan fungsi wrapper untuk menyederhanakan counter, seperti yang telah disarankan oleh jawaban slebetman - Saya menulis ini hanya karena ide inti sangat jelas dalam implementasi ini:
sumber