Finalmente ho imparato come usare le monadi (non so se le capisco ...), ma il mio codice non è mai molto elegante. Immagino sia da una mancanza di presa su come tutte quelle funzioni su Control.Monad
possono davvero aiutare. Quindi ho pensato che sarebbe stato carino chiedere consigli su questo in un particolare pezzo di codice usando la monade di stato.Suggerimenti per un codice più elegante con le monadi?
L'obiettivo del codice è calcolare molti tipi di percorsi casuali, ed è qualcosa che sto cercando di fare prima di qualcosa di più complicato. Il problema è che ho due calcoli stateful allo stesso tempo, e mi piacerebbe sapere come comporre loro con eleganza:
- La funzione che aggiorna il generatore di numeri casuali è qualcosa di tipo
Seed -> (DeltaPosition, Seed)
- La funzione che aggiorna la posizione del walker casuale è qualcosa di tipo
DeltaPosition -> Position -> (Log, Position)
(doveLog
è solo un modo per me di segnalare qual è la posizione corrente del walker casuale).
Quello che ho fatto è questo:
Ho una funzione di comporre questi due calcoli 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))
e poi mi trasformarlo in una funzione che compongono gli stati:
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
E poi ho la cosa più semplice, ad esempio, un walker casuale che somma solo un numero casuale alla sua posizione corrente:
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
e una funzione per fare questo più volte:
rollingN 1 = liftM (:[]) rolling1
rollingN n = liftM2 (:) rolling1 rollings
where rollings = rollingN (n-1)
E poi, se io carico questo ghci
e corro:
*Main> evalState (rollingN 5) (0,mkStdGen 0)
[0.9872770354820595,0.9882724161698186,1.9620425108498993,2.0923229488759123,2.296045158010918]
ottengo quello che voglio, che è un elenco delle posizioni occupate dal walker casuale. Ma ... sento che ci deve essere un modo più elegante per farlo. Ho due domande:
possibile riscrivere queste funzioni in un modo più "monade", utilizzando le funzioni intelligenti da
Control.Monad
?Esiste un motivo generale per combinare stati come questo che possono essere utilizzati? Questo ha qualcosa a che fare con i trasformatori monad o qualcosa del genere?
Tra l'altro, è una buona idea per evitare di usare il costruttore di dati 'State', dal momento che in' successore di mtl' ('monadi-fd'),' State' è definito in termini di 'StateT' e quindi il costruttore di dati' State' non esiste. –
@TravisBrown In realtà, 'monads-fd' è deprecato a favore di' mtl'. (Riconoscendo che il tuo commento ha 5 anni.) – crockeea