You are on page 1of 17

Software Basado en Componentes

Trabajo Final
QuickCheck/Haskell

Lisibonny Beato

18 de febrero de 2010

QuickCheck/Haskell _____________________________________________________________________________

ndice general
Introduccin .................................................................................................................................. 3 El lenguaje de programacin Haskell ............................................................................................ 4 Caractersticas principales y aplicaciones ................................................................................. 4 Programa de ejemplo en Haskell .............................................................................................. 4 QuickCheck para Haskell ............................................................................................................... 6 Qu es QuickCheck? ................................................................................................................ 6 Propiedades............................................................................................................................... 6 Propiedades condicionales.................................................................................................... 7 Propiedades cuantificadas .................................................................................................... 7 Propiedades triviales ............................................................................................................. 8 Combinadores especiales.......................................................................................................... 8 Combinador Classify .............................................................................................................. 8 Combinador Collect ............................................................................................................... 9 Generadores de casos de prueba personalizados............................................................... 10 Caso de estudio: Gestin de nombres de archivo....................................................................... 11 Desarrollando un generador de casos de prueba ................................................................... 11 Definiendo la propiedad a probar ........................................................................................... 11 Recolectando informacin de los casos de prueba................................................................. 12 Desarrollando generadores de casos de prueba alternativos ................................................ 13 Modificando funciones y probando nuevamente las propiedades ........................................ 15 Conclusiones ............................................................................................................................... 16 Bibliografa .................................................................................................................................. 17

_____________________________________________________________________________ Software Basado en Componentes 2

QuickCheck/Haskell _____________________________________________________________________________

Introduccin
A da de hoy la realizacin de pruebas sobre cdigo fuente es la tcnica ms utilizada para asegurar la calidad del software. Se calcula que ms de la mitad del esfuerzo empleado en el desarrollo de software se dedica a labores de testing. Es por esta razn que la investigacin para la automatizacin de esta tarea sea un rea muy activa en los ltimos aos. Est demostrado que los lenguajes de programacin funcionales estn mejor preparados para dicha automatizacin debido a la naturaleza puramente funcional de los mismos y a la carencia de efectos colaterales. Una de estas iniciativas de automatizacin de pruebas en lenguajes funcionales es QuickCheck, una herramienta originalmente desarrollada para el lenguaje de programacin Haskell y que ha sido implementada en otros lenguajes, tanto funcionales como imperativos. El objetivo de este trabajo es, precisamente, mostrar el funcionamiento de QuickCheck para Haskell y obtener conclusiones acerca de su desempeo, ventajas e inconvenientes todo ello derivado del uso de la herramienta en un caso de estudio concreto. Para ello se ha dividido el trabajo en las siguientes partes: La primera parte describe brevemente el lenguaje de programacin funcional Haskell y se muestran sus principales caractersticas y aplicaciones como base para hablar de la herramienta de pruebas QuickCheck. La segunda parte introduce el concepto de propiedad en QuickCheck y especficamente se tratan las propiedades condicionales, cuantificadas y triviales. Tambin se tratan algunas herramientas de QuickCheck relativas a la clasificacin y recoleccin de casos de pruebas, as como tambin a la construccin de generadores de casos de pruebas personalizados. En la tercera parte se desarrolla un caso de estudio que implementa una librera en Haskell para el manejo de nombres de archivo. Se busca probar distintas propiedades sobre dichas funciones, utilizando QuickCheck, y verificar el desempeo de las mismas con generadores de casos de prueba personalizados.

_____________________________________________________________________________ Software Basado en Componentes 3

QuickCheck/Haskell _____________________________________________________________________________

El lenguaje de programacin Haskell


Haskell es un lenguaje de programacin puramente funcional sin efectos colaterales. Surgi en los aos 80's como respuesta a la necesidad de crear un lenguaje funcional que agrupara todas las caractersticas de los lenguajes que proliferaban en aquella poca.

Caractersticas principales y aplicaciones


