我需要在一个值上调用floor()
,该值仅被限制为Floating
类,但是floor()
需要RealFrac
。
我该怎么做?
我非常愿意在致电abs()
之前先致电floor()
,但是仅此一项似乎不足以解决我的约束冲突。
coerce
抱怨说不能将这两种表示等同,这不足为奇。
看来我需要的是带有类型签名的函数:
(Floating a, RealFrac b) => a -> b
(在我看来)给此签名加abs()
的某些增强版本是完全合理的。
las,对上述类型签名的Hoogle搜索使我一无所获。
有什么想法吗?
谢谢。
:)
答案 0 :(得分:6)
请考虑以下Floating
实例:
import Control.Applicative
instance (Num a) => Num (e -> a) where
(+) = liftA2 (+)
(*) = liftA2 (*)
(-) = liftA2 (-)
abs = fmap abs
signum = fmap signum
negate = fmap negate
fromInteger = pure . fromInteger
instance (Fractional a) => Fractional (e -> a) where
fromRational = pure . fromRational
recip = fmap recip
(/) = liftA2 (/)
instance (Floating a) => Floating (e -> a) where
pi = pure pi
exp = fmap exp
log = fmap log
sin = fmap sin
cos = fmap cos
asin = fmap asin
acos = fmap acos
atan = fmap atan
sinh = fmap sinh
cosh = fmap cosh
asinh = fmap asinh
acosh = fmap acosh
atanh = fmap atanh
演示:
main :: IO ()
main = do
print (sqrt sqrt 81)
let f = sin^2 + cos^2
print (f 42)
(这将输出3.0000000000000004
和1.0
。)
这使函数成为Floating
的实例,但是代码可以概括为Monad
或Applicative
s的所有类型。
您的假设函数需要具有类型
(Floating a, RealFrac b) => (e -> a) -> b
在这种情况下。我们可以将a
和b
设置为Double
:
(e -> Double) -> Double
您如何执行该操作?
还记得我说过的适用于所有应用程序吗?在上述情况下,我们可以将e ->
替换为IO
。然后,您最终得到的类型会变得更糟:
IO Double -> Double
问题是Floating
可以是任何支持例如exp
或sin
操作(可能是纯符号操作,例如在语法树上),而RealFrac
必须是数字(或可转换为数字的东西)。
答案 1 :(得分:0)
您能负担Ord
约束吗?
module FBound (ffloor, fceil) where
import Data.List (foldl')
-- |
-- >>> ffloor . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
-- >>> ffloor . (+0.001) . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
-- >>> ffloor . (+0.999) . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
ffloor :: (Ord a, Floating a, Integral b) => a -> b
ffloor a | a >= 0 = ffloor' a
| otherwise = negate $ fceil' (-a)
-- |
-- >>> fceil. fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
-- >>> fceil . (-0.001) . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
-- >>> fceil . (-0.999) . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
fceil :: (Ord a, Floating a, Integral b) => a -> b
fceil a | a >= 0 = fceil' a
| otherwise = negate $ ffloor' (-a)
-- given a >= 0, ffloor' a <= a < ffloor' a + 1
ffloor' :: (Ord a, Floating a, Integral b) => a -> b
ffloor' = foldl' roundDown 0 . reverse . takeWhile (>=1) . iterate (/2)
-- given a >= 0, fceil' a - 1 < a <= fceil' a
fceil' :: (Ord a, Floating a, Integral b) => a -> b
fceil' a = ffloor' (a/2) `roundUp` a
-- given 2*i <= a < 2*i + 2, roundDown i a <= a < roundDown i a + 1
roundDown :: (Ord a, Num a, Integral b) => b -> a -> b
roundDown i a | a < fromIntegral (2*i + 1) = 2*i
| otherwise = 2*i + 1
-- given 2*i <= a < 2*i + 2, roundUp i a - 1 < a <= roundUp i a
roundUp :: (Ord a, Num a, Integral b) => b -> a -> b
roundUp i a | a == fromIntegral (2*i) = 2*i
| a <= fromIntegral (2*i + 1) = 2*i + 1
| otherwise = 2*i + 2