J'ai enfin réussi à utiliser les monades (je ne sais pas si je les comprends ...), mais mon code n'est jamais très élégant. Je suppose est d'un manque d'emprise sur la façon dont toutes ces fonctions sur Control.Monad
peuvent vraiment aider. Donc, je pensais que ce serait bien de demander des conseils à ce sujet dans un morceau de code particulier en utilisant la monade d'état.Conseils pour un code plus élégant avec des monades?
Le but du code est de calculer de nombreux types de marches aléatoires, et c'est quelque chose que j'essaie de faire avant quelque chose de plus compliqué. Le problème est que j'ai deux calculs stateful en même temps, et je voudrais savoir comment les composer avec élégance:
- La fonction qui met à jour le générateur de nombres aléatoires est quelque chose de type
Seed -> (DeltaPosition, Seed)
- La fonction qui met à jour la position du marcheur aléatoire est quelque chose de type
DeltaPosition -> Position -> (Log, Position)
(oùLog
est juste un moyen pour moi de signaler quelle est la position actuelle du marcheur aléatoire).
Ce que je l'ai fait est la suivante:
J'ai une fonction pour composer ces deux calculs: stateful
composing :: (g -> (b, g)) -> (b -> s -> (v,s)) -> (s,g) -> (v, (s, g))
composing generate update (st1, gen1) = let (rnd, gen2) = generate gen1
(val, st2) = update rnd st1
in (val, (st2, gen2))
puis je en faire une fonction qui composent les états:
stateComposed :: State g b -> (b -> State s v) -> State (s,g) v
stateComposed rndmizer updater = let generate = runState rndmizer
update x = runState $ updater x
in State $ composing generate update
Et puis j'ai la chose la plus simple, par exemple, un marcheur aléatoire qui va simplement additionner un nombre aléatoire à sa position actuelle:
update :: Double -> State Double Double
update x = State (\y -> let z = x+y
in (z,z))
generate :: State StdGen Double
generate = State random
rolling1 = stateComposed generate update
et une fonction pour ce faire à plusieurs reprises:
rollingN 1 = liftM (:[]) rolling1
rollingN n = liftM2 (:) rolling1 rollings
where rollings = rollingN (n-1)
Et puis, si je charge cela dans ghci
et lancez:
*Main> evalState (rollingN 5) (0,mkStdGen 0)
[0.9872770354820595,0.9882724161698186,1.9620425108498993,2.0923229488759123,2.296045158010918]
Je reçois ce que je veux, ce qui est liste des positions occupées par le marcheur aléatoire. Mais ... je pense qu'il doit y avoir une façon plus élégante de faire cela. J'ai deux questions:
Puis-je réécrire ces fonctions d'une manière plus « monade », en utilisant des fonctions intelligentes de
Control.Monad
?Existe-t-il un modèle général de combinaison d'états comme celui-ci pouvant être utilisé? Est-ce que cela a quelque chose à voir avec les transformateurs monad ou quelque chose comme ça?
Par ailleurs, il est une bonne idée d'éviter d'utiliser le 'constructeur de données State', puisque dans' successeur de mtl' ('monades-fd'),' State' est défini en termes de 'StateT' et donc le constructeur de données' State' n'existe pas. –
@TravisBrown En fait, 'monads-fd' est déprécié en faveur de' mtl'. (Reconnaissant que votre commentaire a 5 ans.) – crockeea