Haskell-汇总概率表

Haskell-汇总概率表,haskell,fold,Haskell,Fold,我将通过哈斯克尔向你学习,刚刚完成“更多的几个单子”部分。在这一部分中,我们创建了一个newtype Prob a=Prob{getProb::[(a,Rational)]}并为其创建了一个Monad实例。这使我们能够计算非确定性计算中的结果概率,如下所示: data Coin = Heads | Tails deriving (Show, Eq) coin :: Prob Coin coin = Prob [(Heads, 1%2), (Tails, 1%2)] loadedCoin ::

我将通过哈斯克尔向你学习,刚刚完成“更多的几个单子”部分。在这一部分中,我们创建了一个
newtype Prob a=Prob{getProb::[(a,Rational)]}
并为其创建了一个
Monad
实例。这使我们能够计算非确定性计算中的结果概率,如下所示:

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

coinTest :: Prob Bool
coinTest = do
    a <- coin
    b <- coin
    c <- loadedCoin
    return (all (==Tails) [a,b,c])
读者可以编写一个简洁的函数来总结所有的
False
s和
True
s,这样我们就得到了
[(True,9%40),(False,31%40)]
。我已经设法做到了这一点。它适用于这种特殊情况,但我觉得它根本不是一个有用的函数,因为它太专业化了。以下是我的想法:

sumProbs :: Prob Bool -> Prob Bool
sumProbs (Prob ps) = let (trues, falses) = partition fst ps
                         ptrue = reduce trues
                         pfalse = reduce falses
                     in Prob [ptrue, pfalse]
                     where reduce = foldr1 (\(b,r) (_,r') -> (b,r+r'))
我很想把它推广到任何
eqa=>probaa
,但到目前为止运气不好。我在考虑可能使用
Map
unionWith
或者类似的东西。或者我可以利用
(a,b)
有一个
函子b
实例这一事实?我想我缺少了一些更简单更优雅的解决方案


因此,总结一下:我如何编写一个函数
sumProbs::(Eq a)=>Prob a->Prob a
,它将共享相同值(键)的所有概率相加?

使用
Map
是个好主意,但除了
Eq a
之外,还需要
Ord a
。如果您对此满意,那么我们也可以使用更简单的列表解决方案:只需将
分区
替换为和的组合:


如果使用
Data.Map
,则和将执行以下操作:


Ord a
放宽到
Eq a
将需要效率较低的二次计算;比如:

sumProbs :: (Eq a) => Prob a -> Prob a
sumProbs = Prob . foldr go [] . getProb
    where
    go (x, y) = run
        where
        run [] = (x, y):[]
        run ((a, b):rest)
            | x == a    = (x, y + b): rest
            | otherwise = (a, b): run rest

哦,天哪,那很方便!我不熟悉
groupBy
/
orderBy
(当然我在其他语言中也使用过)。阅读代码很明显它是做什么的,尽管我必须阅读
上的
并练习我的fu型!我想你不知道如何在
foldr1(\(b,r)(\ur')->(b,r+r'))
中摆脱相当丑陋的lambda?@kai:IMO认为lambda很好,但如果你愿意,你可以做
foldr1(second.(+).snd)
。啊,是的,这很干净。我想缺点是,这需要和
Ord
约束,而另一种解决方案使用
Eq
(我认为
sortBy
是不必要的)。@Kai
groupBy
如果值相等的键彼此不相邻,则不会分组(尝试
group[1,2,1]
)。所以,
sortBy
是必要的哦,那就解决了!我想这是出于性能原因?@kai如果有许多重复的键,它可能会执行得更快(这将执行
n log u
,其中
u
是唯一键的数目)
import Data.List (groupBy, sortBy)
import Data.Function (on)

sumProbs :: (Ord a) => Prob a -> Prob a
sumProbs (Prob ps) = Prob . map reduce
                   . groupBy ((==)`on`fst)
                   $ sortBy (compare`on`fst) ps
import Data.Map (toList, fromListWith)

newtype Prob a = Prob { getProb :: [(a, Rational)] }
    deriving Show

sumProbs :: (Ord a) => Prob a -> Prob a
sumProbs = Prob . toList . fromListWith (+) . getProb
sumProbs :: (Eq a) => Prob a -> Prob a
sumProbs = Prob . foldr go [] . getProb
    where
    go (x, y) = run
        where
        run [] = (x, y):[]
        run ((a, b):rest)
            | x == a    = (x, y + b): rest
            | otherwise = (a, b): run rest