是否有一个标准的Haskell函数,其类型为:(浮点a,RealFrac b)=> a-> b?

时间:2019-06-14 13:29:24

标签: haskell constraints typeclass coerce

我需要在一个值上调用floor(),该值仅被限制为Floating类,但是floor()需要RealFrac

我该怎么做?

我非常愿意在致电abs()之前先致电floor(),但是仅此一项似乎不足以解决我的约束冲突。 coerce抱怨说不能将这两种表示等同,这不足为奇。

看来我需要的是带有类型签名的函数:

(Floating a, RealFrac b) => a -> b

(在我看来)给此签名加abs()的某些增强版本是完全合理的。 las,对上述类型签名的Hoogle搜索使我一无所获。

有什么想法吗?

谢谢。
:)

2 个答案:

答案 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.00000000000000041.0。)

这使函数成为Floating的实例,但是代码可以概括为MonadApplicative s的所有类型。

您的假设函数需要具有类型

(Floating a, RealFrac b) => (e -> a) -> b
在这种情况下

。我们可以将ab设置为Double

(e -> Double) -> Double

您如何执行该操作?

还记得我说过的适用于所有应用程序吗?在上述情况下,我们可以将e ->替换为IO。然后,您最终得到的类型会变得更糟:

IO Double -> Double

问题是Floating可以是任何支持例如expsin操作(可能是纯符号操作,例如在语法树上),而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