我刚刚结束了为了一个很好的学习你的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)]
答案 0 :(得分:3)
您想要的功能是
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
的实例,以便直接获得此结果&#34;&#34;:
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)]