Entero positivo de la cadena
Definir la función
enteroPositivo :: String -> Maybe Int
tal que (enteroPositivo cs)
es justo el contenido de la cadena cs
, si dicho contenido es un entero positivo, y Nothing
en caso contrario. Por ejemplo,
enteroPositivo "235" == Just 235 enteroPositivo "-235" == Nothing enteroPositivo "23.5" == Nothing enteroPositivo "235 " == Nothing enteroPositivo "cinco" == Nothing enteroPositivo "" == Nothing
Soluciones
import Data.Maybe (listToMaybe) import Numeric (readDec) import Data.Char (isDigit) import Test.Hspec (Spec, describe, hspec, it, shouldBe) import Test.QuickCheck -- 1ª solución -- =========== enteroPositivo1 :: String -> Maybe Integer enteroPositivo1 "" = Nothing enteroPositivo1 cs | todosDigitos1 cs = Just (read cs) | otherwise = Nothing -- (todosDigitos cs) se verifica si todos los elementos de cs son -- dígitos. Por ejemplo, -- todosDigitos "235" == True -- todosDigitos "-235" == False -- todosDigitos "23.5" == False -- todosDigitos "235 " == False -- todosDigitos "cinco" == False todosDigitos1 :: String -> Bool todosDigitos1 cs = and [esDigito1 c | c <- cs] -- (esDigito c) se verifica si el carácter c es un dígito. Por ejemplo, -- esDigito '5' == True -- esDigito 'a' == False esDigito1 :: Char -> Bool esDigito1 c = c `elem` "0123456789" -- 2ª solución -- =========== enteroPositivo2 :: String -> Maybe Integer enteroPositivo2 "" = Nothing enteroPositivo2 cs | todosDigitos2 cs = Just (read cs) | otherwise = Nothing todosDigitos2 :: String -> Bool todosDigitos2 cs = and [esDigito2 c | c <- cs] esDigito2 :: Char -> Bool esDigito2 c = c `elem` ['0'..'9'] -- 3ª solución -- =========== enteroPositivo3 :: String -> Maybe Integer enteroPositivo3 "" = Nothing enteroPositivo3 cs | todosDigitos3 cs = Just (read cs) | otherwise = Nothing todosDigitos3 :: String -> Bool todosDigitos3 cs = and [esDigito3 c | c <- cs] esDigito3 :: Char -> Bool esDigito3 = (`elem` ['0'..'9']) -- 4ª solución -- =========== enteroPositivo4 :: String -> Maybe Integer enteroPositivo4 "" = Nothing enteroPositivo4 cs | todosDigitos4 cs = Just (read cs) | otherwise = Nothing todosDigitos4 :: String -> Bool todosDigitos4 cs = and [esDigito4 c | c <- cs] esDigito4 :: Char -> Bool esDigito4 c = '0' <= c && c <= '9' -- 5ª solución -- =========== enteroPositivo5 :: String -> Maybe Integer enteroPositivo5 "" = Nothing enteroPositivo5 cs | todosDigitos5 cs = Just (read cs) | otherwise = Nothing todosDigitos5 :: String -> Bool todosDigitos5 cs = and [isDigit c | c <- cs] -- 6ª solución -- =========== enteroPositivo6 :: String -> Maybe Integer enteroPositivo6 "" = Nothing enteroPositivo6 cs | todosDigitos6 cs = Just (read cs) | otherwise = Nothing todosDigitos6 :: String -> Bool todosDigitos6 [] = True todosDigitos6 (c:cs) = isDigit c && todosDigitos6 cs -- 7ª solución -- =========== enteroPositivo7 :: String -> Maybe Integer enteroPositivo7 "" = Nothing enteroPositivo7 cs | todosDigitos7 cs = Just (read cs) | otherwise = Nothing todosDigitos7 :: String -> Bool todosDigitos7 = foldr ((&&) . isDigit) True -- 8ª solución -- =========== enteroPositivo8 :: String -> Maybe Integer enteroPositivo8 "" = Nothing enteroPositivo8 cs | todosDigitos8 cs = Just (read cs) | otherwise = Nothing todosDigitos8 :: String -> Bool todosDigitos8 = all isDigit -- 9ª solución -- =========== enteroPositivo9 :: String -> Maybe Integer enteroPositivo9 cs | null xs = Nothing | otherwise = Just (head xs) where xs = [x | (x,y) <- readDec cs, null y] -- Nota. En la solución anterior se ha usado la función readDec de la -- librería Numeric. El valor de (readDec cs) es la lista de los pares -- (x,y) tales que x es el entero positivo al principio de cs e y es el -- resto. Por ejemplo, -- readDec "235" == [(235,"")] -- readDec "-235" == [] -- readDec "23.5" == [(23,".5")] -- readDec "235 " == [(235," ")] -- readDec "cinco" == [] -- readDec "" == [] -- 10ª solución -- =========== enteroPositivo10 :: String -> Maybe Integer enteroPositivo10 = fmap fst . listToMaybe . filter (null . snd) . readDec -- Nota. En la solución anterior se ha usado la función listToMaybe -- (de la librería Data.Maybe) tal que (listToMaybe xs) es Nothing si xs -- es la lista vacía o (Just x) donde x es el primer elemento de xs. Por -- ejemplo, -- listToMaybe [] == Nothing -- listToMaybe [3,2,5] == Just 3 -- y la función fmap tal que (fmap f x) le aplica la función f a los -- elementos de x. Por ejemplo, -- fmap (+2) (Just 3) == Just 5 -- fmap (+2) Nothing == Nothing -- fmap (+2) [3,4,6] == [5,6,8] -- fmap (+2) [] == [] -- Nota. Ejemplos de cálculo con enteroPositivo3 -- enteroPositivo10 "325" -- = (fmap fst . listToMaybe . filter (null . snd) . readDec) "325" -- = (fmap fst . listToMaybe . filter (null . snd)) [(325,"")] -- = (fmap fst . listToMaybe) [(325,"")] -- = fmap fst (Just (325,"")) -- = Just 325 -- -- enteroPositivo10 "32.5" -- = (fmap fst . listToMaybe . filter (null . snd) . readDec) "32.5" -- = (fmap fst . listToMaybe . filter (null . snd)) [(32,".5")] -- = (fmap fst . listToMaybe ) [] -- = fmap fst Nothing -- = Nothing -- Verificación -- ============ verifica :: IO () verifica = hspec spec specG :: (String -> Maybe Integer) -> Spec specG enteroPositivo = do it "e1" $ enteroPositivo "235" `shouldBe` Just 235 it "e2" $ enteroPositivo "-235" `shouldBe` Nothing it "e3" $ enteroPositivo "23.5" `shouldBe` Nothing it "e4" $ enteroPositivo "235 " `shouldBe` Nothing it "e5" $ enteroPositivo "cinco" `shouldBe` Nothing it "e6" $ enteroPositivo "" `shouldBe` Nothing spec :: Spec spec = do describe "def. 1" $ specG enteroPositivo1 describe "def. 2" $ specG enteroPositivo2 describe "def. 3" $ specG enteroPositivo3 describe "def. 4" $ specG enteroPositivo4 describe "def. 5" $ specG enteroPositivo5 describe "def. 6" $ specG enteroPositivo6 describe "def. 7" $ specG enteroPositivo7 describe "def. 8" $ specG enteroPositivo8 describe "def. 9" $ specG enteroPositivo9 describe "def. 10" $ specG enteroPositivo10 -- La verificación es -- λ> verifica -- 60 examples, 0 failures -- Equivalencia de las definiciones -- ================================ -- Generador de cadenas. Por ejemplo. -- λ> generate cadenaArbitraria -- "69883777219" -- λ> generate cadenaArbitraria -- "iyodnfsw2m78mhu651bvtt7" cadenaArbitraria :: Gen String cadenaArbitraria = frequency [ (1, listOf (elements ['0'..'9'])), -- 50% solo dígitos (1, listOf (elements (['0'..'9'] ++ ['a'..'z']))) -- 50% alfanuméricos ] -- La propiedad es prop_enteroPositivo :: Property prop_enteroPositivo = forAll cadenaArbitraria $ \s -> all (== enteroPositivo1 s) [enteroPositivo2 s, enteroPositivo3 s, enteroPositivo4 s, enteroPositivo5 s, enteroPositivo6 s, enteroPositivo7 s, enteroPositivo8 s, enteroPositivo9 s, enteroPositivo10 s] -- La verificación es -- λ> quickCheck prop_enteroPositivo -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> ej = replicate (2*10^6) '5' -- λ> (length . show) <$> enteroPositivo1 ej -- Just 2000000 -- (2.19 secs, 2,187,674,312 bytes) -- λ> (length . show) <$> enteroPositivo2 ej -- Just 2000000 -- (1.97 secs, 2,235,673,400 bytes) -- λ> (length . show) <$> enteroPositivo3 ej -- Just 2000000 -- (1.52 secs, 1,243,673,336 bytes) -- λ> (length . show) <$> enteroPositivo4 ej -- Just 2000000 -- (1.87 secs, 1,467,673,352 bytes) -- λ> (length . show) <$> enteroPositivo5 ej -- Just 2000000 -- (1.45 secs, 1,243,673,400 bytes) -- λ> (length . show) <$> enteroPositivo6 ej -- Just 2000000 -- (1.49 secs, 1,099,673,184 bytes) -- λ> (length . show) <$> enteroPositivo7 ej -- Just 2000000 -- (1.04 secs, 1,035,673,176 bytes) -- λ> (length . show) <$> enteroPositivo8 ej -- Just 2000000 -- (0.97 secs, 1,019,673,232 bytes) -- λ> (length . show) <$> enteroPositivo9 ej -- Just Interrupted. -- λ> (length . show) <$> enteroPositivo10 ej -- Just Interrupted.