2013-01-04 16 views
14

Jako ćwiczenie edukacyjne próbuję zaimplementować heapsort w Haskell. Pomyślałem, że monada State byłaby właściwym wyborem, ponieważ sterty polegają w dużym stopniu na przenoszeniu danych w obrębie jednej struktury (i przydałaby się notacja). Poza tym staram się umocnić moje zrozumienie monad w ogóle.Czy ostatnio zmieniono interfejs API Control.Monad.State?

examples on the State monad w Dowiedz się Państwo Haskell (i number z othertutorials), mówią, że State jest zdefiniowany jako:

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

Byłbym przechodzącej funkcję typu s -> (a,s) (które mogą, nie należy stosować curry w innych argumentach) do konstruktora wartości State. Więc moje funkcje wyglądać następująco:

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 

nie kompilacji, z powodu następującego błędu:

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

z lektury API docs dla Control.Monad.State, wygląda jakby konstruktor State wartość została usunięta z modułu, ponieważ te tutoriale zostały napisane. Jako początkujący uważam dokumentację za daleka od oczywistej. Moje pytanie brzmi:

  1. Czy mam rację, sądząc, że konstruktor wartości State zniknął?
  2. Co powinienem użyć zamiast tego?

Odpowiedz

15
  1. Tak, to znika, zastąpiony przez StateT. (Monadę State definiuje się teraz jako transformator monadowy StateT).
  2. Powinieneś używać funkcji state.

Chciałbym zapytać, czy twoje podejście jest poprawne. Zamiast martwić się o to, jak wprowadzono State, należy rozważyć zastosowanie funkcji do-notacji i funkcji get i.

+0

jest 'state' zastąpienie drop-in dla' State'? A może muszę zadeklarować instancję 'MonadState' dla mojej struktury danych, zanim będzie działać? –

+0

'Stan' jest zamiennikiem dla' stanu', jak go używasz, tak. 'State (Heap a)' jest tym, co musi być instancją 'MonadState', i to już jest. – dave4420

+0

Jaki jest zalecany styl dla twojej sugestii użycia notacji 'do' z' get' i 'put'? Czy powinienem definiować większość moich funkcji niskopoziomowych w sposób niemonadyczny (na przykład: 'push :: Ord a => a -> Heap a -> Heap a'), a następnie zbuduj z nich obliczenia stanowe w' do 'blokowanie za pomocą wielu wiązań' let'? Czy też 'push' i' pop' (lub, na niższym poziomie, moje procedury przechodzenia drzewa) są monadycznymi funkcjami, które mogą być sekwencjonowane bezpośrednio w bloku 'do'? –

13

Oto poprawne implementacje przykładach przedstawionych w książce związanej State Monady:

monadycznego stosu:

-- 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] 

monadycznego RANDOM generatorów:

-- 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

Jest to całkiem przydatne – FtheBuilder

3

myślę state marki dla zwięzłej implementacji pop, ale modify jest lepsze dla push, ponieważ zwraca 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

odcukrzona monadycznego stosu:

-- 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] 

odcukrzona monadycznego generator liczb losowych:

-- 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)