2013-01-04 16 views
14

Come esercizio di apprendimento, sto cercando di implementare un heapsort in Haskell. Ho pensato che la monade State sarebbe stata la scelta giusta per fare questo, dal momento che gli heap si basano abbastanza sul trasferimento dei dati all'interno di una singola struttura (e la notazione do sarebbe utile). Inoltre, sto cercando di consolidare la mia comprensione delle monadi in generale.L'API Control.Monad.State è stata modificata di recente?

Il examples on the State monad in impari A Haskell (e un number di othertutorials), dicono che State è definito come:

newtype State s a = State { runState :: s -> (a,s) } 

dovrei essere di passaggio in funzione del tipo di s -> (a,s) (che può essere o non essere curried in altri argomenti) al costruttore del valore State. Quindi le mie funzioni simile a questa:

pop :: Ord a => State (Heap a) a 
pop = State pop' 
pop' :: Ord a => Heap a -> (a, Heap a) 
-- implementation of pop' goes here 

push :: Ord a => a -> State (Heap a)() 
push item = State $ push' item 
push' :: Ord a => a -> Heap a -> ((), Heap a) 
-- implementation of push' goes here 

Questo non viene compilato, con il seguente errore:

Not in scope: data constructor `State' 
Perhaps you meant `StateT' (imported from Control.Monad.State) 

Dalla lettura del API docs per Control.Monad.State, sembra che il costruttore State valore è stato rimosso dal modulo poiché sono stati scritti questi tutorial. Come principiante, trovo che la documentazione sia lungi dall'essere autoesplicativa. Quindi la mia domanda è:

  1. Ho ragione nel ritenere che il costruttore del valore State sia sparito?
  2. Cosa dovrei usare invece?

risposta

15
  1. Sì, non c'è più, sostituita da StateT. (Il monad State è ora definito in termini di trasformatore monad StateT.)
  2. Si dovrebbe invece utilizzare la funzione state.

Mi chiederei se il tuo approccio è corretto, tuttavia. Invece di preoccuparsi di come è implementato lo standard State, è consigliabile utilizzare la notazione e le funzioni get e put.

+0

è 'state' una sostituzione drop-in per' State'? O devo dichiarare un'istanza di 'MonadState' per la mia struttura dati prima che funzioni? –

+0

'state' è un rimpiazzo sostitutivo per' State' mentre lo stai usando, sì. 'State (Heap a)' è ciò che deve essere un'istanza di 'MonadState', e lo è già. – dave4420

+0

Qual è lo stile consigliato per il tuo suggerimento di usare la notazione 'do' con' get' e 'put'? Dovrei definire la maggior parte delle mie funzioni di basso livello non monadicamente (per esempio: 'push :: Ord a => a -> Heap a -> Heap a'), e quindi costruisci un calcolo statico di questi in un' do 'Blocca usando un sacco di legami' let'? O dovremmo 'push' e' pop' (o, a un livello inferiore, le mie routine tree-traversal) essere funzioni monadiche che possono essere sequenziate direttamente in un blocco 'do'? –

13

Ecco le corrette implementazioni degli esempi riportati nel libro relativi a Stato Monade:

monadici PILA:

-- MonadicStack.hs (Learn You a Haskell for Great Good!) 

import Control.Monad.State 

type Stack = [Int] 

pop :: State Stack Int 
-- The following line was wrong in the book: 
-- pop = State $ \(x:xs) -> (x,xs) 
pop = do 
x:xs <- get 
put xs 
return x 

push :: Int -> State Stack() 
-- The following line was wrong in the book: 
-- push a = State $ \xs -> ((),a:xs) 
push a = do 
xs <- get 
put (a:xs) 
return() 

pop1 = runState pop [1..5] 
push1 = runState (push 1) [2..5] 

stackManip :: State Stack Int 
stackManip = do 
push 3 
a <- pop 
pop 

stackManip1 = runState stackManip [5,8,2,1] 
stackManip2 = runState stackManip [1,2,3,4] 

stackStuff :: State Stack() 
stackStuff = do 
a <- pop 
if a == 5 
    then push 5 
    else do 
    push 3 
    push 8 

stackStuff1 = runState stackStuff [9,0,2,1,0] 
stackStuff2 = runState stackStuff [5,4,3,2,1] 

moreStack :: State Stack() 
moreStack = do 
a <- stackManip 
if a == 100 
    then stackStuff 
    else return() 

moreStack1 = runState moreStack [100,9,0,2,1,0] 
moreStack2 = runState moreStack [9,0,2,1,0] 

stackyStack :: State Stack() 
stackyStack = do 
stackNow <- get 
if stackNow == [1,2,3] 
    then put [8,3,1] 
    else put [9,2,1] 

stackyStack1 = runState stackyStack [1,2,3] 
stackyStack2 = runState stackyStack [10,20,30,40] 

monadici RANDOM GENERATORI:

-- MonadicRandomGenerator.hs (Learn You a Haskell for Great Good!) 

import System.Random 
import Control.Monad.State 

randomSt :: (RandomGen g, Random a) => State g a 
-- The following line was wrong in the book: 
-- randomSt = State random 
randomSt = do 
gen <- get 
let (value,nextGen) = random gen 
put nextGen 
return value 

