我有
类型列表PrimMonad m => m [Double]
为了计算平均值,我定义了以下函数:
sum' :: PrimMonad m => m [Double] -> m Double
sum' xs = (sum <$> xs)
len' :: PrimMonad m => m [Double] -> m Int
len' xs = length <$> xs
avg :: PrimMonad m => m [Double] -> m Double
avg xs = (sum' xs) / (fromIntegral $ len' xs)
但是,我遇到了avg
功能的问题。我收到以下错误:
Could not deduce (Fractional (m Double)) arising from a use of ‘/’ …
from the context (PrimMonad m)
bound by the type signature for
avg :: PrimMonad m => m [Double] -> m Double
at
In the expression: (sum' xs) / (fromIntegral $ len' xs)
In an equation for ‘avg’:
avg xs = (sum' xs) / (fromIntegral $ len' xs)
Could not deduce (Integral (m Int)) …
arising from a use of ‘fromIntegral’
from the context (PrimMonad m)
bound by the type signature for
avg :: PrimMonad m => m [Double] -> m Double
at
In the expression: fromIntegral
In the second argument of ‘(/)’, namely ‘(fromIntegral $ len' xs)’
In the expression: (sum' xs) / (fromIntegral $ len' xs)
Compilation failed.
我需要做什么才能定义这个简单的函数?
答案 0 :(得分:7)
问题在于/
想要Double
这样的数字,而不是像m Double
这样的monad中的数字。
天真的修复可能是
avg :: PrimMonad m => m [Double] -> m Double
avg xs = (/) <$> sum' xs <*> (fromIntegral <$> len' xs)
但这并不令人满意,因为它将运行monadic动作xs
两次。
我宁愿使用像
这样的东西avg :: PrimMonad m => m [Double] -> m Double
avg xs = (\ys -> sum ys / fromIntegral (length ys)) <$> xs
我也会避免将xs
命名为monadic动作,因为xs
可以很容易地用于普通列表。然而,这是个人偏好的问题。
更好的是,我首先定义一个非monadic平均值:
avg :: [Double] -> Double
avg xs = sum xs / fromIntegral (length xs)
avgM :: PrimMonad m => m [Double] -> m Double
avgM = fmap avg
由于avgM
太短,我可能会省略其定义,并在需要时直接调用fmap avg
。
答案 1 :(得分:1)
您面临的错误是双重的
fromIntegral
。现在到解决部分 - 最简单的方法是使用do
符号来规避整个问题
为了简单起见,我从getting-average-of-calculated-list-haskell
复制了一个avg函数{-# LANGUAGE BangPatterns #-}
import Data.List
avg :: (Integral a, Fractional b) => [a] -> b
avg xs = g $ foldl' c (0,0) xs
where
c (!a,!n) x = (a+x,n+1)
g (a,n) = fromIntegral a / fromIntegral n
monAvg :: (Monad m, Integral a, Fractional b) => m [a] -> m b
monAvg mxs = do xs <- mx
return $ avg xs
但这可能不是你想要它太专业化的东西。
我们需要的最小的事情只是Functor
- 所以fmap avg
也解决了你的问题。
如果你想保留你的功能 - 那么你需要<*>
Applicatives
来组合。
appAvg :: Applicative m => m [Double] -> m Double
appAvg xs = (/) <$> sum xs <*> (fromIntegral <$> len' xs)