我有一種型別來描述校準后放射性碳日期概率分布。細節和背景對于這個問題并不重要:它歸結_calPDFDens為每年的一個概率值_calPDFCals:
data CalPDF = CalPDF {
-- | Sample identifier, e.g. a lab number
_calPDFid :: String
-- | Years calBCAD
, _calPDFCals :: VU.Vector YearBCAD
-- | Probability densities for each year in '_calPDFCals'
, _calPDFDens :: VU.Vector Float
}
(VU是Data.Vector.Unboxed)
現在:通常的做法是對多個此類分布求和以得出和概率分布。這意味著對 in 的年份進行完全外部聯接_calPDFCals,然后將_calPDFDens. 我實作了如下:
sumPDFs :: CalPDF -> CalPDF -> CalPDF
sumPDFs = combinePDFs ( )
combinePDFs :: (Float -> Float -> Float) -> CalPDF -> CalPDF -> CalPDF
combinePDFs f (CalPDF name1 cals1 dens1) (CalPDF name2 cals2 dens2) =
let startRange = minimum [VU.head cals1, VU.head cals2]
stopRange = maximum [VU.last cals1, VU.last cals2]
emptyBackdrop = zip [startRange..stopRange] (repeat (0.0 :: Float))
pdf1 = VU.toList $ VU.zip cals1 dens1
pdf2 = VU.toList $ VU.zip cals2 dens2
pdfCombined = fullOuter f pdf2 (fullOuter f pdf1 emptyBackdrop)
pdfNew = CalPDF (name1 " " name2) (VU.fromList $ map fst pdfCombined) (VU.fromList $ map snd pdfCombined)
in normalizeCalPDF pdfNew
where
-- https://stackoverflow.com/questions/24424403/join-or-merge-function-in-haskell
fullOuter :: (Float -> Float -> Float) -> [(YearBCAD, Float)] -> [(YearBCAD, Float)] -> [(YearBCAD, Float)]
fullOuter _ xs [] = xs
fullOuter _ [] ys = ys
fullOuter f xss@(x:xs) yss@(y:ys)
| fst x == fst y = (fst x, f (snd x) (snd y)) : fullOuter f xs ys
| fst x < fst y = x : fullOuter f xs yss
| otherwise = y : fullOuter f xss ys
我想知道我是否可以重寫這段代碼,使它CalPDF成為的一個實體,Monoid并sumPDFs成為<>.
我無法克服并導致我發布的問題是問題,mempty應該是什么樣子。我已經在combinePDFs:中有這樣的東西emptyBackdrop。這在我的實作中是必需的,以填充或完成兩個輸入 PDF 之間的年份(如果它們不重疊)。
emptyBackdrop滿足 的一些要求mempty,但這取決于輸入的 PDF。理論上,真實的mempty將是 a CalPDF,它開始于時間的開始,結束于時間的結束,并將這些無限年中的每一年的概率歸于零。但這不能用未裝箱的向量來實作。
有沒有一種優雅的方式來制作CalPDF和實體化Monoid?讓它成為Semigroup我已經擁有的實體是否有用?
編輯:正如@leftaroundabout 所建議的,這里是上述設定的可重現的最小實作。
main :: IO ()
main = do
let myPDF1 = [(1,1), (2,1), (3,1)]
myPDF2 = [(2,1), (3,1), (4,1)]
putStrLn $ show $ sumPDFs myPDF1 myPDF2
type CalPDF = [(Int, Float)]
sumPDFs :: CalPDF -> CalPDF -> CalPDF
sumPDFs pdf1 pdf2 =
let startRange = minimum [fst $ head pdf1, fst $ head pdf2]
stopRange = maximum [fst $ last pdf1, fst $ last pdf2]
emptyBackdrop = zip [startRange..stopRange] (repeat (0.0 :: Float))
pdfCombined = fullOuter pdf2 (fullOuter pdf1 emptyBackdrop)
in pdfCombined
where
fullOuter :: [(Int, Float)] -> [(Int, Float)] -> [(Int, Float)]
fullOuter xs [] = xs
fullOuter [] ys = ys
fullOuter xss@(x@(year1,dens1):xs) yss@(y@(year2,dens2):ys)
| year1 == year2 = (year1, dens1 dens2) : fullOuter xs ys
| year1 < year2 = x : fullOuter xs yss
| otherwise = y : fullOuter xss ys
uj5u.com熱心網友回復:
考慮修改一下你的型別。
import Data.Map (Map)
import qualified Data.Map as M
data CalPDF = CalPDF
{ _calPDFid :: [String]
, _calPDFdens :: Map YearBCAD Float
}
現在的實體確實可以很短:
instance Semigroup CalPDF where
CalPDF id dens <> CalPDF id' dens' = CalPDF
(id <> id')
(M.unionWith ( ) dens dens')
instance Monoid CalPDF where
mempty = CalPDF mempty mempty
我用[String]單 分隔符代替String了兩個原因:它允許您 在名稱中使用而不會產生歧義,并且它使您更簡單一點,因為當一個或另一個引數為空時,<>您無需避免添加保持遵紀守法。Pretty-printers 仍然可以通過s 使用 eg來顯示這一點。 String intercalate " "
如果其中一種更適合您的需求,您可以以基本相同的方式使用HashMap或IntMap代替。Map
uj5u.com熱心網友回復:
AnySemigroup可以提升為Monoidwith Maybe。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/487886.html
上一篇:如何將串列中的串列轉換為字串