Es un lenguaje de muy alto nivel en donde muchos detalles son manejados de forma automtica. Es expresivo y conciso, por lo que el programador es capaz de lograr mucho con una pequea cantidad de cdigo. Se puede decir que Haskell le da mayor prioridad al tiempo del programador que al tiempo de procesamiento del cdigo, lo que lo hace un lenguaje de desempeo bajo en trminos de procesamiento. Una de las caractersticas ms importantes de Haskell es su extraordinaria capacidad para manejar datos complejos y para combinar componentes. Solo realiza clculos cuando el resultado es requerido, en otras palabras, utiliza tcnicas de evaluacin perezosa. Es un lenguaje fuertemente tipado, por lo que impone severas restricciones acerca de cmo realizar operaciones entre valores de distintos tipos de dato. Haskell es usado en aplicaciones de muy diversa naturaleza: compiladores, analizadores, validadores de teoremas, aplicaciones para el procesamiento de datos estructurados y otras aplicaciones de dominios especficos.

Programa de ejemplo en Haskell


El siguiente es un programa escrito en Haskell que implementa una funcin para calcular el factorial de un nmero. Contiene, adems, una funcin principal que pregunta al usuario cul es el factorial del nmero 5 y le notifica si la respuesta que ha dado es correcta o no.

module Main where factorial n = if n == 0 then 1 else n * factorial (n - 1) main = do putStrLn "Cul es el factorial de 5?" x <- readLn if x == factorial 5 then putStrLn "Correcto!" else putStrLn "Incorrecto!"

_____________________________________________________________________________ Software Basado en Componentes 4

QuickCheck/Haskell _____________________________________________________________________________ A continuacin se muestra la ejecucin del programa utilizando WinHugs, que es la interfaz para Windows de Hugs, uno de los diversos intrpretes de Haskell.

_____________________________________________________________________________ Software Basado en Componentes 5

QuickCheck/Haskell _____________________________________________________________________________

QuickCheck para Haskell


Qu es QuickCheck?
QuickCheck es una herramienta para la verificacin y testing automtico de programas escritos en Haskell. Aunque actualmente existen implementaciones de QuickCheck para otros lenguajes, tales como Earlang y Java, QuickCheck fue originalmente desarrollado para Haskell. Fue creado en el ao 2000 por Koen Claessen y John Hughes de Chalmers University en Suecia. Con QuickCheck el programador especifica las propiedades que los programas deben satisfacer y QuickCheck prueba dichas propiedades en un gran nmero de casos de prueba generados aleatoriamente.

Propiedades
Las propiedades en QuickCheck son expresadas como funciones de Haskell. Como convencin son nombradas comenzando con el prefijo prop_. Dichas propiedades estn universalmente cuantificadas sobre sus parmetros, lo que significa que para cualquier valor que tomen dichos parmetros de entrada la propiedad se mantiene. Deben tener tipos monomrficos y, a menos que se utilicen combinadores especiales, siempre devuelven un valor booleano. A continuacin se muestra la estructura tpica de una propiedad en QuickCheck:
<nombre-de-la-propiedad> <variables> = <propiedad> where <supuestos> = <variable>::<tipo>

El siguiente ejemplo muestra una propiedad que intenta probar si aplicando la funcin reverse dos veces sobre una lista devuelve como resultado la lista original: prop_TransTrans xs = reverse (reverse xs) == xs where types = xs::[Int]

Para ejecutar las propiedades en QuickCheck se pueden utilizar una de estas dos sentencias: quickCheck <nombre-de-la-propiedad>: Ejecuta las pruebas sobre la propiedad y devuelve un resultado. verboseCheck <nombre-de-la-propiedad>: Ejecuta las pruebas y devuelve informacin de los casos de prueba utilizados, conjuntamente con el resultado de las pruebas.

Si probamos la propiedad anterior tendramos la siguiente salida:


Main> quickCheck prop_TransTrans OK, passed 100 tests.

_____________________________________________________________________________ Software Basado en Componentes 6

QuickCheck/Haskell _____________________________________________________________________________ En este caso QuickCheck nos indica que de los cien casos de prueba generados, todos han pasado satisfactoriamente las pruebas. Sin embargo, si intentsemos probar que la aplicacin de la funcin reverse sobre una lista es igual a la lista origina, como se especifica en la siguiente propiedad: prop_TransId xs = reverse (xs) == xs where types = xs::[Int] Obtendramos la siguiente salida en Hugs:
Main> quickCheck prop_TransId Falsifiable, after 5 tests: [-3,0,-2]

En este caso QuickCheck nos dice que el quinto caso de prueba no satisface la propiedad especificada. Nos muestra tambin los datos que se utilizaron en dicho caso de prueba. En las siguientes sub-secciones veremos tres tipos especiales de propiedades que se pueden utilizar en QuickCheck: Condicionales, cuantificadas y triviales.

