En lisant http://uncyclopedia.wikia.com/wiki/Haskell (et en ignorant toutes les choses "offensantes"), je suis tombé sur le morceau de code obscurci suivant:
fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))$1
Lorsque j'exécute ce morceau de code dans ghci
(après avoir importé Data.Function
et Control.Applicative
), ghci
affiche la liste de toutes les puissances de 2.
Comment fonctionne ce morceau de code?
Pour commencer, nous avons la belle définition
x = 1 : map (2*) x
ce qui en soi est un peu hallucinant si vous ne l'avez jamais vu auparavant. Quoi qu'il en soit, c'est une astuce assez standard de paresse et de récursivité. Maintenant, nous allons nous débarrasser de la récursivité explicite en utilisant fix
et point-free-ify.
x = fix (\vs -> 1 : map (2*) vs)
x = fix ((1:) . map (2*))
La prochaine chose que nous allons faire est d'étendre le :
section et rendre la map
inutilement complexe.
x = fix ((:) 1 . (map . (*) . (*2)) 1)
Eh bien, nous avons maintenant deux copies de cette constante 1
. Cela ne fera jamais l'affaire, nous allons donc utiliser le lecteur applicatif pour dédoublonner cela. De plus, la composition des fonctions est un peu détraquée, alors remplaçons-la par (<$>)
partout où nous le pouvons.
x = fix (liftA2 (.) (:) (map . (*) . (*2)) 1)
x = fix (((.) <$> (:) <*> (map . (*) . (*2))) 1)
x = fix (((<$>) <$> (:) <*> (map <$> (*) <$> (*2))) 1)
Ensuite: cet appel à map
est beaucoup trop lisible. Mais il n'y a rien à craindre: on peut utiliser les lois de la monade pour l'étendre un peu. En particulier, fmap f x = x >>= return . f
, donc
map f x = x >>= return . f
map f x = ((:[]) <$> f) =<< x
Nous pouvons pointer-libre-ify, remplacer (.)
avec (<$>)
, puis ajoutez des sections parasites:
map = (=<<) . ((:[]) <$>)
map = (=<<) <$> ((:[]) <$>)
map = (<$> ((:[]) <$>)) (=<<)
En remplaçant cette équation dans notre étape précédente:
x = fix (((<$>) <$> (:) <*> ((<$> ((:[]) <$>)) (=<<) <$> (*) <$> (*2))) 1)
Enfin, vous cassez votre barre d'espace et produisez la merveilleuse équation finale
x=fix(((<$>)<$>(:)<*>((<$>((:[])<$>))(=<<)<$>(*)<$>(*2)))1)
J'écrivais une longue réponse avec un parcours complet de mes IRC journaux des expériences menant au code final (c'était au début de 2008), mais j'ai accidentellement tout le texte :) Non autant de perte - pour la plupart, l'analyse de Daniel est parfaite.
Voici ce que j'ai commencé avec:
Jan 25 23:47:23 <olsner> @pl let q = 2 : map (2*) q in q
Jan 25 23:47:23 <lambdabot> fix ((2 :) . map (2 *))
Les différences se résument principalement à l'ordre dans lequel les restructurations ont eu lieu.
x = 1 : map (2*) x
j'ai commencé avec 2 : map ...
, Et j'ai gardé ce 2 initial jusqu'à la toute dernière version, où j'ai pressé un (*2)
Et changé le $2
À la fin en $1
. L'étape "rendre la carte inutilement complexe" ne s'est pas produite (si tôt).map
a été ajoutée avant de remplacer liftM2 par des combinateurs applicatifs. C'est aussi là que tous les espaces ont disparu..
Pour la composition des fonctions. Le remplacement de tous ceux par <$>
S'est apparemment produit au cours des mois entre cela et l'encyclopédie.BTW, voici une version mise à jour qui ne mentionne plus le nombre 2
:
fix$(<$>)<$>(:)<*>((<$>((:[{- Jörð -}])<$>))(=<<)<$>(*)<$>(>>=)(+)($))$1