composing floor and sqrt in haskell

后端 未结 2 1553
借酒劲吻你
借酒劲吻你 2021-01-06 02:41

I\'m just learning haskell (on my own, for fun) and I\'ve come up against a wall.

My Question:

How can I define a function

flrt = (floor . s         


        
相关标签:
2条回答
  • 2021-01-06 02:52

    The problem is that you're trying to use Integer as the input. Haskell is strongly typed, which means there are no implicit coercions or conversions of any kind. Look at signatures of functions you're trying to compose:

    sqrt  :: Floating a => a -> a
    floor :: (RealFrac a, Integral b) => a -> b
    

    And at the signature of your function inferred by GHC:

    > :t floor . sqrt
    floor . sqrt :: (RealFrac b, Integral c, Floating b) => b -> c
    

    So, to have a function that goes from Integer (which doesn't have a Floating instance) to Integer, you have to first convert your argument to Floating, which can be done by using fromIntegral:

    > :t floor . sqrt . fromIntegral
    floor . sqrt . fromIntegral :: (Integral a, Integral c) => a -> c
    
    0 讨论(0)
  • 2021-01-06 02:56

    As copumpkin remarked, it might actually be a bad idea to convert to floating point here, because this comes with loss of precision and therefore might, even with rounding, yield incorrect results for sufficiently large integer inputs.

    I assume all numbers you're dealing with will at least be small enough that there is some floating-point representation for them, e.g. all are < 10300. But, for instance

    Prelude> round(sqrt.fromInteger$10^60 :: Double) ^ 2
    1000000000000000039769249677312000395398304974095154031886336
    Prelude>  {-   and not   -}     10^60    {-  == (10^30)^2 == (sqrt$10^60) ^ 2  -}
    1000000000000000000000000000000000000000000000000000000000000
    

    Which is way off, in terms of absolute difference. Still it's certainly a rather good approximation relative to the numbers themselves, so you can use it as a quickly determined starting point for an algorithm to find the exact result. You can implement Newton/Raphson (in this case AKA Heron) with Integers:

    flrt :: Integer -> Integer  -- flrt x ≈ √x,  with  flrt x^2 ≤ x < flrt(x+1)^2
    flrt x = approx (round . (sqrt::Double->Double) . fromInteger $ x)
       where approx r
                | ctrl <= x, (r+1)^2 > x  = r
                | otherwise               = approx $ r - diff
              where ctrl = r^2
                    diff = (ctrl - x) // (2*r)    -- ∂/∂x x² = 2x
    
             a//b = a`div`b + if (a>0)==(b>0) then 1 else 0   -- always away from 0
    

    This now works as desired:

    *IntegerSqrt> (flrt $ 10^60) ^ 2
    1000000000000000000000000000000000000000000000000000000000000
    

    The division always away from 0 in the Newton-Raphson correction is here necessary to prevent getting stuck in an infinite recursion.

    0 讨论(0)
提交回复
热议问题