Propiedades condicionales

Este tipo especial de propiedad permite especificar una condicin que los casos generados por QuickCheck deben cumplir en orden de ser utilizados como casos de prueba. Si la condicin no se cumple para un caso de prueba, este es descartado y se prueba con el siguiente. La sintaxis de una propiedad de este tipo es como se especifica a continuacin: <condicin> ==> <propiedad> El siguiente ejemplo muestra una propiedad que busca probar si el mximo entre dos nmeros es siempre el segundo nmero cuando el primer nmero es menor o igual a este: prop_MaxMi x y = x <= y ==> max x y == y where types = (x::Int, y::Int)

Propiedades cuantificadas

Con este tipo de propiedad se puede especificar un generador de casos de prueba personalizado en vez de utilizar el generador por defecto que provee QuickCheck para el tipo de dato en cuestin. Con este tipo de propiedad es posible controlar la distribucin de los casos de prueba y, a diferencia del tipo anterior, no filtra datos por lo que el total de casos de prueba est disponible para ser utilizado. La sintaxis de una propiedad de este tipo es como se muestra a continuacin: forAll <generador> $ \<patrn> -> <propiedad> _____________________________________________________________________________ Software Basado en Componentes 7

QuickCheck/Haskell _____________________________________________________________________________ El siguiente ejemplo utiliza un generador de nmeros mayores que cien para ser utilizado en las pruebas de la propiedad del mximo entre dos nmeros descrita en la seccin anterior: prop_MaxMi x y = forAll mayorquecien $ \(x, y) -> max x y == y where types = (x::Int, y::Int)

Propiedades triviales

Con esta propiedad se pueden obtener estadsticas acerca de los casos triviales o casos en donde la condicin especificada ha sido verdadera. Esta propiedad devuelve la proporcin de estos casos con respecto al total de casos generados. La sintaxis de una propiedad de este tipo es como se muestra a continuacin: <condicin> `trivial` <propiedad> El siguiente ejemplo muestra la cantidad de casos triviales dentro del conjunto de casos de prueba de una propiedad que busca que las listas generadas estn ordenadas en orden de ser usadas como casos de prueba: ordenadas xs = and (zipWith (<=) xs (drop 1 xs)) insertar x xs = takeWhile (<x) xs++[x]++dropWhile (<x) xs prop_Insertar x xs = ordenadas xs ==> null xs `trivial` ordenadas (insertar x xs) where types = x::Int

Si ejecutamos esta propiedad en Hugs, obtenemos lo siguiente:


Main> quickCheck prop_Insertar OK, passed 100 tests (42% trivial).

Combinadores especiales
Combinador Classify

Con este combinador se puede ver la distribucin de los casos de prueba de acuerdo a un criterio especfico. La sintaxis del combinador Classify es como sigue: classify <condicin> <cadena>$ <propiedad>

_____________________________________________________________________________ Software Basado en Componentes 8

QuickCheck/Haskell _____________________________________________________________________________ El siguiente ejemplo muestra el porcentaje de casos de prueba en el que las listas utilizadas como casos de prueba eran de tamao menor que uno y mayor que 2: ordenadas xs = and (zipWith (<=) xs (drop 1 xs)) insertar x xs = takeWhile (<x) xs++[x]++dropWhile (<x) xs prop_Insertar x xs = ordenadas xs ==> classify (length xs <= 1) "Listas de tamao < 1" $ classify (length xs > 2) "Listas de tamao > 2" $ ordenadas (insertar x xs) where types = x::Int Si ejecutamos esta propiedad en Hugs, tendramos una salida como la siguiente:
Main> quickCheck prop_Insertar OK, passed 100 tests. 81% Listas de tamao < 1. 7% Listas de tamao > 2.

Combinador Collect

La recoleccin de valores utilizando este combinador es similar al anterior, con la diferencia de que en este caso se recolectan todos los datos generados y la distribucin de los mismos se reporta al final de las pruebas. La sintaxis de este combinador es como sigue: collect <expresin>$ <propiedad> El siguiente ejemplo muestra el porcentaje de casos de prueba de acuerdo a todos los tamaos de las listas generadas: ordenadas xs = and (zipWith (<=) xs (drop 1 xs)) insertar x xs = takeWhile (<x) xs++[x]++dropWhile (<x) xs prop_Insertar x xs = ordenadas xs ==> collect (length xs)$ ordenadas (insertar x xs) where types = x::Int

