2013-01-04 64 views
14

作爲一種學習練習,我試圖在Haskell中實現heapsort。我認爲State monad是做這件事的正確選擇,因爲堆很大程度上依賴於在單個結構內移動數據(並且do表示法會很有用)。此外,我期望鞏固我對一般單子的理解。最近Control.Monad.State API是否已更改?

examples on the State monad瞭解您一個Haskell(和othertutorials一個number),說State被定義爲:

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

我應該傳遞s -> (a,s)類型的函數(其可以是或可以不要在其他參數中被壓縮)到State值構造函數。所以,我的功能看起來像這樣:

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 

這並不編譯,並出現以下錯誤:

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

從閱讀API docsControl.Monad.State,它看起來好像State值構造已被刪除從這些教程編寫而來的模塊。作爲初學者,我發現文檔遠不是自明的。所以我的問題是:

  1. 我相信State價值的構造函數不見了嗎?
  2. 我該用什麼來代替?

回答

15
  1. 是的,它走,通過StateT取代。 (State monad現在按照StateT monad變壓器來定義。)
  2. 您應該使用state函數代替。

但是,我會質疑你的方法是否正確。不必擔心State的實現方式,請考慮使用符號和getput函數。

+0

是'state'一個下拉更換爲'State'?或者,我需要爲數據結構聲明一個'MonadState'實例,然後才能工作? –

+0

當你使用它時,'state'是'State'的替代品,是的。 'State(Heap a)'是需要成爲'MonadState'的實例,它已經是。 – dave4420

+0

建議您在'get'和'put'中使用'do'符號的建議風格是什麼?我是否應該非單調地定義大多數低級函數(例如:'push :: Ord a => a - > Heap a - > Heap a'),然後在'do中構建一個有狀態計算'使用大量'let'綁定塊?或者'push'和'pop'(或者在更低的層次上,我的樹遍歷例程)是單程函數,可以直接在'do'塊中排序? –

13

這裏是在涉及國家單子書中所示的例子正確的實現:

單子STACK:

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

單子隨機生成:

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

這非常有用 – FtheBuilder

3

我覺得state品牌簡潔地實現pop,但modify更適合push,因爲它返回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

脫單子STACK:

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

脫一元隨機數發生器:

-- 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) 
相關問題