使用monads:折叠部分内容

时间:2015-01-28 06:47:30

标签: haskell

我刚刚结束了为了一个很好的学习你的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)]

1 个答案:

答案 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)]