Una salida en Hugs podra ser como sigue:

Main> quickCheck prop_Insertar OK, passed 100 tests. 39% 0. 33% 1. 20% 2. 7% 3. 1% 4.

_____________________________________________________________________________ Software Basado en Componentes 9

QuickCheck/Haskell _____________________________________________________________________________ Generadores de casos de prueba personalizados

QuickCheck provee generadores de casos de prueba para la mayora de tipos de datos nativos de Haskell: Int, Char, Float, List. Sin embargo, en algunas ocasiones puede ser necesario utilizar generadores de casos de prueba personalizados en vez de los generadores por defecto de QuickCheck. A continuacin se definen combinadores tiles para la construccin de generadores de casos personalizados: choose: Este generador hace una seleccin aleatoria entre un intervalo especificado. El siguiente ejemplo muestra un generador que utiliza choose para especificar que se generarn dos nmeros enteros, el primero con valores en un rango de 7 a 10 y el segundo en un rango de 9 a 14: randomDouble :: Gen (Integer, Integer) randomDouble = do x1 <- choose (7,10) x2 <- choose (9,14) return (x1, x2)

oneof: Este generador selecciona de una lista de valores especificados. El siguiente ejemplo muestra un generador que utiliza oneof para especificar que se generarn dos nmeros enteros, el primero devolver un valor que ser 1 o 2 y el segundo 2 o 3: randomDouble :: Gen (Integer, Integer) randomDouble = do x1 <- oneof [return 1, return 2] x2 <- oneof [return 2, return 3] return (x1, x2)

frequency: Este generador al igual que el anterior selecciona de una lista de valores especificados, pero asigna un nmero que es usado para indicar la probabilidad con la que se utilizar cada valor en los casos de prueba que se generen. El siguiente ejemplo devuelve el primer entero devuelve los mismos valores que en el caso anterior, pero con frequency se le especifica que el valor 1 se generar el doble de veces el valor 2: randomDouble :: Gen (Integer, Integer) randomDouble = do x1 <- frequency[(2,return 1), (1,return 2)] x2 <- oneof [return 2, return 3] return (x1, x2)

Para tipos de datos definidos por el usuario, dichos generadores deben ser construidos utilizando la clase Arbitrary de Haskell. _____________________________________________________________________________ Software Basado en Componentes 10

QuickCheck/Haskell _____________________________________________________________________________

Caso de estudio: Gestin de nombres de archivo


Para probar las caractersticas de la herramienta de pruebas QuickCheck vamos a crear un programa en Haskell que trabaja con nombres de archivos. Especficamente implementa dos funciones: dividirNA, que divide un nombre de archivo en nombre y extensin, y unirNA que convierte un nombre y una extensin en un nombre de archivo completo. import Control.Monad ( liftM ) import Data.List ( intersperse ) import Test.QuickCheck dividirNA:: String -> (String, String) dividirNA na = let na' = span (/= '.') . reverse $ na in case (length (fst na') == length na) of True -> (na, "") False -> (reverse . drop 1 $ snd na', ('.':) . reverse . fst $ na') unirNA:: (String, String) -> String unirNA (nombre, ext) = nombre ++ ext

Desarrollando un generador de casos de prueba


Una propiedad que podramos querer probar es si cuando se aplica la funcin unirNA al resultado que devuelve la funcin dividirNA sobre un nombre de archivo obtenemos el nombre de archivo completo original. Antes de definir las propiedades en QuickCheck vamos a definir un nuevo tipo de datos y a instanciar la clase Arbitrary para generar los nombres de archivo. newtype Nombrearchivo = NA { unNA :: String } deriving Show instance Arbitrary Nombrearchivo where arbitrary = do nombre <- elements ["archivo1", "archivo2", "archivo3"] ext <- listOf $ elements ['a'..'z'] return (NA (nombre ++ "." ++ ext))

Definiendo la propiedad a probar


Ahora procedemos a especificar la propiedad en QuickCheck: prop_nombresarchivo_correctos:: Nombrearchivo -> Property prop_nombresarchivo_correctos naStr = property $ unirNA (dividirNA na) == na where na = unNA naStr _____________________________________________________________________________ Software Basado en Componentes 11

QuickCheck/Haskell _____________________________________________________________________________ Si ejecutamos la propiedad en Hugs, obtenemos lo siguiente:


