使用 monads:折叠其部分内容
Using monads: folding over part of its contents
我刚刚完成学习 Haskell 的学习,但我仍在努力学习如何使用 Monads。
在本章的最后for a few monads more作者给出了一个练习(倒数第二段)。具体来说,他鼓励我们编写一个函数,将所有 (False, Rational)
值折叠成单个值 (False, sum Rationals)
.
我已经写出本章中出现的所有代码,并将相关部分包含在此处
import Data.Ratio
import Control.Monad
import Control.Applicative
import Data.List (all)
newtype Prob a = Prob { getProb :: [(a, Rational)]} deriving (Show)
instance Functor Prob where
fmap f (Prob xs) = Prob $ map (\(x, p) -> (f x, p)) xs
flatten :: Prob (Prob a) -> Prob a
flatten (Prob xs) = Prob $ concat $ map multAll xs
where multAll (Prob innerxs, p) = map (\(x, r) -> (x, p*r)) innerxs
instance Applicative Prob where
pure = return
(<*>) = ap
instance Monad Prob where
return x = Prob [(x, 1%1)]
m >>= f = flatten (fmap f m)
fail _ = Prob []
data Coin = Heads | Tails deriving (Show, Eq)
coin :: Prob Coin
coin = Prob [(Heads, 1%2), (Tails, 1%2)]
loadedCoin :: Prob Coin
loadedCoin = Prob [(Heads, 1%10), (Tails, 9%10)]
flipThree :: Prob Bool
flipThree = do
a <- coin
b <- coin
c <- loadedCoin
return (all (== Tails) [a, b, c])
当我运行这个代码时,我得到
ghci> getProb flipThree
[(False,1 % 40),(False,9 % 40),(False,1 % 40),(False,9 % 40),(False,1 % 40),(False,9 % 40),(False,1 % 40),(True,9 % 40)]
我想以某种方式过滤 flipThree
中第一个位置有 False
的元素,然后对相关概率求和。我已经编写了一些丑陋的非 monadic 代码来执行此操作,但我相信有更好的方法。
所需的输出将是
ghci> getProb flipThree
[(False,31 % 40),(True,9 % 40)]
您想要的功能是
import Data.Function
import Data.List
runProb :: Eq a => Prob a -> [(a, Rational)]
runProb = map (\x -> (fst (head x), sum (map snd x)))
. groupBy ((==) `on` fst)
. getProb
所以
>runProb flipThree
[(False,31 % 40),(True,9 % 40)]
你也可以让你的类型成为 MonadPlus
的一个实例来排序得到这个结果 "directly":
instance MonadPlus Prob where
mzero = Prob []
mplus (Prob x) (Prob y) = Prob (x++y)
flipThree' = do
a <- coin
b <- coin
c <- loadedCoin
guard (all (== Tails) [a, b, c])
> runProb flipThree'
[((),9 % 40)]
我刚刚完成学习 Haskell 的学习,但我仍在努力学习如何使用 Monads。
在本章的最后for a few monads more作者给出了一个练习(倒数第二段)。具体来说,他鼓励我们编写一个函数,将所有 (False, Rational)
值折叠成单个值 (False, sum Rationals)
.
我已经写出本章中出现的所有代码,并将相关部分包含在此处
import Data.Ratio
import Control.Monad
import Control.Applicative
import Data.List (all)
newtype Prob a = Prob { getProb :: [(a, Rational)]} deriving (Show)
instance Functor Prob where
fmap f (Prob xs) = Prob $ map (\(x, p) -> (f x, p)) xs
flatten :: Prob (Prob a) -> Prob a
flatten (Prob xs) = Prob $ concat $ map multAll xs
where multAll (Prob innerxs, p) = map (\(x, r) -> (x, p*r)) innerxs
instance Applicative Prob where
pure = return
(<*>) = ap
instance Monad Prob where
return x = Prob [(x, 1%1)]
m >>= f = flatten (fmap f m)
fail _ = Prob []
data Coin = Heads | Tails deriving (Show, Eq)
coin :: Prob Coin
coin = Prob [(Heads, 1%2), (Tails, 1%2)]
loadedCoin :: Prob Coin
loadedCoin = Prob [(Heads, 1%10), (Tails, 9%10)]
flipThree :: Prob Bool
flipThree = do
a <- coin
b <- coin
c <- loadedCoin
return (all (== Tails) [a, b, c])
当我运行这个代码时,我得到
ghci> getProb flipThree
[(False,1 % 40),(False,9 % 40),(False,1 % 40),(False,9 % 40),(False,1 % 40),(False,9 % 40),(False,1 % 40),(True,9 % 40)]
我想以某种方式过滤 flipThree
中第一个位置有 False
的元素,然后对相关概率求和。我已经编写了一些丑陋的非 monadic 代码来执行此操作,但我相信有更好的方法。
所需的输出将是
ghci> getProb flipThree
[(False,31 % 40),(True,9 % 40)]
您想要的功能是
import Data.Function
import Data.List
runProb :: Eq a => Prob a -> [(a, Rational)]
runProb = map (\x -> (fst (head x), sum (map snd x)))
. groupBy ((==) `on` fst)
. getProb
所以
>runProb flipThree
[(False,31 % 40),(True,9 % 40)]
你也可以让你的类型成为 MonadPlus
的一个实例来排序得到这个结果 "directly":
instance MonadPlus Prob where
mzero = Prob []
mplus (Prob x) (Prob y) = Prob (x++y)
flipThree' = do
a <- coin
b <- coin
c <- loadedCoin
guard (all (== Tails) [a, b, c])
> runProb flipThree'
[((),9 % 40)]