Ir al contenido principal

Suma si todos los valores son justos


Definir la función

sumaSiTodosJustos :: (Num a, Eq a) => [Maybe a] -> Maybe a

tal que (sumaSiTodosJustos xs) es justo la suma de todos los elementos de xs si todos son justos (es decir, si Nothing no pertenece a xs) y Nothing en caso contrario. Por ejemplo,

sumaSiTodosJustos [Just 2, Just 5]           == Just 7
sumaSiTodosJustos [Just 2, Just 5, Nothing]  == Nothing

Soluciones

import Data.Maybe (catMaybes, fromJust, isJust)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)

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

sumaSiTodosJustos1 :: (Num a, Eq a) => [Maybe a] -> Maybe a
sumaSiTodosJustos1 xs
    | todosJustos xs = Just (sum [x | (Just x) <- xs])
    | otherwise      = Nothing

-- (todosJustos xs) se verifica si todos los elementos de xs si todos
-- son justos (es decir, si Nothing no pertenece a xs) y Nothing en caso
-- contrario. Por ejemplo,
--    todosJustos [Just 2, Just 5]           == True
--    todosJustos [Just 2, Just 5, Nothing]  == False
todosJustos :: Eq a => [Maybe a] -> Bool
todosJustos = notElem Nothing

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

sumaSiTodosJustos2 :: (Num a, Eq a) => [Maybe a] -> Maybe a
sumaSiTodosJustos2 xs
  | todosJustos2 xs = Just (sum [x | (Just x) <- xs])
  | otherwise       = Nothing

todosJustos2 :: Eq a => [Maybe a] -> Bool
todosJustos2 = all isJust

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

sumaSiTodosJustos3 :: (Num a, Eq a) => [Maybe a] -> Maybe a
sumaSiTodosJustos3 xs
  | todosJustos xs = Just (sum [fromJust x | x <- xs])
  | otherwise      = Nothing

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

sumaSiTodosJustos4 :: (Num a, Eq a) => [Maybe a] -> Maybe a
sumaSiTodosJustos4 xs
  | todosJustos xs = Just (sum (catMaybes xs))
  | otherwise      = Nothing

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

sumaSiTodosJustos5 :: (Num a, Eq a) => [Maybe a] -> Maybe a
sumaSiTodosJustos5 xs = suma (sequence xs)
  where suma Nothing   = Nothing
        suma (Just ys) = Just (sum ys)

-- Nota. En la solución anterior se usa la función
--    sequence :: Monad m => [m a] -> m [a]
-- tal que (sequence xs) es la mónada obtenida evaluando cada una de las
-- de xs de izquierda a derecha. Por ejemplo,
--    sequence [Just 2, Just 5]   ==  Just [2,5]
--    sequence [Just 2, Nothing]  ==  Nothing

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

sumaSiTodosJustos6 :: (Num a, Eq a) => [Maybe a] -> Maybe a
sumaSiTodosJustos6 xs = fmap sum (sequence xs)

-- Nota. En la solución anterior se usa la función
--    fmap :: (a -> b) -> f a -> f b
-- tal que (fmap f x) aplica la función f al functor x. Por ejemplo,
--    fmap (+2) (Just 3)  ==  Just 5
--    fmap (+2) Nothing   ==  Nothing

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

sumaSiTodosJustos7 :: (Num a, Eq a) => [Maybe a] -> Maybe a
sumaSiTodosJustos7 = fmap sum . sequence

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

verifica :: IO ()
verifica = hspec spec

specG :: ([Maybe Int] -> Maybe Int) -> Spec
specG sumaSiTodosJustos = do
  it "e1" $
    sumaSiTodosJustos [Just 2, Just 5] `shouldBe` Just 7
  it "e2" $
    sumaSiTodosJustos [Just 2, Just 5, Nothing] `shouldBe` Nothing

spec :: Spec
spec = do
  describe "def. 1" $ specG sumaSiTodosJustos1
  describe "def. 2" $ specG sumaSiTodosJustos2
  describe "def. 3" $ specG sumaSiTodosJustos3
  describe "def. 4" $ specG sumaSiTodosJustos4
  describe "def. 5" $ specG sumaSiTodosJustos5
  describe "def. 6" $ specG sumaSiTodosJustos6
  describe "def. 7" $ specG sumaSiTodosJustos7

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