Main> quickCheck prop_nombresarchivo_correctos OK, passed 100 tests.

Recolectando informacin de los casos de prueba


El resultado obtenido nos dice que la propiedad ha superado con xito todas las pruebas, sin embargo vamos a echar un vistazo a dichos casos de prueba para asegurarnos que se estn generando nombres de archivo correctos. Para esto podramos utilizar el combinador Collect de QuickCheck. prop_nombresarchivo_correctos_collect:: Nombrearchivo -> Property prop_nombresarchivo_correctos_collect naStr = collect na $ unirNA (dividirNA na) == na where na = unNA naStr

A continuacin se muestra un subconjunto de la salida obtenida al ejecutar esta propiedad:


Main> quickCheck prop_nombresarchivo_correctos_collect OK, passed 100 tests. 1% "archivo1.hydjhd". 1% "archivo1.a". 1% "archivo1.mogmlz". 1% "archivo1.josr". 1% "archivo1. tidqpnitvrseakbkppkjmqtlutkyhnsirlsmkrnsmxsvwhzhwfut".

La cantidad de informacin recolectada puede ser grande y difcil de entender, por lo que podramos utilizar el combinador Classify para obtener una idea ms concreta de cmo se estn generando los nombres de archivo para los casos de prueba. Vamos a clasificarlos utilizando como criterio el tamao de la extensin de los nombres de archivo, especficamente visualizaremos los archivos sin extensin, los archivos de menos de cinco caracteres de extensin (a los cuales llamaremos normales) y los que tienen ms de cinco (a los cuales llamaremos largos). prop_nombresarchivo_correctos_classify :: Nombrearchivo -> Property prop_nombresarchivo_correctos_classify naStr = classify (length ext == 0) "Sin extensin" $ classify (length ext > 0 && length ext < 5) "Extensin normal" $ classify (length ext >= 5) "Extensin larga" $ unirNA (dividirNA na) == na where na = unNA naStr (nombre,ext) = dividirNA na _____________________________________________________________________________ Software Basado en Componentes 12

QuickCheck/Haskell _____________________________________________________________________________ Ejecutando dicha propiedad en Hugs tenemos la siguiente salida:


Main> quickCheck prop_nombresarchivo_correctos_classify OK, passed 100 tests. 70% Extensin larga. 22% Extensin normal. 8% Sin extensin.

Si revisamos detenidamente la informacin obtenida, podemos ver que no estamos trabajando con el conjunto completo de nombres de archivo vlidos, como por ejemplo LEEME, .emacs, archivo. o documento.txt.old.

Desarrollando generadores de casos de prueba alternativos


Para corregir este problema podramos desarrollar un generador de casos de prueba alternativo sin instanciar la clase Arbitrary y utilizaremos oneof. nombresarchivo :: Gen String nombresarchivo = do nombre <- opt identifier punto <- opt (return ".") ext <- opt identifier exts <- listOf identifier oneof [ return $ nombre ++ punto ++ ext , return $ nombre ++ "." ++ (concat . intersperse "." $ exts)]

En este caso el generador ser capaz de sortear las dificultades que presentaba el generador anterior. Con una propiedad cuantificada utilizaremos este nuevo generador:

prop_nombresarchivo_correctos_nueva:: Property prop_nombresarchivo_correctos_nueva = forAll nombresarchivo $ \na -> unirNA (dividirNA na) == na

Con este nuevo generador podemos ver que los nombres de archivo son ahora ms variados: 41R8x. 1LAi.k .K3 .wu.mi1kqh8.Y7PKH6.p86.O

_____________________________________________________________________________ Software Basado en Componentes 13

QuickCheck/Haskell _____________________________________________________________________________ Y ejecutando la propiedad podemos ver que todos los casos pasan la prueba:
Main> quickCheck prop_nombresarchivo_correctos_nueva OK, passed 100 tests.

Sin embargo algunos de estos nombres de archivo puede que no posean una extensin de archivo real: LEEME y .emacs, por ejemplo, por lo que si aplicamos la funcin dividirNA sobre ellos el resultado debera ser el mismo nombre de archivo. Para verificar si esta funcin es correcta en los casos anteriormente mencionados, vamos a desarrollar un generador de casos de prueba solo para archivos sin extensin y vamos a probar una nueva propiedad con este generador para verificar si la funcin dividirNA se comporta correctamente: NombrearchivosSinExt :: Gen String NombrearchivosSinExt = do nombre <- identifier punto <- opt (return ".") return ( punto ++ nombre ) prop_archivo_iguala_nombrearchivo :: Property prop_archivo_iguala_nombrearchivo = forAll NombrearchivosSinExt $ \na -> let (nombre,ext) = dividirNA na in nombre == na

