cabal update ; cabal install hunit quickcheck
, o stack update ; stack install hunit quickcheck
.Maybe
y listasAcabamos de aprender sobre mónadas y comprensiones de listas. Los ejercicios que siguien son una oportunidad para ver que hemos aprendido. Son relativamente simples.
Escribí una función que detecta si una String tiene cierto formato. El formato requerido es el siguiente:
n
el valor de ese dígito. La string contiene luego n
caracteres 'a'
.'a'
, o la string se termina, o la secuencia se repite, empezando con un dígito (puede ser distinto).Ejemplos:
Buenas strings | Malas strings |
---|---|
3aaa2aa | 3aaa2a |
9aaaaaaaaa | 10aaaaaaaaaa |
0 | 1 |
001a | 100a |
2aa2aa | 2bb2bb |
Tu función debe usar la mónada Maybe
. Debería parecerse a esto:
stringFitsFormat :: String -> Bool
= isJust . go
stringFitsFormat where go :: String -> Maybe String
-- go evalua a Just "" si es exitosa, sino a Nothing
...
Ayuda: usá readMaybe :: Read a => String -> Maybe a
de Text.Read
y stripPrefix :: Eq a => [a] -> [a] -> Maybe [a]
de Data.List
.
Usá una comprensión de lista para producir la lista de todos los números entre 1 y 100 (incluso) que son divisibles por 5 pero no por 7.
specialNumbers :: [Int]
QuickCheck te da el poder increible de hacer tests aleatorios. Todo lo que hay que hacer es escribir propiedades generales para testear, y dejar que QuickCheck haga la generación de casos de tests. En nuestro caso, vamos a hacer tests para asegurarnos que las instancias de Ring
cumplan con las propiedades de los anillos.
Si querés verificar los anillos, vas a necesitar instancias Arbitrary
para Mod5
y Mat2x2
para que QuickCheck pueda crear valores arbitrarios para esos tipos. Averiguá la documentación de la clase Arbitrary
. Vas a ver la función arbitrary :: Gen a
, esta es la que debés implementar.
Fijate en la parte de la documentación que se llama “Random generation”, en particular “generator combinators”. Estos combinadores van a ser útiles. Lo más importante, es que Gen
es instancia de la clase Monad
, o sea, ¡es una mónada! Entonces podés empezar con arbitrary = do ...
y seguir así.
Escribí instancias Arbitrary
para Mod5
y Mat2x2
.
Ayuda: Integer
ya tiene instancias para Arbitrary
y Random
.
http://hackage.haskell.org/package/QuickCheck-2.7.6/docs/Test-QuickCheck.html
Podrás probar tus instancias en GHCi evaluando las expresiones siguientes:
sample (arbitrary :: Gen Mod5)
sample (arbitrary :: Gen Mat2x2)
Implementá el método shrink
para Mat2x2
leyendo la documentación de shrink
.
Leé la página de Wikipedia sobre anillos.
Un poco más abajo en la página, vas a ver las 9 propiedades que un anillo debe tener. Codificá esas propiedades de forma que se puedan usar con QuickCheck. Para ser compatible con nuestro test automático, nombralos desde prop_1
hasta prop_9
.
Asegurate que tus propiedades anden con quickCheck
ejecutando, por ejemplo, quickCheck prop_1
en GHCi. Si querés testear otra cosa que Integer
, tenés que agregar una anotación de tipo, como por ejemplo quickCheck (prop_1 :: Mat2x2 -> Mat2x2 -> Bool)
.
Escribí una propiedad para gobernarlos a todos, llamada prop_ring
, que averigua si todas las propiedades del anillo se cumplen. Hay varias formas de hacer eso, algunas más limpias que otras. Usá combinadores útiles, como conjoin
y .&&.
. Cuidado que los tipos de esos combinadores son un poco desafiantes.
(También es posible escribir prop_ring
sin usar esos combinadores, pero no es tan lindo ni divertido.)
Uno de los anillos definidos en Ring.hs
está roto. Utiliza tus tests para encontrar cuál, y escribí tu descubrimiento como comentario en tu código.
Hay instancias de Parsable
en Ring.hs
. Son más difíciles de comprobar usando tests basados sobre propiedades, entonces vamos a usar tests unitarios con HUnit.
Escribí un test
parserTests :: Test
= TestList [ ... ] parserTests
que incluya por lo menos dos tests para cada instancia de Parsable
. Asegurate de dar nombres descriptivos a cada test usando (~:)
. Podés ayudarte con la función parseAll
, que parsea suponiendo que toda la string se consume - es decir, comprueba si no queda nada despues de parsear.
Ayuda: para comprobar la instancia Integer Parsable
, probablemente vas a necesitar una anotación de tipo para aclarar que querés Integer
y no, por ejemplo, Int
. Por ejemplo, puede ser que necesites escribir algo como:
"3" ~?= Just (3 :: Integer) parseAll