El tipo abstracto de datos de los conjuntos
1. El tipo abstracto de datos de los conjuntos
Un conjunto es una estructura de datos, caracterizada por ser una colección de elementos en la que no importe ni el orden ni la repetición de elementos.
Las operaciones que definen al tipo abstracto de datos (TAD) de los conjuntos (cuyos elementos son del tipo a) son las siguientes:
1 2 3 4 5 6 |
vacio :: Conj a inserta :: Ord a => a -> Conj a -> Conj a menor :: Ord a => Conj a -> a elimina :: Ord a => a -> Conj a -> Conj a pertenece :: Ord a => a -> Conj a -> Bool esVacio :: Conj a -> Bool |
tales que
- vacio es el conjunto vacío.
- (inserta x c) es el conjunto obtenido añadiendo el elemento x al
conjunto c. - (menor c) es el menor elemento del conjunto c.
- (elimina x c) es el conjunto obtenido eliminando el elemento x del conjunto c.
- (pertenece x c) se verifica si x pertenece al conjunto c.
- (esVacio c) se verifica si c es el conjunto vacío.
Las operaciones tienen que verificar las siguientes propiedades:
- inserta x (inserta x c) == inserta x c
- inserta x (inserta y c) == inserta y (inserta x c)
- not (pertenece x vacio)
- pertenece y (inserta x c) == (x==y) || pertenece y c
- elimina x vacio == vacio
- Si x == y, entonces elimina x (inserta y c) == elimina x c
- Si x /= y, entonces elimina x (inserta y c) == inserta y (elimina x c)
- esVacio vacio
- not (esVacio (inserta x c))
2. Los conjuntos en Haskell
2.1. El tipo abstracto de datos de los conjuntos en Haskell
El TAD de los conjuntos se encuentra en el módulo Conjunto.hs cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
module TAD.Conjunto (Conj, vacio, -- Conj a inserta, -- Ord a => a -> Conj a -> Conj a menor, -- Ord a => Conj a -> a elimina, -- Ord a => a -> Conj a -> Conj a pertenece, -- Ord a => a -> Conj a -> Bool esVacio -- Conj a -> Bool ) where import TAD.ConjuntoConListasNoOrdenadasConDuplicados -- import TAD.ConjuntoConListasNoOrdenadasSinDuplicados -- import TAD.ConjuntoConListasOrdenadasSinDuplicados -- import TAD.ConjuntosConLibreria |
Para usar el TAD hay que usar una implementación concreta. En principio, consideraremos las siguientes:
- mediante listas no ordenadas con duplicados,
- mediante listas no ordenadas sin duplicados,
- mediante listas ordenadas sin duplicados y
- mediante la librería de conjuntos.
2.2. Implementación de los conjuntos mediante listas no ordenadas con duplicados
La implementación se encuentra en el módulo ConjuntoConListasNoOrdenadasConDuplicados.hs cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
module TAD.ConjuntoConListasNoOrdenadasConDuplicados (Conj, vacio, -- Conj a inserta, -- Ord a => a -> Conj a -> Conj a menor, -- Ord a => Conj a -> a elimina, -- Ord a => a -> Conj a -> Conj a pertenece, -- Ord a => a -> Conj a -> Bool esVacio -- Conj a -> Bool ) where import Data.List (intercalate, nub, sort) import Test.QuickCheck -- Conjuntos como listas no ordenadas con repeticiones: newtype Conj a = Cj [a] -- (escribeConjunto c) es la cadena correspondiente al conjunto c. Por -- ejemplo, -- λ> escribeConjunto (Cj []) -- "{}" -- λ> escribeConjunto (Cj [5]) -- "{5}" -- λ> escribeConjunto (Cj [2, 5]) -- "{2, 5}" -- λ> escribeConjunto (Cj [5, 2, 5]) -- "{2, 5}" escribeConjunto :: (Show a, Ord a) => Conj a -> String escribeConjunto (Cj xs) = "{" ++ intercalate ", " (map show (sort (nub xs))) ++ "}" -- Procedimiento de escritura de conjuntos. instance (Show a, Ord a) => Show (Conj a) where show = escribeConjunto -- Nota: Aunque el conjunto no está ordenado y tenga repeticiones, al -- escribirlo se hará sin repeticiones y ordenando sus elementos. -- vacio es el conjunto vacío. Por ejemplo, -- λ> vacio -- {} vacio :: Conj a vacio = Cj [] -- (inserta x c) es el conjunto obtenido añadiendo el elemento x al -- conjunto c. Por ejemplo, -- λ> inserta 5 vacio -- {5} -- λ> inserta 2 (inserta 5 vacio) -- {2, 5} -- λ> inserta 5 (inserta 2 vacio) -- {2, 5} inserta :: Eq a => a -> Conj a -> Conj a inserta x (Cj ys) = Cj (x:ys) -- (menor c) es el menor elemento del conjunto c. Por ejemplo, -- λ> menor (inserta 5 (inserta 2 vacio)) -- 2 menor :: Ord a => Conj a -> a menor (Cj []) = error "conjunto vacío" menor (Cj xs) = minimum xs -- (elimina x c) es el conjunto obtenido eliminando el elemento x -- del conjunto c. Por ejemplo, -- λ> elimina 2 (inserta 5 (inserta 2 vacio)) -- {5} elimina :: Eq a => a -> Conj a -> Conj a elimina x (Cj ys) = Cj (filter (/= x) ys) -- (esVacio c) se verifica si c es el conjunto vacío. Por ejemplo, -- λ> esVacio (inserta 5 (inserta 2 vacio)) -- False -- λ> esVacio vacio -- True esVacio :: Conj a -> Bool esVacio (Cj xs) = null xs -- (pertenece x c) se verifica si x pertenece al conjunto c. Por ejemplo, -- λ> pertenece 2 (inserta 5 (inserta 2 vacio)) -- True -- λ> pertenece 4 (inserta 5 (inserta 2 vacio)) -- False pertenece :: Eq a => a -> Conj a -> Bool pertenece x (Cj xs) = x `elem` xs -- (subconjunto c1 c2) se verifica si c1 es un subconjunto de c2. Por -- ejemplo, -- subconjunto (Cj [1,3,2,1]) (Cj [3,1,3,2]) == True -- subconjunto (Cj [1,3,4,1]) (Cj [3,1,3,2]) == False subconjunto :: Ord a => Conj a -> Conj a -> Bool subconjunto (Cj xs) (Cj ys) = sublista xs ys where sublista [] _ = True sublista (z:zs) vs = elem z vs && sublista zs vs -- (igualConjunto c1 c2) se verifica si los conjuntos c1 y c2 son -- iguales. Por ejemplo, -- igualConjunto (Cj [1,3,2,1]) (Cj [3,1,3,2]) == True -- igualConjunto (Cj [1,3,4,1]) (Cj [3,1,3,2]) == False igualConjunto :: Ord a => Conj a -> Conj a -> Bool igualConjunto c c' = subconjunto c c' && subconjunto c' c --- Los conjuntos son comparables por igualdad. instance Ord a => Eq (Conj a) where (==) = igualConjunto -- Generador de conjuntos -- -- ====================== -- genConjunto es un generador de conjuntos. Por ejemplo, -- λ> sample (genConjunto :: Gen (Conj Int)) -- {} -- {1} -- {0, 2, 3} -- {-6, 5} -- {2, 5} -- {-9, -6, 4, 8} -- {0, 1} -- {-13, -11, -5, -2, -1, 0, 4, 6, 7, 8, 9, 14} -- {-7, -5, -2, -1, 1, 2, 10, 13, 15} -- {-18, -17, -16, -10, -9, 0, 1, 3, 4, 13, 16} -- {-20, -15, -7, -1, 2, 8, 10, 15, 20} genConjunto :: (Arbitrary a, Ord a) => Gen (Conj a) genConjunto = do xs <- listOf arbitrary return (foldr inserta vacio xs) -- Los conjuntos son concreciones de los arbitrarios. instance (Arbitrary a, Ord a) => Arbitrary (Conj a) where arbitrary = genConjunto -- Propiedades de los conjuntos -- -- ============================ prop_conjuntos :: Int -> Int -> Conj Int -> Bool prop_conjuntos x y c = inserta x (inserta x c) == inserta x c && inserta x (inserta y c) == inserta y (inserta x c) && not (pertenece x vacio) && pertenece y (inserta x c) == (x == y) || pertenece y c && elimina x vacio == vacio && elimina x (inserta y c) == (if x == y then elimina x c else inserta y (elimina x c)) && esVacio (vacio :: Conj Int) && not (esVacio (inserta x c)) -- Comprobación -- λ> quickCheck prop_conjuntos -- +++ OK, passed 100 tests. |
2.3. Implementación de los conjuntos mediante listas no ordenadas sin duplicados
La implementación se encuentra en el módulo ConjuntoConListasNoOrdenadasSinDuplicados.hs cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
module TAD.ConjuntoConListasNoOrdenadasSinDuplicados (Conj, vacio, -- Conj a inserta, -- Ord a => a -> Conj a -> Conj a menor, -- Ord a => Conj a -> a elimina, -- Ord a => a -> Conj a -> Conj a pertenece, -- Ord a => a -> Conj a -> Bool esVacio -- Conj a -> Bool ) where import Data.List (intercalate, sort) import Test.QuickCheck -- Los conjuntos como listas no ordenadas sin repeticiones. newtype Conj a = Cj [a] -- (escribeConjunto c) es la cadena correspondiente al conjunto c. Por -- ejemplo, -- λ> escribeConjunto (Cj []) -- "{}" -- λ> escribeConjunto (Cj [5]) -- "{5}" -- λ> escribeConjunto (Cj [2, 5]) -- "{2, 5}" -- λ> escribeConjunto (Cj [5, 2]) -- "{2, 5}" escribeConjunto :: (Show a, Ord a) => Conj a -> String escribeConjunto (Cj xs) = "{" ++ intercalate ", " (map show (sort xs)) ++ "}" -- Procedimiento de escritura de conjuntos. instance (Show a, Ord a) => Show (Conj a) where show = escribeConjunto -- vacio es el conjunto vacío. Por ejemplo, -- λ> vacio -- {} vacio :: Conj a vacio = Cj [] -- (inserta x c) es el conjunto obtenido añadiendo el elemento x al -- conjunto c. Por ejemplo, -- λ> inserta 5 vacio -- {5} -- λ> inserta 2 (inserta 5 vacio) -- {2, 5} -- λ> inserta 5 (inserta 2 vacio) -- {2, 5} inserta :: Eq a => a -> Conj a -> Conj a inserta x s@(Cj xs) | pertenece x s = s | otherwise = Cj (x:xs) -- (menor c) es el menor elemento del conjunto c. Por ejemplo, -- λ> menor (inserta 5 (inserta 2 vacio)) -- 2 menor :: Ord a => Conj a -> a menor (Cj []) = error "conjunto vacío" menor (Cj xs) = minimum xs -- (elimina x c) es el conjunto obtenido eliminando el elemento x -- del conjunto c. Por ejemplo, -- λ> elimina 2 (inserta 5 (inserta 2 vacio)) -- {5} elimina :: Eq a => a -> Conj a -> Conj a elimina x (Cj s) = Cj [y | y <- s, y /= x] -- (esVacio c) se verifica si c es el conjunto vacío. Por ejemplo, -- λ> esVacio (inserta 5 (inserta 2 vacio)) -- False -- λ> esVacio vacio -- True esVacio :: Conj a -> Bool esVacio (Cj xs) = null xs -- (pertenece x c) se verifica si x pertenece al conjunto c. Por ejemplo, -- λ> pertenece 2 (inserta 5 (inserta 2 vacio)) -- True -- λ> pertenece 4 (inserta 5 (inserta 2 vacio)) -- False pertenece :: Eq a => a -> Conj a -> Bool pertenece x (Cj xs) = x `elem` xs -- (subconjunto c1 c2) se verifica si c1 es un subconjunto de c2. Por -- ejemplo, -- subconjunto (Cj [1,3,2]) (Cj [3,1,2]) == True -- subconjunto (Cj [1,3,4,1]) (Cj [1,3,2]) == False subconjunto :: Ord a => Conj a -> Conj a -> Bool subconjunto (Cj xs) (Cj ys) = sublista xs ys where sublista [] _ = True sublista (z:zs) vs = elem z vs && sublista zs vs -- (igualConjunto c1 c2) se verifica si los conjuntos c1 y c2 son -- iguales. Por ejemplo, -- igualConjunto (Cj [3,2,1]) (Cj [1,3,2]) == True -- igualConjunto (Cj [1,3,4]) (Cj [1,3,2]) == False igualConjunto :: Ord a => Conj a -> Conj a -> Bool igualConjunto c c' = subconjunto c c' && subconjunto c' c --- Los conjuntos son comparables por igualdad. instance Ord a => Eq (Conj a) where (==) = igualConjunto -- Generador de conjuntos -- -- ====================== -- genConjunto es un generador de conjuntos. Por ejemplo, -- λ> sample (genConjunto :: Gen (Conj Int)) -- {} -- {-1, 0} -- {-4, 1, 2} -- {-3, 0, 2, 3, 4} -- {-7} -- {-10, -7, -5, -2, -1, 2, 5, 8} -- {5, 7, 8, 10} -- {-9, -6, -3, 8} -- {-8, -6, -5, -1, 7, 9, 14} -- {-18, -15, -14, -13, -3, -2, 1, 2, 4, 8, 12, 17} -- {-17, -16, -13, -12, -11, -9, -6, -3, 0, 1, 3, 5, 6, 7, 16, 18} genConjunto :: (Arbitrary a, Ord a) => Gen (Conj a) genConjunto = do xs <- listOf arbitrary return (foldr inserta vacio xs) -- Los conjuntos son concreciones de los arbitrarios. instance (Arbitrary a, Ord a) => Arbitrary (Conj a) where arbitrary = genConjunto -- Propiedades de los conjuntos -- -- ============================ prop_conjuntos :: Int -> Int -> Conj Int -> Bool prop_conjuntos x y c = inserta x (inserta x c) == inserta x c && inserta x (inserta y c) == inserta y (inserta x c) && not (pertenece x vacio) && pertenece y (inserta x c) == (x == y) || pertenece y c && elimina x vacio == vacio && elimina x (inserta y c) == (if x == y then elimina x c else inserta y (elimina x c)) && esVacio (vacio :: Conj Int) && not (esVacio (inserta x c)) -- Comprobación -- λ> quickCheck prop_conjuntos -- +++ OK, passed 100 tests. |
2.4. Implementación de los conjuntos mediante listas ordenadas sin repeticiones
La implementación se encuentra en el módulo ConjuntoConListasOrdenadasSinDuplicados.hs cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
module TAD.ConjuntoConListasOrdenadasSinDuplicados (Conj, vacio, -- Conj a inserta, -- Ord a => a -> Conj a -> Conj a menor, -- Ord a => Conj a -> a elimina, -- Ord a => a -> Conj a -> Conj a pertenece, -- Ord a => a -> Conj a -> Bool esVacio -- Conj a -> Bool ) where import Data.List (intercalate) import Test.QuickCheck -- Los conjuntos como listas ordenadas sin repeticiones. newtype Conj a = Cj [a] deriving Eq -- (escribeConjunto c) es la cadena correspondiente al conjunto c. Por -- ejemplo, -- λ> escribeConjunto (Cj []) -- "{}" -- λ> escribeConjunto (Cj [5]) -- "{5}" -- λ> escribeConjunto (Cj [2, 5]) -- "{2, 5}" -- λ> escribeConjunto (Cj [5, 2]) -- "{2, 5}" escribeConjunto :: Show a => Conj a -> String escribeConjunto (Cj xs) = "{" ++ intercalate ", " (map show xs) ++ "}" -- Procedimiento de escritura de conjuntos. instance Show a => Show (Conj a) where show = escribeConjunto -- vacio es el conjunto vacío. Por ejemplo, -- λ> vacio -- {} vacio :: Conj a vacio = Cj [] -- (inserta x c) es el conjunto obtenido añadiendo el elemento x al -- conjunto c. Por ejemplo, -- λ> inserta 5 vacio -- {5} -- λ> inserta 2 (inserta 5 vacio) -- {2, 5} -- λ> inserta 5 (inserta 2 vacio) -- {2, 5} inserta :: Ord a => a -> Conj a -> Conj a inserta x (Cj s) = Cj (agrega x s) where agrega z [] = [z] agrega z s'@(y:ys) | z > y = y : agrega z ys | z < y = z : s' | otherwise = s' -- (menor c) es el menor elemento del conjunto c. Por ejemplo, -- λ> menor (inserta 5 (inserta 2 vacio)) -- 2 menor :: Ord a => Conj a -> a menor (Cj []) = error "conjunto vacío" menor (Cj (x:_)) = x -- (elimina x c) es el conjunto obtenido eliminando el elemento x -- del conjunto c. Por ejemplo, -- λ> elimina 2 (inserta 5 (inserta 2 vacio)) -- {5} elimina :: Ord a => a -> Conj a -> Conj a elimina x (Cj s) = Cj (elimina' x s) where elimina' _ [] = [] elimina' z s'@(y:ys) | z > y = y : elimina' z ys | z < y = s' | otherwise = ys -- (esVacio c) se verifica si c es el conjunto vacío. Por ejemplo, -- λ> esVacio (inserta 5 (inserta 2 vacio)) -- False -- λ> esVacio vacio -- True esVacio :: Conj a -> Bool esVacio (Cj xs) = null xs -- (pertenece x c) se verifica si x pertenece al conjunto c. Por ejemplo, -- λ> pertenece 2 (inserta 5 (inserta 2 vacio)) -- True -- λ> pertenece 4 (inserta 5 (inserta 2 vacio)) -- False pertenece :: Ord a => a -> Conj a -> Bool pertenece x (Cj s) = x `elem` takeWhile (<= x) s -- Generador de conjuntos -- -- ====================== -- genConjunto es un generador de conjuntos. Por ejemplo, -- λ> sample (genConjunto :: Gen (Conj Int)) -- {} -- {1} -- {2} -- {} -- {-5, -1} -- {-9, -8, 2, 3, 10} -- {4} -- {-13, -7, 1, 14} -- {-12, -10, -9, -4, 1, 2, 5, 14, 16} -- {-18, -15, -14, -13, -10, -7, -6, -4, -1, 1, 10, 11, 12, 16} -- {-16, -9, -6, -5, -4, -2, 3, 6, 9, 13, 17} genConjunto :: (Arbitrary a, Ord a) => Gen (Conj a) genConjunto = do xs <- listOf arbitrary return (foldr inserta vacio xs) -- Los conjuntos son concreciones de los arbitrarios. instance (Arbitrary a, Ord a) => Arbitrary (Conj a) where arbitrary = genConjunto -- Propiedades de los conjuntos -- -- ============================ prop_conjuntos :: Int -> Int -> Conj Int -> Bool prop_conjuntos x y c = inserta x (inserta x c) == inserta x c && inserta x (inserta y c) == inserta y (inserta x c) && not (pertenece x vacio) && pertenece y (inserta x c) == (x == y) || pertenece y c && elimina x vacio == vacio && elimina x (inserta y c) == (if x == y then elimina x c else inserta y (elimina x c)) && esVacio (vacio :: Conj Int) && not (esVacio (inserta x c)) -- Comprobación -- λ> quickCheck prop_conjuntos -- +++ OK, passed 100 tests. |
2.5. Implementación de los conjuntos mediante librería
La implementación se encuentra en el módulo ConjuntoConLibreria.hs cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
module TAD.ConjuntoConLibreria (Conj, vacio, -- Conj a inserta, -- Ord a => a -> Conj a -> Conj a menor, -- Ord a => Conj a -> a elimina, -- Ord a => a -> Conj a -> Conj a pertenece, -- Ord a => a -> Conj a -> Bool esVacio -- Conj a -> Bool ) where import Data.Set as S (Set, empty, insert, findMin, delete, member, null, fromList, toList) import Data.List (intercalate) import Test.QuickCheck -- Los conjuntos como conjuntos de la librería Data.Set newtype Conj a = Cj (Set a) deriving (Eq, Ord) -- (escribeConjunto c) es la cadena correspondiente al conjunto c. Por -- ejemplo, -- λ> escribeConjunto (Cj (fromList [])) -- "{}" -- λ> escribeConjunto (Cj (fromList [5])) -- "{5}" -- λ> escribeConjunto (Cj (fromList [2, 5])) -- "{2, 5}" -- λ> escribeConjunto (Cj (fromList [5, 2])) -- "{2, 5}" escribeConjunto :: Show a => Conj a -> String escribeConjunto (Cj s) = "{" ++ intercalate ", " (map show (toList s)) ++ "}" -- Procedimiento de escritura de conjuntos. instance Show a => Show (Conj a) where show = escribeConjunto -- vacio es el conjunto vacío. Por ejemplo, -- λ> vacio -- {} vacio :: Conj a vacio = Cj empty -- (inserta x c) es el conjunto obtenido añadiendo el elemento x al -- conjunto c. Por ejemplo, -- λ> inserta 5 vacio -- {5} -- λ> inserta 2 (inserta 5 vacio) -- {2, 5} -- λ> inserta 5 (inserta 2 vacio) -- {2, 5} inserta :: Ord a => a -> Conj a -> Conj a inserta x (Cj s) = Cj (insert x s) -- (menor c) es el menor elemento del conjunto c. Por ejemplo, -- λ> menor (inserta 5 (inserta 2 vacio)) -- 2 menor :: Ord a => Conj a -> a menor (Cj s) = findMin s -- (elimina x c) es el conjunto obtenido eliminando el elemento x -- del conjunto c. Por ejemplo, -- λ> elimina 2 (inserta 5 (inserta 2 vacio)) -- {5} elimina :: Ord a => a -> Conj a -> Conj a elimina x (Cj s) = Cj (delete x s) -- (esVacio c) se verifica si c es el conjunto vacío. Por ejemplo, -- λ> esVacio (inserta 5 (inserta 2 vacio)) -- False -- λ> esVacio vacio -- True esVacio :: Conj a -> Bool esVacio (Cj s) = S.null s -- (pertenece x c) se verifica si x pertenece al conjunto c. Por ejemplo, -- λ> pertenece 2 (inserta 5 (inserta 2 vacio)) -- True -- λ> pertenece 4 (inserta 5 (inserta 2 vacio)) -- False pertenece :: Ord a => a -> Conj a -> Bool pertenece x (Cj s) = member x s -- Generador de conjuntos -- -- ====================== -- genConjunto es un generador de conjuntos. Por ejemplo, -- λ> sample (genConjunto :: Gen (Conj Int)) -- {} -- {-2, 0} -- {-1, 3} -- {-3, 2} -- {-5, -4, -3, 2, 4, 6, 7} -- {-4, 4} -- {-9, -6, -3, 1, 5, 11, 12} -- {-10, -8, -7, -3, 1, 2, 8, 9, 10, 13} -- {-13, -8, -7, -6, -1, 0, 1, 6, 7, 9, 11, 14, 16} -- {-15, -12, -9, 1, 2, 9, 13, 15, 16, 18} -- {-16} genConjunto :: (Arbitrary a, Ord a) => Gen (Conj a) genConjunto = do xs <- listOf arbitrary return (Cj (fromList xs)) -- Los conjuntos son concreciones de los arbitrarios. instance (Arbitrary a, Ord a) => Arbitrary (Conj a) where arbitrary = genConjunto -- Propiedades de los conjuntos -- -- ============================ prop_conjuntos :: Int -> Int -> Conj Int -> Bool prop_conjuntos x y c = inserta x (inserta x c) == inserta x c && inserta x (inserta y c) == inserta y (inserta x c) && not (pertenece x vacio) && pertenece y (inserta x c) == (x == y) || pertenece y c && elimina x vacio == vacio && elimina x (inserta y c) == (if x == y then elimina x c else inserta y (elimina x c)) && esVacio (vacio :: Conj Int) && not (esVacio (inserta x c)) -- Comprobación -- λ> quickCheck prop_conjuntos -- +++ OK, passed 100 tests. |
3. Los conjuntos en Python
3.1. El tipo abstracto de los conjuntos en Python
La implementación se encuentra en el módulo conjunto.py cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
__all__ = [ 'Conj', 'vacio', 'inserta', 'menor', 'elimina', 'pertenece', 'esVacio', 'conjuntoAleatorio' ] # from src.TAD.conjuntoConListasNoOrdenadasConDuplicados import ( # Conj, conjuntoAleatorio, elimina, esVacio, inserta, # menor, pertenece, vacio) # from src.TAD.conjuntoConListasNoOrdenadasSinDuplicados import ( # Conj, conjuntoAleatorio, elimina, esVacio, inserta, menor, pertenece, # vacio) from src.TAD.conjuntoConListasOrdenadasSinDuplicados import ( Conj, conjuntoAleatorio, elimina, esVacio, inserta, menor, pertenece, vacio) # from src.TAD.conjuntoConLibreria import ( # Conj, conjuntoAleatorio, elimina, esVacio, inserta, menor, pertenece, # vacio) |
Para usar el TAD hay que usar una implementación concreta. En principio, consideraremos las siguientes:
- mediante listas no ordenadas con duplicados,
- mediante listas no ordenadas sin duplicados,
- mediante listas ordenadas sin duplicados y
- mediante la librería de conjuntos.
3.2. Implementación de los conjuntos mediante listas no ordenadas con duplicados
La implementación se encuentra en el módulo conjuntoConListasNoOrdenadasConDuplicados.py en el que se define la clase Conj con los siguientes métodos:
- inserta(x) añade x al conjunto.
- menor() es el menor elemento del conjunto.
- elimina(x) elimina las ocurrencias de x en el conjunto.
- pertenece(x) se verifica si x pertenece al conjunto.
- esVacia() se verifica si la cola es vacía.
Por ejemplo,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
>>> c = Conj() >>> c {} >>> c.inserta(5) >>> c.inserta(2) >>> c.inserta(3) >>> c.inserta(4) >>> c.inserta(5) >>> c {2, 3, 4, 5} >>> c.menor() 2 >>> c.elimina(3) >>> c {2, 4, 5} >>> c.pertenece(4) True >>> c.pertenece(3) False >>> c.esVacio() False >>> c = Conj() >>> c.esVacio() True >>> c = Conj() >>> c.inserta(2) >>> c.inserta(5) >>> d = Conj() >>> d.inserta(5) >>> d.inserta(2) >>> d.inserta(5) >>> c == d True |
Además se definen las correspondientes funciones. Por ejemplo,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
>>> vacio() {} >>> inserta(5, inserta(3, inserta(2, inserta(5, vacio())))) {2, 3, 5} >>> menor(inserta(5, inserta(3, inserta(2, inserta(5, vacio()))))) 2 >>> elimina(5, inserta(5, inserta(3, inserta(2, inserta(5, vacio()))))) {2, 3} >>> pertenece(5, inserta(5, inserta(3, inserta(2, inserta(5, vacio()))))) True >>> pertenece(1, inserta(5, inserta(3, inserta(2, inserta(5, vacio()))))) False >>> esVacio(inserta(5, inserta(3, inserta(2, inserta(5, vacio()))))) False >>> esVacio(vacio()) True >>> inserta(5, inserta(2, vacio())) == inserta(2, inserta(5, (inserta(2, vacio())))) True |
Finalmente, se define un generador aleatorio de conjuntos y se comprueba que los conjuntos cumplen las propiedades de su especificación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
from __future__ import annotations __all__ = [ 'Conj', 'vacio', 'inserta', 'menor', 'elimina', 'pertenece', 'esVacio', 'conjuntoAleatorio' ] from abc import abstractmethod from copy import deepcopy from dataclasses import dataclass, field from typing import Any, Generic, Protocol, TypeVar from hypothesis import given from hypothesis import strategies as st class Comparable(Protocol): @abstractmethod def __lt__(self: A, otro: A) -> bool: pass A = TypeVar('A', bound=Comparable) # Clase de los conjuntos mediante listas no ordenadas con duplicados # ================================================================== @dataclass class Conj(Generic[A]): _elementos: list[A] = field(default_factory=list) def __repr__(self) -> str: """ Devuelve una cadena con los elementos del conjunto entre llaves y separados por ", ". """ return '{' + ', '.join(str(x) for x in sorted(list(set(self._elementos)))) + '}' def __eq__(self, c: Any) -> bool: """ Se verifica si el conjunto es igual a c; es decir, tienen los mismos elementos sin importar el orden ni las repeticiones. """ return sorted(list(set(self._elementos))) == sorted(list(set(c._elementos))) def inserta(self, x: A) -> None: """ Añade el elemento x al conjunto. """ self._elementos.append(x) def menor(self) -> A: """ Devuelve el menor elemento del conjunto """ return min(self._elementos) def elimina(self, x: A) -> None: """ Elimina el elemento x del conjunto. """ while x in self._elementos: self._elementos.remove(x) def esVacio(self) -> bool: """ Se verifica si el conjunto está vacío. """ return not self._elementos def pertenece(self, x: A) -> bool: """ Se verifica si x pertenece al conjunto. """ return x in self._elementos # Funciones del tipo conjunto # =========================== def vacio() -> Conj[A]: """ Crea y devuelve un conjunto vacío de tipo A. """ c: Conj[A] = Conj() return c def inserta(x: A, c: Conj[A]) -> Conj[A]: """ Inserta un elemento x en el conjunto c y devuelve un nuevo comjunto con el elemento insertado. """ _aux = deepcopy(c) _aux.inserta(x) return _aux def menor(c: Conj[A]) -> A: """ Devuelve el menor elemento del conjunto c. """ return c.menor() def elimina(x: A, c: Conj[A]) -> Conj[A]: """ Elimina las ocurrencias de c en c y devuelve una copia del conjunto resultante. """ _aux = deepcopy(c) _aux.elimina(x) return _aux def pertenece(x: A, c: Conj[A]) -> bool: """ Se verifica si x pertenece a c. """ return c.pertenece(x) def esVacio(c: Conj[A]) -> bool: """ Se verifica si el conjunto está vacío. """ return c.esVacio() # Generador de conjuntos # ====================== def conjuntoAleatorio() -> st.SearchStrategy[Conj[int]]: """ Genera una estrategia de búsqueda para generar conjuntos de enteros de forma aleatoria. Utiliza la librería Hypothesis para generar una lista de enteros y luego se convierte en una instancia de la clase cola. """ return st.lists(st.integers()).map(Conj) # Comprobación de las propiedades de los conjuntos # ================================================ # Las propiedades son @given(c=conjuntoAleatorio(), x=st.integers(), y=st.integers()) def test_conjuntos(c: Conj[int], x: int, y: int) -> None: v: Conj[int] = vacio() assert inserta(x, inserta(x, c)) == inserta(x, c) assert inserta(x, inserta(y, c)) == inserta(y, inserta(x, c)) assert not pertenece(x, v) assert pertenece(y, inserta(x, c)) == (x == y) or pertenece(y, c) assert elimina(x, v) == v def relacion(x: int, y: int, c: Conj[int]) -> Conj[int]: if x == y: return elimina(x, c) return inserta(y, elimina(x, c)) assert elimina(x, inserta(y, c)) == relacion(x, y, c) assert esVacio(vacio()) assert not esVacio(inserta(x, c)) # La comprobación es # > poetry run pytest -q conjuntoConListasNoOrdenadasConDuplicados.py # 1 passed in 0.33s |
3.3. Implementación de los conjuntos mediante listas no ordenadas sin duplicados
La implementación se encuentra en el módulo conjuntoConListasNoOrdenadasSinDuplicados.py cuyo contenido es
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
from __future__ import annotations __all__ = [ 'Conj', 'vacio', 'inserta', 'menor', 'elimina', 'pertenece', 'esVacio', 'conjuntoAleatorio' ] from abc import abstractmethod from copy import deepcopy from dataclasses import dataclass, field from typing import Any, Generic, Protocol, TypeVar from hypothesis import given from hypothesis import strategies as st class Comparable(Protocol): @abstractmethod def __lt__(self: A, otro: A) -> bool: pass A = TypeVar('A', bound=Comparable) # Clase de los conjuntos mediante listas no ordenadas sin duplicados # ================================================================== @dataclass class Conj(Generic[A]): _elementos: list[A] = field(default_factory=list) def __repr__(self) -> str: """ Devuelve una cadena con los elementos del conjunto entre llaves y separados por ", ". """ return '{' + ', '.join(str(x) for x in sorted(self._elementos)) + '}' def __eq__(self, c: Any) -> bool: """ Se verifica si el conjunto es igual a c; es decir, tienen los mismos elementos sin importar el orden ni las repeticiones. """ return sorted(self._elementos) == sorted(c._elementos) def inserta(self, x: A) -> None: """ Añade el elemento x al conjunto. """ if x not in self._elementos: self._elementos.append(x) def menor(self) -> A: """ Devuelve el menor elemento del conjunto """ return min(self._elementos) def elimina(self, x: A) -> None: """ Elimina el elemento x del conjunto. """ if x in self._elementos: self._elementos.remove(x) def esVacio(self) -> bool: """ Se verifica si el conjunto está vacío. """ return not self._elementos def pertenece(self, x: A) -> bool: """ Se verifica si x pertenece al conjunto. """ return x in self._elementos # Funciones del tipo conjunto # =========================== def vacio() -> Conj[A]: """ Crea y devuelve un conjunto vacío de tipo A. """ c: Conj[A] = Conj() return c def inserta(x: A, c: Conj[A]) -> Conj[A]: """ Inserta un elemento x en el conjunto c y devuelve un nuevo comjunto con el elemento insertado. """ _aux = deepcopy(c) _aux.inserta(x) return _aux def menor(c: Conj[A]) -> A: """ Devuelve el menor elemento del conjunto c. """ return c.menor() def elimina(x: A, c: Conj[A]) -> Conj[A]: """ Elimina las ocurrencias de c en c y devuelve una copia del conjunto resultante. """ _aux = deepcopy(c) _aux.elimina(x) return _aux def pertenece(x: A, c: Conj[A]) -> bool: """ Se verifica si x pertenece a c. """ return c.pertenece(x) def esVacio(c: Conj[A]) -> bool: """ Se verifica si el conjunto está vacío. """ return c.esVacio() # Generador de conjuntos # ====================== def sin_duplicados(xs: list[int]) -> list[int]: return list(set(xs)) def conjuntoAleatorio() -> st.SearchStrategy[Conj[int]]: """ Estrategia de búsqueda para generar conjuntos de enteros de forma aleatoria. """ xs = st.lists(st.integers()).map(sin_duplicados) return xs.map(Conj) # Comprobación de las propiedades de los conjuntos # ================================================ # Las propiedades son @given(c=conjuntoAleatorio(), x=st.integers(), y=st.integers()) def test_conjuntos(c: Conj[int], x: int, y: int) -> None: assert inserta(x, inserta(x, c)) == inserta(x, c) assert inserta(x, inserta(y, c)) == inserta(y, inserta(x, c)) assert not pertenece(x, vacio()) assert pertenece(y, inserta(x, c)) == (x == y) or pertenece(y, c) assert elimina(x, vacio()) == vacio() def relacion(x: int, y: int, c: Conj[int]) -> Conj[int]: if x == y: return elimina(x, c) return inserta(y, elimina(x, c)) assert elimina(x, inserta(y, c)) == relacion(x, y, c) assert esVacio(vacio()) assert not esVacio(inserta(x, c)) # La comprobación es # > poetry run pytest -q conjuntoConListasNoOrdenadasSinDuplicados.py # 1 passed in 0.26s |
3.4. Implementación de los conjuntos mediante listas ordenadas sin duplicados
La implementación se encuentra en el módulo conjuntoConListasOrdenadasSinDuplicados.py cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
from __future__ import annotations __all__ = [ 'Conj', 'vacio', 'inserta', 'menor', 'elimina', 'pertenece', 'esVacio', 'conjuntoAleatorio' ] from abc import abstractmethod from bisect import bisect_left, insort_left from copy import deepcopy from dataclasses import dataclass, field from itertools import takewhile from typing import Generic, Protocol, TypeVar from hypothesis import given from hypothesis import strategies as st class Comparable(Protocol): @abstractmethod def __lt__(self: A, otro: A) -> bool: pass A = TypeVar('A', bound=Comparable) # Clase de los conjuntos mediante listas ordenadas sin duplicados # =============================================================== @dataclass class Conj(Generic[A]): _elementos: list[A] = field(default_factory=list) def __repr__(self) -> str: """ Devuelve una cadena con los elementos del conjunto entre llaves y separados por ", ". """ return '{' + ', '.join(str(x) for x in self._elementos) + '}' def inserta(self, x: A) -> None: """ Añade el elemento x al conjunto. """ if x not in self._elementos: insort_left(self._elementos, x) def menor(self) -> A: """ Devuelve el menor elemento del conjunto """ return self._elementos[0] def elimina(self, x: A) -> None: """ Elimina el elemento x del conjunto. """ pos = bisect_left(self._elementos, x) if pos < len(self._elementos) and self._elementos[pos] == x: self._elementos.pop(pos) def esVacio(self) -> bool: """ Se verifica si el conjunto está vacío. """ return not self._elementos def pertenece(self, x: A) -> bool: """ Se verifica si x pertenece al conjunto. """ return x in takewhile(lambda y: y <= x, self._elementos) # Funciones del tipo conjunto # =========================== def vacio() -> Conj[A]: """ Crea y devuelve un conjunto vacío de tipo A. """ c: Conj[A] = Conj() return c def inserta(x: A, c: Conj[A]) -> Conj[A]: """ Inserta un elemento x en el conjunto c y devuelve un nuevo comjunto con el elemento insertado. """ _aux = deepcopy(c) _aux.inserta(x) return _aux def menor(c: Conj[A]) -> A: """ Devuelve el menor elemento del conjunto c. """ return c.menor() def elimina(x: A, c: Conj[A]) -> Conj[A]: """ Elimina las ocurrencias de c en c y devuelve una copia del conjunto resultante. """ _aux = deepcopy(c) _aux.elimina(x) return _aux def pertenece(x: A, c: Conj[A]) -> bool: """ Se verifica si x pertenece a c. """ return c.pertenece(x) def esVacio(c: Conj[A]) -> bool: """ Se verifica si el conjunto está vacío. """ return c.esVacio() # Generador de conjuntos # ====================== def sin_duplicados_y_ordenado(xs: list[int]) -> list[int]: xs = list(set(xs)) xs.sort() return xs def conjuntoAleatorio() -> st.SearchStrategy[Conj[int]]: """ Estrategia de búsqueda para generar conjuntos de enteros de forma aleatoria. """ xs = st.lists(st.integers()).map(sin_duplicados_y_ordenado) return xs.map(Conj) # Comprobación de las propiedades de los conjuntos # ================================================ # Las propiedades son @given(c=conjuntoAleatorio(), x=st.integers(), y=st.integers()) def test_conjuntos(c: Conj[int], x: int, y: int) -> None: assert inserta(x, inserta(x, c)) == inserta(x, c) assert inserta(x, inserta(y, c)) == inserta(y, inserta(x, c)) assert not pertenece(x, vacio()) assert pertenece(y, inserta(x, c)) == (x == y) or pertenece(y, c) assert elimina(x, vacio()) == vacio() def relacion(x: int, y: int, c: Conj[int]) -> Conj[int]: if x == y: return elimina(x, c) return inserta(y, elimina(x, c)) assert elimina(x, inserta(y, c)) == relacion(x, y, c) assert esVacio(vacio()) assert not esVacio(inserta(x, c)) # La comprobación es # > poetry run pytest -q conjuntoConListasOrdenadasSinDuplicados.py # 1 passed in 0.13s |
3.5. Implementación de los conjuntos mediante librería
La implementación se encuentra en el módulo conjuntoConLibreria.py cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
from __future__ import annotations __all__ = [ 'Conj', 'vacio', 'inserta', 'menor', 'elimina', 'pertenece', 'esVacio', 'conjuntoAleatorio' ] from abc import abstractmethod from copy import deepcopy from dataclasses import dataclass, field from typing import Generic, Protocol, TypeVar from hypothesis import given from hypothesis import strategies as st class Comparable(Protocol): @abstractmethod def __lt__(self: A, otro: A) -> bool: pass A = TypeVar('A', bound=Comparable) # Clase de los conjuntos mediante librería # ======================================== @dataclass class Conj(Generic[A]): _elementos: set[A] = field(default_factory=set) def __repr__(self) -> str: xs = [str(x) for x in self._elementos] return "{" + ", ".join(xs) + "}" def inserta(self, x: A) -> None: """ Añade el elemento x al conjunto. """ self._elementos.add(x) def menor(self) -> A: """ Devuelve el menor elemento del conjunto """ return min(self._elementos) def elimina(self, x: A) -> None: """ Elimina el elemento x del conjunto. """ self._elementos.discard(x) def esVacio(self) -> bool: """ Se verifica si el conjunto está vacío. """ return not self._elementos def pertenece(self, x: A) -> bool: """ Se verifica si x pertenece al conjunto. """ return x in self._elementos # Funciones del tipo conjunto # =========================== def vacio() -> Conj[A]: """ Crea y devuelve un conjunto vacío de tipo A. """ c: Conj[A] = Conj() return c def inserta(x: A, c: Conj[A]) -> Conj[A]: """ Inserta un elemento x en el conjunto c y devuelve un nuevo comjunto con el elemento insertado. """ _aux = deepcopy(c) _aux.inserta(x) return _aux def menor(c: Conj[A]) -> A: """ Devuelve el menor elemento del conjunto c. """ return c.menor() def elimina(x: A, c: Conj[A]) -> Conj[A]: """ Elimina las ocurrencias de c en c y devuelve una copia del conjunto resultante. """ _aux = deepcopy(c) _aux.elimina(x) return _aux def pertenece(x: A, c: Conj[A]) -> bool: """ Se verifica si x pertenece a c. """ return c.pertenece(x) def esVacio(c: Conj[A]) -> bool: """ Se verifica si el conjunto está vacío. """ return c.esVacio() # Generador de conjuntos # ====================== def conjuntoAleatorio() -> st.SearchStrategy[Conj[int]]: """ Estrategia de búsqueda para generar conjuntos de enteros de forma aleatoria. """ return st.builds(Conj, st.lists(st.integers()).map(set)) # Comprobación de las propiedades de los conjuntos # ================================================ # Las propiedades son @given(c=conjuntoAleatorio(), x=st.integers(), y=st.integers()) def test_conjuntos(c: Conj[int], x: int, y: int) -> None: assert inserta(x, inserta(x, c)) == inserta(x, c) assert inserta(x, inserta(y, c)) == inserta(y, inserta(x, c)) assert not pertenece(x, vacio()) assert pertenece(y, inserta(x, c)) == (x == y) or pertenece(y, c) assert elimina(x, vacio()) == vacio() def relacion(x: int, y: int, c: Conj[int]) -> Conj[int]: if x == y: return elimina(x, c) return inserta(y, elimina(x, c)) assert elimina(x, inserta(y, c)) == relacion(x, y, c) assert esVacio(vacio()) assert not esVacio(inserta(x, c)) # La comprobación es # > poetry run pytest -q conjuntoConLibreria.py # 1 passed in 0.22s |