Ejecutando esta propiedad podemos detectar un problema en nuestra funcin dividirNA:


Main> quickCheck prop_archivo_iguala_nombrearchivo Falsifiable, after 8 tests: ".5tcd"

Ejecutando la funcin dividirNA en Haskell con este contra-ejemplo vemos que identifica al nombre de archivo como la extensin del archivo y no como el nombre:
Main> dividirNA ".5tcd" ("",".5tcd")

_____________________________________________________________________________ Software Basado en Componentes 14

QuickCheck/Haskell _____________________________________________________________________________

Modificando funciones y probando nuevamente las propiedades


Para solucionar el problema con la funcin dividirNA en el caso de que los nombres de archivo no tengan extensin, vamos a modificarla de modo que contemple estos casos. dividirNA_nueva :: String -> (String, String) dividirNA_nueva na = let na' = span (/= '.') . reverse $ na in case (length (fst na') == length na of True -> (na, "") False | length (fst na') == length na - 1 -> (na, "") | otherwise -> (reverse . drop 1 $ snd na' , ('.':) . reverse . fst $ na')

Con esta nueva funcin de divisin de nombres de archivos vamos a reescribir las propiedades prop_archivo_iguala_nombrearchivo y prop_nombresarchivo_correctos_nueva: prop_archivo_iguala_nombrearchivo_nueva :: Property prop_archivo_iguala_nombrearchivo_nueva = forAll NombrearchivosSinExt $ \na -> let (nombre,ext) = dividirNA_nueva na in nombre == na prop_nombresarchivo_correctos_nueva2:: Property prop_nombresarchivo_correctos_nueva2 = forAll nombresarchivo $ \na -> unirNA (dividirNA_nueva na) == na Ejecutando las propiedades en Hugs podemos ver que ahora todos los casos de prueba pasan las pruebas de ambas propiedades exitosamente:
Main> quickCheck prop_nombresarchivo_correctos_nueva2 OK, passed 100 tests. Main> quickCheck prop_archivo_iguala_nombrearchivo_nueva OK, passed 100 tests.

_____________________________________________________________________________ Software Basado en Componentes 15

QuickCheck/Haskell _____________________________________________________________________________

Conclusiones
Despus de trabajar con QuickCheck en el caso de estudio podra concluir que la visin de alto nivel de esta herramienta va muy acorde con la naturaleza funcional de Haskell, dado que para probar los programas no es necesario disear pruebas individuales y planear cuidadosamente los datos que dichas pruebas utilizarn, cosa que si es importante cuando se trata con lenguajes imperativos. Por esta misma razn una de las ventajas de trabajar con QuickCheck es la reduccin de los tiempos de validacin de los programas, que se consigue gracias a que los casos de prueba son generados de forma automtica. QuickCheck, adems, verifica los programas intentando encontrar ejemplos que no cumplen las especificaciones dadas (contra-ejemplos). A pesar de que esto no es una garanta de consistencia, ayuda a reducir dicho riesgo dentro de nuestro cdigo. Otro punto fuerte de esta herramienta es que ayuda en las tareas de documentacin de los programas, debido a que otros programadores que tengan que ver o trabajar con nuestro cdigo pueden saber exactamente qu propiedades hemos probado en nuestros programas, la naturaleza de los casos de prueba que hemos utilizado y los resultados que hemos obtenido en dichas pruebas. Sin embargo, hay que ser cuidadosos con la distribucin de los casos de prueba: si los datos de prueba que se utilizan no estn bien distribuidos las conclusiones que se obtienen pueden no ser correctas.

_____________________________________________________________________________ Software Basado en Componentes 16

QuickCheck/Haskell _____________________________________________________________________________

Bibliografa
1. Sitio Web de Haskell, http://www.haskell.org/ 2. Sitio Web de QuickCheck, http://www.cs.chalmers.se/~rjmh/QuickCheck/ 3. Artculo de QuickCheck en Wikipedia, http://en.wikipedia.org/wiki/QuickCheck

_____________________________________________________________________________ Software Basado en Componentes 17

You might also like