Cara bermain golf dengan rekursi
Rekursi, meskipun bukan opsi tercepat, seringkali merupakan yang terpendek. Secara umum, rekursi adalah yang terpendek jika solusi dapat disederhanakan menjadi solusi untuk bagian tantangan yang lebih kecil, terutama jika inputnya berupa angka atau string. Misalnya, jika f("abcd")
dapat dihitung dari "a"
dan f("bcd")
, biasanya yang terbaik adalah menggunakan rekursi.
Ambil, misalnya, faktorial:
n=>[...Array(n).keys()].reduce((x,y)=>x*++y,1)
n=>[...Array(n)].reduce((x,_,i)=>x*++i,1)
n=>[...Array(n)].reduce(x=>x*n--,1)
n=>{for(t=1;n;)t*=n--;return t}
n=>eval("for(t=1;n;)t*=n--")
f=n=>n?n*f(n-1):1
Dalam contoh ini, rekursi jelas jauh lebih pendek daripada opsi lainnya.
Bagaimana dengan jumlah karakter:
s=>[...s].map(x=>t+=x.charCodeAt(),t=0)|t
s=>[...s].reduce((t,x)=>t+x.charCodeAt())
s=>[for(x of(t=0,s))t+=x.charCodeAt()]|t // Firefox 30+ only
f=s=>s?s.charCodeAt()+f(s.slice(1)):0
Yang ini lebih sulit, tetapi kita dapat melihat bahwa ketika diimplementasikan dengan benar, rekursi menghemat 4 byte .map
.
Sekarang mari kita lihat berbagai jenis rekursi:
Pra rekursi
Ini biasanya jenis rekursi terpendek. Input dibagi menjadi dua bagian a
dan b
, dan fungsi menghitung sesuatu dengan a
dan f(b)
. Kembali ke contoh faktorial kami:
f=n=>n?n*f(n-1):1
Dalam hal ini, a
adalah n , b
adalah n-1 , dan nilai yang dikembalikan adalah a*f(b)
.
Catatan penting: Semua fungsi rekursif harus memiliki cara untuk berhenti berulang ketika inputnya cukup kecil. Dalam fungsi faktorial, ini dikontrol dengan n? :1
, yaitu jika inputnya 0 , kembalikan 1 tanpa menelepon f
lagi.
Pasca rekursi
Pasca rekursi mirip dengan pra rekursi, tetapi sedikit berbeda. Input dibagi menjadi dua bagian a
dan b
, dan fungsi menghitung sesuatu dengan a
, lalu memanggil f(b,a)
. Argumen kedua biasanya memiliki nilai default (yaitu f(a,b=1)
).
Pra-rekursi baik ketika Anda perlu melakukan sesuatu yang istimewa dengan hasil akhir. Misalnya, jika Anda ingin faktorial dari angka plus 1:
f=(n,p=1)=>n?f(n-1,n*p):p+1
Namun demikian, post-tidak selalu lebih pendek dari menggunakan pra-rekursi dalam fungsi lain:
n=>(f=n=>n?n*f(n-1):1)(n)+1
Jadi kapan lebih pendek? Anda mungkin memperhatikan bahwa pasca rekursi dalam contoh ini membutuhkan tanda kurung di sekitar argumen fungsi, sedangkan pra-rekursi tidak. Secara umum, jika kedua solusi membutuhkan tanda kurung di sekitar argumen, pasca-rekursi sekitar 2 byte lebih pendek:
n=>!(g=([x,...a])=>a[0]?x-a.pop()+g(a):0)(n)
f=([x,...a],n=0)=>a[0]?f(a,x-a.pop()+n):!n
(program di sini diambil dari jawaban ini )
Cara menemukan solusi terpendek
Biasanya satu-satunya cara untuk menemukan metode terpendek adalah dengan mencoba semuanya. Ini termasuk:
- Loop
.map
(untuk string, baik [...s].map
atau s.replace
; untuk angka, Anda dapat membuat rentang )
- Array pemahaman
- Pra-rekursi (kadang-kadang di dalam yang lain dari opsi ini)
- Pasca rekursi
Dan ini hanyalah solusi yang paling umum; solusi terbaik mungkin merupakan kombinasi dari ini, atau bahkan sesuatu yang sama sekali berbeda . Cara terbaik untuk menemukan solusi terpendek adalah mencoba segalanya .