randomSt1 = (runState randomSt (mkStdGen 1)) :: (Int,StdGen) 
randomSt2 = (runState randomSt (mkStdGen 2)) :: (Float,StdGen) 

threeCoins :: State StdGen (Bool,Bool,Bool) 
threeCoins = do 
a <- randomSt 
b <- randomSt 
c <- randomSt 
return (a,b,c) 

threeCoins1 = runState threeCoins (mkStdGen 33) 
threeCoins2 = runState threeCoins (mkStdGen 2) 

-- rollDie and rollNDice are not explained in the book LYAHFGG. 
-- But these functions are interesting and complementary: 

rollDie :: State StdGen Int 
rollDie = do 
generator <- get 
let (value, newGenerator) = randomR (1,6) generator 
put newGenerator 
return value 

rollDie1 = runState rollDie (mkStdGen 1) 
rollDie2 = runState rollDie (mkStdGen 2) 

rollNDice :: Int -> State StdGen [Int] 
rollNDice 0 = do 
return [] 
rollNDice n = do 
value <- rollDie 
list <- rollNDice (n-1) 
return (value:list) 

rollNDice1 = runState (rollNDice 10) (mkStdGen 1) 
rollNDice2 = runState (rollNDice 20) (mkStdGen 2) 
+0

È abbastanza utile – FtheBuilder

3

penso state marche per un'implementazione succinta di pop, ma modify è meglio per push poiché restituisce l'uni t:

import Control.Monad.State 

type Stack a = [a] 

pop :: State (Stack a) a 
pop = state $ \(a:as) -> (a, as) 

push :: a -> State (Stack a)() 
push a = modify (a:) 
2

private degli zuccheri monadici PILA:

-- DesugaredMonadicStack.hs (Learn You a Haskell for Great Good!) 

import Control.Monad.State 

type Stack = [Int] 

pop :: State Stack Int 
pop = 
get >>= 
\(x:xs) -> put xs >> 
return x 

push :: Int -> State Stack() 
push a = 
get >>= 
\xs -> put (a:xs) >> 
return() 

pop1 = runState pop [1..5] 
push1 = runState (push 1) [2..5] 

stackManip :: State Stack Int 
stackManip = 
push 3 >> 
pop >>= 
\a -> pop 

stackManip1 = runState stackManip [5,8,2,1] 
stackManip2 = runState stackManip [1,2,3,4] 

stackStuff :: State Stack() 
stackStuff = 
pop >>= 
\a -> 
    if a == 5 then 
    push 5 
    else 
    push 3 >> 
    push 8 

stackStuff1 = runState stackStuff [9,0,2,1,0] 
stackStuff2 = runState stackStuff [5,4,3,2,1] 

moreStack :: State Stack() 
moreStack = 
stackManip >>= 
\a -> 
    if a == 100 then 
    stackStuff 
    else 
    return() 

moreStack1 = runState moreStack [100,9,0,2,1,0] 
moreStack2 = runState moreStack [9,0,2,1,0] 
moreStack3 = runState moreStack [100,5,4,3,2,1] 

stackyStack :: State Stack() 
stackyStack = 
get >>= 
\stackNow -> 
    if stackNow == [1,2,3] then 
    put [8,3,1] 
    else 
    put [9,2,1] 

stackyStack1 = runState stackyStack [1,2,3] 
stackyStack2 = runState stackyStack [10,20,30,40] 

private degli zuccheri monadici Generatore casuale:

-- DesugaredMonadicRandomGenerator.hs (Learn You a Haskell for Great Good!) 

import System.Random 
import Control.Monad.State 

randomSt :: (RandomGen g, Random a) => State g a 
randomSt = 
get >>= 
\gen -> 
    let (value,nextGen) = random gen 
    in 
    put nextGen >> 
    return value 

randomSt1 = (runState randomSt (mkStdGen 1)) :: (Int,StdGen) 
randomSt2 = (runState randomSt (mkStdGen 2)) :: (Float,StdGen) 

threeCoins :: State StdGen (Bool,Bool,Bool) 
threeCoins = 
randomSt >>= 
\a -> randomSt >>= 
\b -> randomSt >>= 
\c -> return (a,b,c) 

threeCoins1 = runState threeCoins (mkStdGen 33) 
threeCoins2 = runState threeCoins (mkStdGen 2) 

-- rollDie and rollNDice are not explained in the book LYAHFGG. 
-- But these functions are interesting and complementary: 

rollDie :: State StdGen Int 
rollDie = 
get >>= 
\generator -> 
    let (value, newGenerator) = randomR (1,6) generator 
    in 
    put newGenerator >> 
    return value 

rollDie1 = runState rollDie (mkStdGen 1) 
rollDie2 = runState rollDie (mkStdGen 2) 

rollNDice :: Int -> State StdGen [Int] 
rollNDice 0 = return [] 
rollNDice n = 
rollDie >>= 
\value -> rollNDice (n-1) >>= 
\list -> return (value:list) 

rollNDice1 = runState (rollNDice 10) (mkStdGen 1) 
rollNDice2 = runState (rollNDice 20) (mkStdGen 2) 
Problemi correlati