Comment jouer au golf avec récursion
La récursion, bien que n'étant pas l'option la plus rapide, est très souvent la plus courte. En règle générale, la récursivité est la plus courte si la solution peut être simplifiée, mais surtout si l’entrée est un nombre ou une chaîne. Par exemple, si f("abcd")
peut être calculé à partir de "a"
etf("bcd")
, il est généralement préférable d'utiliser la récursivité.
Prenons, par exemple, factorielle:
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
Dans cet exemple, la récursivité est évidemment beaucoup plus courte que toute autre option.
Que diriez-vous de la somme des charcodes:
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
Celui-ci est plus compliqué, mais nous pouvons voir que lorsqu'il est correctement implémenté, la récursivité économise 4 octets .map
.
Voyons maintenant les différents types de récursivité:
Pré-récursion
C'est généralement le type de récursivité le plus court. L'entrée est divisée en deux parties a
et b
, et la fonction calcule quelque chose avec a
et f(b)
. Revenons à notre exemple factoriel:
f=n=>n?n*f(n-1):1
Dans ce cas, a
est n , b
est n-1 , et la valeur retournée est a*f(b)
.
Remarque importante: Toutes les fonctions récursives doivent avoir un moyen d'arrêter de récursir lorsque l'entrée est suffisamment petite. Dans la fonction factorielle, ceci est contrôlé avec le n? :1
, c'est-à-dire que si l'entrée est 0 , retourne 1 sans appeler à f
nouveau.
Post-récurrence
La post-récurrence est similaire à la pré-récursion, mais légèrement différente. L'entrée est divisée en deux parties a
et b
, et la fonction calcule quelque chose avec a
, puis appelle f(b,a)
. Le second argument a généralement une valeur par défaut (ie f(a,b=1)
).
La pré-récursivité est bonne lorsque vous devez faire quelque chose de spécial avec le résultat final. Par exemple, si vous voulez la factorielle d’un nombre plus 1:
f=(n,p=1)=>n?f(n-1,n*p):p+1
Cependant, même dans ce cas, la post-utilisation n’est pas toujours plus courte que l’utilisation de la pré-récursion dans une autre fonction:
n=>(f=n=>n?n*f(n-1):1)(n)+1
Alors, quand est-ce plus court? Vous remarquerez peut-être que la post-récursion dans cet exemple nécessite des parenthèses autour des arguments de la fonction, contrairement à la pré-récursion. En règle générale, si les deux solutions nécessitent des parenthèses autour des arguments, la post-récurrence est environ 2 octets plus courte:
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
(programmes ici tirés de cette réponse )
Comment trouver la solution la plus courte
Habituellement, la seule façon de trouver la méthode la plus courte est de toutes les essayer. Ceci comprend:
Et ce ne sont que les solutions les plus courantes; la meilleure solution pourrait être une combinaison de ceux-ci, ou même quelque chose de complètement différent . La meilleure façon de trouver la solution la plus courte est de tout essayer .