En lisant https://en.uncyclopedia.co/wiki/Haskell (et en ignorant tous les trucs "offensants"), 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 l'importation Data.Function
et Control.Applicative
), ghci
imprime la liste de toutes les puissances de 2.
Comment fonctionne ce morceau de code?
Réponses:
Pour commencer, nous avons la belle définition
ce qui en soi est un peu hallucinant si vous ne l'avez jamais vu auparavant. Quoi qu'il en soit, c'est un truc assez classique de paresse et de récursivité. Maintenant, nous allons nous débarrasser de la récursivité explicite en utilisant
fix
, et point-free-ify.La prochaine chose que nous allons faire est d'élargir la
:
section et de rendre la partiemap
inutilement complexe.Eh bien, nous avons maintenant deux copies de cette constante
1
. Cela ne fonctionnera jamais, nous utiliserons donc l'applicatif du lecteur pour dédupliquer cela. De plus, la composition des fonctions est un peu nul, alors remplaçons cela par(<$>)
là où nous le pouvons.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'élargir un peu. En particulierfmap f x = x >>= return . f
, doncNous pouvons sans point-ify, remplacer
(.)
par(<$>)
, puis ajouter des sections parasites:En remplaçant cette équation dans notre étape précédente:
Enfin, vous cassez votre barre d'espace et produisez la merveilleuse équation finale
la source
{- thor's mother -}
!J'écrivais une longue réponse avec un aperçu complet de mes journaux IRC des expériences menant au code final (c'était au début de 2008), mais j'ai accidentellement tout le texte :) Pas tellement de perte cependant - pour le l'analyse de Daniel est en grande partie parfaite.
Voici par quoi j'ai commencé:
Les différences se résument principalement à l'ordre dans lequel les refactorisations ont eu lieu.
x = 1 : map (2*) x
j'ai commencé par2 : map ...
, et j'ai gardé ce 2 initial jusqu'à la toute dernière version, où j'ai inséré un(*2)
et changé le$2
à la fin en$1
. L'étape «rendre la carte inutilement complexe» n'a pas eu lieu (si tôt).map
fonction masquée a été mise en place avant de remplacer liftM2 par des combinateurs applicatifs. C'est aussi là que tous les espaces ont disparu..
composition de fonction restante. Le remplacement de tous ceux par<$>
apparemment s'est produit quelque temps dans les mois entre cela et l'encyclopédie.BTW, voici une version mise à jour qui ne mentionne plus le numéro
2
:la source
Les deux réponses dérivent l'extrait de code obscurci du court original donné à l'improviste, mais la question demande en fait comment le long code obscurci fait son travail.
Voici comment:
Voici
( (\ x -> [2*x]) =<<) = (>>= (\ x -> [2*x])) = concatMap (\ x -> [2*x]) = map (2*)
une fonction, donc encore une fois<$> = .
:sont toutes les puissances de 2 , par ordre croissant.
Cela utilise
A <$> B <*> C $ x = liftA2 A B C x
et puisqueliftA2 A B C
c'est appliqué àx
c'est une fonction, une fonction que cela signifieliftA2 A B C x = A (B x) (C x)
.(f `op` g) = op f g = (f `op`) g = (`op` g) f
sont les trois lois des sections opérateur>>=
est une liaison monadique, et depuis(`op` g) f = op f g
et les types sontpar type application et substitution on voit que la monade en question est
[]
pour qui(>>= g) = concatMap g
.concatMap (\ x -> [2*x]) xs
est simplifié commeet par définition,
où
pour que
la source