Ir al contenido principal

Posición del primer falso en un vector


Definir la función

posicion :: Array Int Bool -> Maybe Int

tal que (posicion v) es la menor posición del vector de booleanos v cuyo valor es falso y es Nothing si todos los valores son verdaderos. Por ejemplo,

posicion (listArray (0,4) [True,True,False,True,False]) == Just 2
posicion (listArray (0,4) [i <= 2 | i <- [0..4]])       == Just 3
posicion (listArray (0,4) [i <= 7 | i <- [0..4]])       == Nothing

Soluciones

import Data.Array (Array, (!), assocs, bounds, elems, indices, listArray)
import Data.List (find, findIndex)
import Data.Maybe (listToMaybe)
import qualified Data.Vector as V (findIndex, fromList)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck

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

posicion1 :: Array Int Bool -> Maybe Int
posicion1 v | p > n     = Nothing
            | otherwise = Just p
  where p = (length . takeWhile id . elems) v
        (_,n) = bounds v

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

posicion2 :: Array Int Bool -> Maybe Int
posicion2 v | null xs   = Nothing
            | otherwise = Just (head xs)
  where xs = posiciones2 v

-- (posiciones v) es la de las posiciones donde el elemento de v es
-- falso. Por ejemplo,
--    λ> posiciones (listArray (0,4) [True,True,False,True,False])
--    [2,4]
--    λ> posiciones (listArray (0,4) [i <= 2 | i <- [0..4]])
--    [3,4]
--    λ> posiciones (listArray (0,4) [i <= 7 | i <- [0..4]])
--    []
posiciones2 :: Array Int Bool -> [Int]
posiciones2 v = [i | i <- indices v, not (v!i)]

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

posicion3 :: Array Int Bool -> Maybe Int
posicion3 v =
  case posiciones3 v of
    []    -> Nothing
    (x:_) -> Just x

posiciones3 :: Array Int Bool -> [Int]
posiciones3 v = [i | (i, x) <- assocs v, not x]

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

posicion4 :: Array Int Bool -> Maybe Int
posicion4 v = listToMaybe [i | (i, x) <- assocs v, not x]

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

posicion5 :: Array Int Bool -> Maybe Int
posicion5 v = find (\i -> not (v ! i)) (indices v)

-- 6ª solución
-- ===========

posicion6 :: Array Int Bool -> Maybe Int
posicion6 v =
  foldr (\i n -> if not (v ! i) then Just i else n) Nothing (indices v)

-- 7ª solución
-- ===========

posicion7 :: Array Int Bool -> Maybe Int
posicion7 v = buscar a
  where
    (a, b) = bounds v
    buscar i
      | i > b     = Nothing
      | not (v!i) = Just i
      | otherwise = buscar (i + 1)

-- 8ª solución
-- ===========

posicion8 :: Array Int Bool -> Maybe Int
posicion8 v =
  case findIndex not (elems v) of
    Nothing -> Nothing
    Just i  -> Just (i + fst (bounds v))

-- 9ª solución
-- ===========

posicion9 :: Array Int Bool -> Maybe Int
posicion9 v = V.findIndex not v'
  where v' = V.fromList (elems v)

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

verifica :: IO ()
verifica = hspec spec

specG :: (Array Int Bool -> Maybe Int) -> Spec
specG posicion = do
  it "e1" $
    posicion (listArray (0,4) [True,True,False,True,False]) `shouldBe` Just 2
  it "e2" $
    posicion (listArray (0,4) [i <= 2 | i <- [0..4]])       `shouldBe` Just 3
  it "e3" $
    posicion (listArray (0,4) [i <= 7 | i <- [0..4]])       `shouldBe` Nothing

spec :: Spec
spec = do
  describe "def. 1" $ specG posicion1
  describe "def. 2" $ specG posicion2
  describe "def. 3" $ specG posicion3
  describe "def. 4" $ specG posicion4
  describe "def. 5" $ specG posicion5
  describe "def. 6" $ specG posicion6
  describe "def. 7" $ specG posicion7
  describe "def. 8" $ specG posicion8
  describe "def. 9" $ specG posicion9

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

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

-- Generador de vectores cuyos elementos son booleanos. Por ejemplo,
--    λ> generate genVector
--    array (0,3) [(0,False),(1,True),(2,True),(3,False)]
genVector :: Gen (Array Int Bool)
genVector = do
    n <- choose (0, 100)
    vals <- vectorOf (n + 1) arbitrary
    return (listArray (0, n) vals)

-- La propiedad es
prop_equivalencia :: Property
prop_equivalencia =
  forAll genVector $ \v ->
  all (== posicion1 v)
      [posicion2 v,
       posicion3 v,
       posicion4 v,
       posicion5 v,
       posicion6 v,
       posicion7 v,
       posicion8 v,
       posicion9 v]

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

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

-- La comparación es
--    λ> :set +s
--    λ> posicion1 (listArray (0,2*10^6) [i < 2*10^6 | i <- [0..2*10^6]])
--    Just 2000000
--    (2.24 secs, 2,528,623,872 bytes)
--    λ> posicion2 (listArray (0,2*10^6) [i < 2*10^6 | i <- [0..2*10^6]])
--    Just 2000000
--    (2.87 secs, 2,704,625,544 bytes)
--    λ> posicion3 (listArray (0,2*10^6) [i < 2*10^6 | i <- [0..2*10^6]])
--    Just 2000000
--    (2.93 secs, 2,704,623,824 bytes)
--    λ> posicion4 (listArray (0,2*10^6) [i < 2*10^6 | i <- [0..2*10^6]])
--    Just 2000000
--    (2.70 secs, 2,704,623,784 bytes)
--    λ> posicion5 (listArray (0,2*10^6) [i < 2*10^6 | i <- [0..2*10^6]])
--    Just 2000000
--    (2.71 secs, 2,784,623,808 bytes)
--    λ> posicion6 (listArray (0,2*10^6) [i < 2*10^6 | i <- [0..2*10^6]])
--    Just 2000000
--    (3.04 secs, 2,768,623,744 bytes)
--    λ> posicion7 (listArray (0,2*10^6) [i < 2*10^6 | i <- [0..2*10^6]])
--    Just 2000000
--    (3.24 secs, 2,896,623,792 bytes)
--    λ> posicion8 (listArray (0,2*10^6) [i < 2*10^6 | i <- [0..2*10^6]])
--    Just 2000000
--    (2.26 secs, 2,416,623,784 bytes)
--    λ> posicion9 (listArray (0,2*10^6) [i < 2*10^6 | i <- [0..2*10^6]])
--    Just 2000000
--    (2.25 secs, 2,450,211,376 bytes)