Come giocare a golf con la ricorsione
La ricorsione, sebbene non sia l'opzione più veloce, è spesso la più breve. In genere, la ricorsione è più breve se la soluzione può essere semplificata per una parte più piccola della sfida, soprattutto se l'input è un numero o una stringa. Ad esempio, se f("abcd")
può essere calcolato da "a"
e f("bcd")
, di solito è meglio usare la ricorsione.
Prendi, ad esempio, fattoriale:
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
In questo esempio, la ricorsione è ovviamente molto più breve di qualsiasi altra opzione.
Che ne dici di somma di codici di carattere:
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
Questo è più complicato, ma possiamo vedere che se implementato correttamente, la ricorsione risparmia 4 byte .map
.
Ora diamo un'occhiata ai diversi tipi di ricorsione:
Pre-ricorsione
Questo è di solito il tipo più breve di ricorsione. L'input è diviso in due parti a
e b
, e la funzione calcola qualcosa con a
e f(b)
. Tornando al nostro esempio fattoriale:
f=n=>n?n*f(n-1):1
In questo caso, a
è n , b
è n-1 e il valore restituito è a*f(b)
.
Nota importante: tutte le funzioni ricorsive devono avere un modo per interrompere la ricorrenza quando l'ingresso è abbastanza piccolo. Nella funzione fattoriale, questo è controllato con n? :1
, ovvero se l'ingresso è 0 , restituisce 1 senza richiamare f
nuovamente.
Post-ricorsione
La post-ricorsione è simile alla pre-ricorsione, ma leggermente diversa. L'input è diviso in due parti a
e b
, e la funzione calcola qualcosa con a
, quindi chiama f(b,a)
. Il secondo argomento di solito ha un valore predefinito (cioè f(a,b=1)
).
La pre-ricorsione è buona quando devi fare qualcosa di speciale con il risultato finale. Ad esempio, se si desidera il fattoriale di un numero più 1:
f=(n,p=1)=>n?f(n-1,n*p):p+1
Anche allora, tuttavia, post- non è sempre più breve dell'uso della pre-ricorsione all'interno di un'altra funzione:
n=>(f=n=>n?n*f(n-1):1)(n)+1
Quindi quando è più corto? Si può notare che in questo esempio la post-ricorsione richiede parentesi attorno agli argomenti della funzione, mentre la pre-ricorsione non lo è. In generale, se entrambe le soluzioni richiedono parentesi attorno agli argomenti, la post-ricorsione è più corta di circa 2 byte:
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
(programmi qui presi da questa risposta )
Come trovare la soluzione più breve
Di solito l'unico modo per trovare il metodo più breve è provarli tutti. Ciò comprende:
E queste sono solo le soluzioni più comuni; la soluzione migliore potrebbe essere una combinazione di questi o anche qualcosa di completamente diverso . Il modo migliore per trovare la soluzione più breve è provare tutto .