5

我需要调用floor()一个值,该值仅限于 class Floating,但floor()需要RealFrac.

我怎样才能做到这一点?

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

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

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

abs()并且(对我来说)给出这个签名的一些增强版本似乎是完全合法的。唉,对上述类型签名的 Hoogle 搜索让我两手空空。

有什么想法吗?

谢谢。
:)

4

2 回答 2

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,但代码泛化到所有类型为Monads 或Applicatives 的类型。

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

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

在这种情况下。我们可以设置a和:bDouble

(e -> Double) -> Double

您如何实施该操作?

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

IO Double -> Double

问题是它Floating可以是任何支持例如expsin操作的东西(可以是纯粹的符号操作,例如在语法树上),而RealFrac必须是数字(或可转换为数字的东西)。

于 2019-06-14T14:27:49.243 回答
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
于 2019-06-14T19:35:57.733 回答