Esta semana he publicado en Exercitium las soluciones de los siguientes problemas sobre el tipo abstracto de datos de los conjuntos
- 1. Partición de un conjunto según una propiedad
- 2. Partición según un número
- 3. Aplicación de una función a los elementos de un conjunto
- 4. Todos los elementos verifican una propiedad
- 5. Algunos elementos verifican una propiedad
A continuación se muestran las soluciones.
1. TAD de los conjuntos: Partición de un conjunto según una propiedad
Utilizando el tipo abstracto de datos de los conjuntos definir la función
particion :: Ord a => (a -> Bool) -> Conj a -> (Conj a, Conj a) |
tal que particion c
es el par formado por dos conjuntos: el de los elementos de c
que verifican p
y el de los elementos que no lo verifican. Por ejemplo,
λ> ej = inserta 5 (inserta 4 (inserta 7 (inserta 2 vacio))) λ> particion even ej ({2, 4},{5, 7}) |
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
import TAD.Conjunto (Conj, vacio, inserta) import TAD_Transformaciones_conjuntos_listas (conjuntoAlista, listaAconjunto) import TAD_Subconjunto_por_propiedad (filtra) import Data.List (partition) import Test.QuickCheck.HigherOrder -- 1ª solución -- =========== particion :: Ord a => (a -> Bool) -> Conj a -> (Conj a, Conj a) particion p c = (filtra p c, filtra (not . p) c) -- La función filtra está definida en el ejercicio -- "Subconjunto determinado por una propiedad" que se encuentra en -- https://bit.ly/3lplFoV -- 2ª solución -- =========== particion2 :: Ord a => (a -> Bool) -> Conj a -> (Conj a, Conj a) particion2 p c = (listaAconjunto xs, listaAconjunto ys) where (xs, ys) = partition p (conjuntoAlista c) -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_particion :: (Int -> Bool) -> [Int] -> Bool prop_particion p xs = particion p c == particion2 p c where c = listaAconjunto xs -- La comprobación es -- λ> quickCheck' prop_particion -- +++ OK, passed 100 tests. |
from __future__ import annotations from abc import abstractmethod from copy import deepcopy from typing import Callable, Protocol, TypeVar from hypothesis import given from src.TAD.conjunto import (Conj, conjuntoAleatorio, elimina, esVacio, inserta, menor, vacio) from src.TAD_Subconjunto_por_propiedad import filtra class Comparable(Protocol): @abstractmethod def __lt__(self: A, otro: A) -> bool: pass A = TypeVar('A', bound=Comparable) # 1ª solución # =========== def particion(p: Callable[[A], bool], c: Conj[A]) -> tuple[Conj[A], Conj[A]]: return (filtra(p, c), filtra(lambda x: not p(x), c)) # La función filtra está definida en el ejercicio # "Subconjunto determinado por una propiedad" que se encuentra en # https://bit.ly/3lplFoV # 2ª solución # =========== def particion2Aux(p: Callable[[A], bool], c: Conj[A]) -> tuple[Conj[A], Conj[A]]: r: Conj[A] = vacio() s: Conj[A] = vacio() while not esVacio(c): mc = menor(c) c = elimina(mc, c) if p(mc): r = inserta(mc, r) else: s = inserta(mc, s) return (r, s) def particion2(p: Callable[[A], bool], c: Conj[A]) -> tuple[Conj[A], Conj[A]]: _c = deepcopy(c) return particion2Aux(p, _c) # 3ª solución # =========== def particion3Aux(p: Callable[[A], bool], c: Conj[A]) -> tuple[Conj[A], Conj[A]]: r: Conj[A] = Conj() s: Conj[A] = Conj() while not c.esVacio(): mc = c.menor() c.elimina(mc) if p(mc): r.inserta(mc) else: s.inserta(mc) return (r, s) def particion3(p: Callable[[A], bool], c: Conj[A]) -> tuple[Conj[A], Conj[A]]: _c = deepcopy(c) return particion3Aux(p, _c) # Comprobación de equivalencia de las definiciones # ================================================ # La propiedad es @given(c=conjuntoAleatorio()) def test_particion(c: Conj[int]) -> None: r = particion(lambda x: x % 2 == 0, c) assert particion2(lambda x: x % 2 == 0, c) == r assert particion3(lambda x: x % 2 == 0, c) == r # La comprobación es # src> poetry run pytest -q TAD_Particion_por_una_propiedad.py # 1 passed in 0.28s |
2. TAD de los conjuntos: Partición según un número
Utilizando el tipo abstracto de datos de los conjuntos definir la función
divide :: (Ord a) => a-> Conj a -> (Conj a, Conj a) |
tal que divide x c
es el par formado por dos subconjuntos de c
: el de los elementos menores o iguales que x
y el de los mayores que x
. Por ejemplo,
λ> divide 5 (inserta 7 (inserta 2 (inserta 8 vacio))) ({2},{7, 8}) |
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
import TAD.Conjunto (Conj, vacio, inserta, esVacio, menor, elimina) import TAD_Particion_por_una_propiedad (particion) import Test.QuickCheck -- 1ª solución -- =========== divide :: Ord a => a-> Conj a -> (Conj a, Conj a) divide x c | esVacio c = (vacio, vacio) | mc <= x = (inserta mc c1, c2) | otherwise = (c1, inserta mc c2) where mc = menor c rc = elimina mc c (c1, c2) = divide x rc -- 2ª solución -- =========== divide2 :: Ord a => a-> Conj a -> (Conj a, Conj a) divide2 x = particion (<= x) -- La función particion está definida en el ejercicio -- "Partición de un conjunto según una propiedad" que se encuentra en -- https://bit.ly/3YCOah5 -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_divide :: Int -> Conj Int -> Bool prop_divide x c = divide x c == divide2 x c -- La comprobación es -- λ> quickCheck prop_divide -- +++ OK, passed 100 tests. |
from __future__ import annotations from abc import abstractmethod from copy import deepcopy from typing import Protocol, TypeVar from hypothesis import given from hypothesis import strategies as st from src.TAD.conjunto import (Conj, conjuntoAleatorio, elimina, esVacio, inserta, menor, vacio) from src.TAD_Particion_por_una_propiedad import particion class Comparable(Protocol): @abstractmethod def __lt__(self: A, otro: A) -> bool: pass A = TypeVar('A', bound=Comparable) # 1ª solución # =========== def divide(x: A, c: Conj[A]) -> tuple[Conj[A], Conj[A]]: if esVacio(c): return (vacio(), vacio()) mc = menor(c) rc = elimina(mc, c) (c1, c2) = divide(x, rc) if mc <= x: return (inserta(mc, c1), c2) return (c1, inserta(mc, c2)) # 2ª solución # =========== def divide2(x: A, c: Conj[A]) -> tuple[Conj[A], Conj[A]]: return particion(lambda y: y <= x, c) # La función particion está definida en el ejercicio # "Partición de un conjunto según una propiedad" que se encuentra en # https://bit.ly/3YCOah5 # 3ª solución # =========== def divide3Aux(x: A, c: Conj[A]) -> tuple[Conj[A], Conj[A]]: r: Conj[A] = vacio() s: Conj[A] = vacio() while not esVacio(c): mc = menor(c) c = elimina(mc, c) if mc <= x: r = inserta(mc, r) else: s = inserta(mc, s) return (r, s) def divide3(x: A, c: Conj[A]) -> tuple[Conj[A], Conj[A]]: _c = deepcopy(c) return divide3Aux(x, _c) # 4ª solución # =========== def divide4Aux(x: A, c: Conj[A]) -> tuple[Conj[A], Conj[A]]: r: Conj[A] = Conj() s: Conj[A] = Conj() while not c.esVacio(): mc = c.menor() c.elimina(mc) if mc <= x: r.inserta(mc) else: s.inserta(mc) return (r, s) def divide4(x: A, c: Conj[A]) -> tuple[Conj[A], Conj[A]]: _c = deepcopy(c) return divide4Aux(x, _c) # Comprobación de equivalencia de las definiciones # ================================================ # La propiedad es @given(x=st.integers(), c=conjuntoAleatorio()) def test_particion(x: int, c: Conj[int]) -> None: r = divide(x, c) assert divide2(x, c) == r assert divide3(x, c) == r assert divide4(x, c) == r # La comprobación es # src> poetry run pytest -q TAD_Particion_segun_un_numero.py # 1 passed in 0.30s |
3. TAD de los conjuntos: Aplicación de una función a los elementos de un conjunto
Utilizando el tipo abstracto de datos de los conjuntos definir la función
mapC :: (Ord a, Ord b) => (a -> b) -> Conj a -> Conj b |
tal que map f c
es el conjunto formado por las imágenes de los elementos del conjunto c
, mediante la aplicación f
. Por ejemplo,
λ> mapC (*2) (inserta 3 (inserta 1 vacio)) {2, 6} |
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
import TAD.Conjunto (Conj, vacio, inserta, esVacio, menor, elimina) import TAD_Transformaciones_conjuntos_listas (conjuntoAlista, listaAconjunto) import Test.QuickCheck.HigherOrder -- 1ª solución -- =========== mapC :: (Ord a, Ord b) => (a -> b) -> Conj a -> Conj b mapC f c | esVacio c = vacio | otherwise = inserta (f mc) (mapC f rc) where mc = menor c rc = elimina mc c -- 2ª solución -- =========== mapC2 :: (Ord a, Ord b) => (a -> b) -> Conj a -> Conj b mapC2 f c = listaAconjunto (map f (conjuntoAlista c)) -- Las funciones conjuntoAlista y listaAconjunto está definida en el -- ejercicio Transformaciones entre conjuntos y listas" que se encuentra -- en https://bit.ly/3RexzxH -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_mapC :: (Int -> Int) -> [Int] -> Bool prop_mapC f xs = mapC f c == mapC2 f c where c = listaAconjunto xs -- La comprobación es -- λ> quickCheck' prop_mapC -- +++ OK, passed 100 tests. |
from __future__ import annotations from abc import abstractmethod from copy import deepcopy from typing import Callable, Protocol, TypeVar from hypothesis import given from src.TAD.conjunto import (Conj, conjuntoAleatorio, elimina, esVacio, inserta, menor, vacio) from src.TAD_Transformaciones_conjuntos_listas import (conjuntoAlista, listaAconjunto) class Comparable(Protocol): @abstractmethod def __lt__(self: A, otro: A) -> bool: pass A = TypeVar('A', bound=Comparable) B = TypeVar('B', bound=Comparable) # 1ª solución # =========== def mapC(f: Callable[[A], B], c: Conj[A]) -> Conj[B]: if esVacio(c): return vacio() mc = menor(c) rc = elimina(mc, c) return inserta(f(mc), mapC(f, rc)) # 2ª solución # =========== def mapC2(f: Callable[[A], B], c: Conj[A]) -> Conj[B]: return listaAconjunto(list(map(f, conjuntoAlista(c)))) # Las funciones conjuntoAlista y listaAconjunto está definida en el # ejercicio Transformaciones entre conjuntos y listas" que se encuentra # en https://bit.ly/3RexzxH # 3ª solución # =========== def mapC3Aux(f: Callable[[A], B], c: Conj[A]) -> Conj[B]: r: Conj[B] = vacio() while not esVacio(c): mc = menor(c) c = elimina(mc, c) r = inserta(f(mc), r) return r def mapC3(f: Callable[[A], B], c: Conj[A]) -> Conj[B]: _c = deepcopy(c) return mapC3Aux(f, _c) # 4ª solución # =========== def mapC4Aux(f: Callable[[A], B], c: Conj[A]) -> Conj[B]: r: Conj[B] = Conj() while not c.esVacio(): mc = c.menor() c.elimina(mc) r.inserta(f(mc)) return r def mapC4(f: Callable[[A], B], c: Conj[A]) -> Conj[B]: _c = deepcopy(c) return mapC4Aux(f, _c) # Comprobación de equivalencia de las definiciones # ================================================ # La propiedad es @given(c=conjuntoAleatorio()) def test_mapPila(c: Conj[int]) -> None: r = mapC(lambda x: 2 * x, c) assert mapC2(lambda x: 2 * x, c) == r assert mapC3(lambda x: 2 * x, c) == r assert mapC4(lambda x: 2 * x, c) == r # La comprobación es # src> poetry run pytest -q TAD_mapC.py # 1 passed in 0.29s |
4. TAD de los conjuntos: Todos los elementos verifican una propiedad
Utilizando el tipo abstracto de datos de los conjuntos definir la función
todos :: Ord a => (a -> Bool) -> Conj a -> Bool |
tal que todos p c
se verifica si todos los elemsntos de c
verifican el predicado p
. Por ejemplo,
todos even (inserta 4 (inserta 6 vacio)) == True todos even (inserta 4 (inserta 7 vacio)) == False |
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
import TAD.Conjunto (Conj, vacio, inserta, esVacio, menor, elimina) import TAD_Transformaciones_conjuntos_listas (conjuntoAlista, listaAconjunto) import Test.QuickCheck.HigherOrder -- 1ª solución -- =========== todos :: Ord a => (a -> Bool) -> Conj a -> Bool todos p c | esVacio c = True | otherwise = p mc && todos p rc where mc = menor c rc = elimina mc c -- 2ª solución -- =========== todos2 :: Ord a => (a -> Bool) -> Conj a -> Bool todos2 p c = all p (conjuntoAlista c) -- La función conjuntoAlista está definida en el ejercicio -- "Transformaciones entre conjuntos y listas" que se encuentra -- en https://bit.ly/3RexzxH -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_todos :: (Int -> Bool) -> [Int] -> Bool prop_todos p xs = todos p c == todos2 p c where c = listaAconjunto xs -- La comprobación es -- λ> quickCheck' prop_todos -- +++ OK, passed 100 tests. |
from __future__ import annotations from abc import abstractmethod from copy import deepcopy from typing import Callable, Protocol, TypeVar from hypothesis import given from src.TAD.conjunto import (Conj, conjuntoAleatorio, elimina, esVacio, inserta, menor, vacio) from src.TAD_Transformaciones_conjuntos_listas import conjuntoAlista class Comparable(Protocol): @abstractmethod def __lt__(self: A, otro: A) -> bool: pass A = TypeVar('A', bound=Comparable) # 1ª solución # =========== def todos(p: Callable[[A], bool], c: Conj[A]) -> bool: if esVacio(c): return True mc = menor(c) rc = elimina(mc, c) return p(mc) and todos(p, rc) # 2ª solución # =========== def todos2(p: Callable[[A], bool], c: Conj[A]) -> bool: return all(p(x) for x in conjuntoAlista(c)) # La función conjuntoAlista está definida en el ejercicio # "Transformaciones entre conjuntos y listas" que se encuentra # en https://bit.ly/3RexzxH # 3ª solución # =========== def todos3Aux(p: Callable[[A], bool], c: Conj[A]) -> bool: while not esVacio(c): mc = menor(c) c = elimina(mc, c) if not p(mc): return False return True def todos3(p: Callable[[A], bool], c: Conj[A]) -> bool: _c = deepcopy(c) return todos3Aux(p, _c) # 4ª solución # =========== def todos4Aux(p: Callable[[A], bool], c: Conj[A]) -> bool: while not c.esVacio(): mc = c.menor() c.elimina(mc) if not p(mc): return False return True def todos4(p: Callable[[A], bool], c: Conj[A]) -> bool: _c = deepcopy(c) return todos4Aux(p, _c) # Comprobación de equivalencia de las definiciones # ================================================ # La propiedad es @given(c=conjuntoAleatorio()) def test_todos(c: Conj[int]) -> None: r = todos(lambda x: x % 2 == 0, c) assert todos2(lambda x: x % 2 == 0, c) == r assert todos3(lambda x: x % 2 == 0, c) == r assert todos4(lambda x: x % 2 == 0, c) == r # La comprobación es # src> poetry run pytest -q TAD_TodosVerificanConj.py # 1 passed in 0.28s |
5. TAD de los conjuntos: Algunos elementos verifican una propiedad
Utilizando el tipo abstracto de datos de los conjuntos definir la función
algunos :: Ord a => (a -> Bool) -> Conj a -> Bool |
tal que algunos p c
se verifica si algún elemento de c
verifica el predicado p
. Por ejemplo,
algunos even (inserta 4 (inserta 7 vacio)) == True algunos even (inserta 3 (inserta 7 vacio)) == False |
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
import TAD.Conjunto (Conj, vacio, inserta, esVacio, menor, elimina) import TAD_Transformaciones_conjuntos_listas (conjuntoAlista, listaAconjunto) import Test.QuickCheck.HigherOrder -- 1ª solución -- =========== algunos :: Ord a => (a -> Bool) -> Conj a -> Bool algunos p c | esVacio c = False | otherwise = p mc || algunos p rc where mc = menor c rc = elimina mc c -- 2ª solución -- =========== algunos2 :: Ord a => (a -> Bool) -> Conj a -> Bool algunos2 p c = any p (conjuntoAlista c) -- La función conjuntoAlista está definida en el ejercicio -- "Transformaciones entre conjuntos y listas" que se encuentra -- en https://bit.ly/3RexzxH -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_algunos :: (Int -> Bool) -> [Int] -> Bool prop_algunos p xs = algunos p c == algunos2 p c where c = listaAconjunto xs -- La comprobación es -- λ> quickCheck' prop_algunos -- +++ OK, passed 100 tests. |
from __future__ import annotations from abc import abstractmethod from copy import deepcopy from typing import Callable, Protocol, TypeVar from hypothesis import given from src.TAD.conjunto import (Conj, conjuntoAleatorio, elimina, esVacio, inserta, menor, vacio) from src.TAD_Transformaciones_conjuntos_listas import conjuntoAlista class Comparable(Protocol): @abstractmethod def __lt__(self: A, otro: A) -> bool: pass A = TypeVar('A', bound=Comparable) # 1ª solución # =========== def algunos(p: Callable[[A], bool], c: Conj[A]) -> bool: if esVacio(c): return False mc = menor(c) rc = elimina(mc, c) return p(mc) or algunos(p, rc) # 2ª solución # =========== def algunos2(p: Callable[[A], bool], c: Conj[A]) -> bool: return any(p(x) for x in conjuntoAlista(c)) # La función conjuntoAlista está definida en el ejercicio # "Transformaciones entre conjuntos y listas" que se encuentra # en https://bit.ly/3RexzxH # 3ª solución # =========== def algunos3Aux(p: Callable[[A], bool], c: Conj[A]) -> bool: while not esVacio(c): mc = menor(c) c = elimina(mc, c) if p(mc): return True return False def algunos3(p: Callable[[A], bool], c: Conj[A]) -> bool: _c = deepcopy(c) return algunos3Aux(p, _c) # 4ª solución # =========== def algunos4Aux(p: Callable[[A], bool], c: Conj[A]) -> bool: while not c.esVacio(): mc = c.menor() c.elimina(mc) if p(mc): return True return False def algunos4(p: Callable[[A], bool], c: Conj[A]) -> bool: _c = deepcopy(c) return algunos4Aux(p, _c) # Comprobación de equivalencia de las definiciones # ================================================ # La propiedad es @given(c=conjuntoAleatorio()) def test_algunos(c: Conj[int]) -> None: r = algunos(lambda x: x % 2 == 0, c) assert algunos2(lambda x: x % 2 == 0, c) == r assert algunos3(lambda x: x % 2 == 0, c) == r assert algunos4(lambda x: x % 2 == 0, c) == r # La comprobación es # src> poetry run pytest -q TAD_AlgunosVerificanConj.py # 1 passed in 0.28s |