Ir al contenido principal

La función suelo


La función suelo asigna a cada número real el número entero más próximo por defecto; es decir, el mayor número entero igual o menor que ese número real. Por ejemplo, al -2.4 le asigna el -3 y al 1.7 el 1.

Haskell tiene una implementación de la función suelo llamada floor. El objetivo de este ejercicio es redefinir dicha función; es decir, definir la función

suelo :: Float -> Integer

tal que (suelo x) es el suelo de x. Por ejemplo,

suelo (-2.7)  ==  -3
suelo (-2.4)  ==  -3
suelo (-2)    ==  -2
suelo 0       ==   0
suelo 2       ==   2
suelo 2.4     ==   2
suelo 2.7     ==   2

Comprobar con QuickCheck que las funciones suelo y floor son equivalentes.


Soluciones

import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck

-- 1ª solución
-- ===========

suelo1 :: Float -> Integer
suelo1 x | x < 0     = head [m | m <- [-1,-2..], fromIntegral m <= x]
         | otherwise = head [m | m <- [1..], x < fromIntegral m] - 1

-- 2ª solución
-- ===========

suelo2 :: Float -> Integer
suelo2 x | x < 0     = until (`menorI` x) (subtract 1) (-1)
         | otherwise = until (x `menor`) (+1) 1 - 1
  where menorI m y = fromIntegral m <= y
        menor  y m = y < fromIntegral m

-- 3ª solución
-- ===========

suelo3 :: Float -> Integer
suelo3 x | x < 0     = until ((<=x) . fromIntegral) (subtract 1) (-1)
         | otherwise = until ((x<)  . fromIntegral) (+1) 1 - 1

-- 4ª solución
-- ===========

suelo4 :: Float -> Integer
suelo4 x = if r < 0 then n-1 else n
  where (n,r) = properFraction x

-- 5ª solución
-- ===========

suelo5 :: Float -> Integer
suelo5 x = fst (until unitario (mejora x) (acota x))
    where inferior y     = until (`menorI` y) (*2) (-1)
          superior y     = until (y `menor`) (*2) 1
          menorI m y     = fromIntegral m <= y
          menor  y m     = y < fromIntegral m
          acota y        = (inferior y, superior y)
          mejora y (m,n) = if p `menorI` y then (p,n) else (m,p)
                           where p =(m+n) `div` 2
          unitario (m,n) = (m+1 == n)

-- Verificación
-- ============

verifica :: IO ()
verifica = hspec spec

specG :: (Float -> Integer) -> Spec
specG suelo = do
  it "e1" $
    suelo (-2.4)  `shouldBe`  -3
  it "e2" $
    suelo (-2.7)  `shouldBe`  -3
  it "e3" $
    suelo (-2)    `shouldBe`  -2
  it "e4" $
    suelo   2.4   `shouldBe`  2
  it "e5" $
    suelo   2.7   `shouldBe`  2
  it "e6" $
    suelo   2     `shouldBe`  2

spec :: Spec
spec = do
  describe "def. 1" $ specG suelo1
  describe "def. 2" $ specG suelo2
  describe "def. 3" $ specG suelo3
  describe "def. 4" $ specG suelo4
  describe "def. 5" $ specG suelo5

-- La verificación es
--    λ> verifica
--    36 examples, 0 failures

-- Comprobación de equivalencia
-- ============================

-- La propiedad es
prop_equivalencia :: Float -> Bool
prop_equivalencia x =
  all (== suelo1 x)
      [suelo2 x,
       suelo3 x,
       suelo4 x,
       suelo5 x]

-- La comprobación es
--    λ> quickCheck prop_equivalencia
--    +++ OK, passed 100 tests.

-- Comparación de eficiencia
-- =========================

-- La comparación es
--    λ> :set +s
--    λ> suelo1 (10^7)
--    10000000
--    (2.93 secs, 2,080,602,576 bytes)
--    λ> suelo2 (10^7)
--    10000000
--    (2.95 secs, 1,520,602,592 bytes)
--    λ> suelo3 (10^7)
--    10000000
--    (1.99 secs, 1,280,602,696 bytes)
--    λ> suelo4 (10^7)
--    10000000
--    (0.01 secs, 602,472 bytes)
--    λ> suelo5 (10^7)
--    10000000
--    (0.00 secs, 628,720 bytes)