Conjuntos de puntos enteros en regiones rectangulares
Los puntos de una cuadrícula se puede representar mediante pares de números enteros
type Punto = (Int,Int)
y las regiones rectangulares mediante el siguiente tipo de dato
data Region = Rectangulo Punto Punto | Union Region Region | Diferencia Region Region deriving (Eq, Show)
donde
-
(Rectangulo p1 p2)es la región formada por un rectángulo cuyo vértice superior izquierdo esp1y su vértice inferior derecho esp2. -
(Union r1 r2)es la región cuyos puntos pertenecen a alguna de las regionesr1yr2. -
(Diferencia r1 r2)es la región cuyos puntos pertenecen a la regiónr1pero no pertenecen a lar2.
Definir la función
puntos :: Region -> [Punto]
tal que (puntos r) es la lista de puntos de la región r. Por ejemplo, usando las regiones definidas por
r0021, r3051, r4162 :: Region r0021 = Rectangulo (0,0) (2,1) r3051 = Rectangulo (3,0) (5,1) r4162 = Rectangulo (4,1) (6,2)
se tiene
λ> puntos r0021 [(0,0),(0,1),(1,0),(1,1),(2,0),(2,1)] λ> puntos r3051 [(3,0),(3,1),(4,0),(4,1),(5,0),(5,1)] λ> puntos r4162 [(4,1),(4,2),(5,1),(5,2),(6,1),(6,2)] λ> puntos (Union r0021 r3051) [(0,0),(0,1),(1,0),(1,1),(2,0),(2,1),(3,0),(3,1),(4,0),(4,1),(5,0),(5,1)] λ> puntos (Diferencia r3051 r4162) [(3,0),(3,1),(4,0),(5,0)] λ> puntos (Union (Diferencia r3051 r4162) r4162) [(3,0),(3,1),(4,0),(5,0),(4,1),(4,2),(5,1),(5,2),(6,1),(6,2)]
Usando la función enRegion, que verifica si un punto pertenece a una región, definida en un ejercicio anterior por
enRegion :: Punto -> Region -> Bool enRegion (x,y) (Rectangulo (x1,y1) (x2,y2)) = x1 <= x && x <= x2 && y1 <= y && y <= y2 enRegion p (Union r1 r2) = enRegion p r1 || enRegion p r2 enRegion p (Diferencia r1 r2) = enRegion p r1 && not (enRegion p r2)
comprobar con QuickCheck que (enRegion p r) es equivalente a (p elem puntos r).
Soluciones
import qualified Data.Set as S import Data.List (nub, union, sort, (\\)) import Test.Hspec (Spec, describe, hspec, it, shouldBe) import Test.QuickCheck type Punto = (Int,Int) data Region = Rectangulo Punto Punto | Union Region Region | Diferencia Region Region deriving (Eq, Show) r0021, r3051, r4162 :: Region r0021 = Rectangulo (0,0) (2,1) r3051 = Rectangulo (3,0) (5,1) r4162 = Rectangulo (4,1) (6,2) -- 1ª solución -- =========== puntos1 :: Region -> [Punto] puntos1 (Rectangulo (x1, y1) (x2, y2)) = [(x, y) | x <- [x1..x2], y <- [y1..y2]] puntos1 (Union r1 r2) = nub (puntos1 r1 ++ puntos1 r2) puntos1 (Diferencia r1 r2) = [p | p <- puntos1 r1, p `notElem` puntos1 r2] -- 2ª solución -- =========== puntos2 :: Region -> [Punto] puntos2 (Rectangulo (x1,y1) (x2,y2)) = [(x,y) | x <- [x1..x2], y <- [y1..y2]] puntos2 (Union r1 r2) = puntos2 r1 `union` puntos2 r2 puntos2 (Diferencia r1 r2) = puntos2 r1 \\ puntos2 r2 -- 3ª solución -- =========== puntos3 :: Region -> [Punto] puntos3 r = S.toList (aux r) where aux (Rectangulo (x1, y1) (x2, y2)) = S.fromList [(x, y) | x <- [x1..x2], y <- [y1..y2]] aux (Union r1 r2) = S.union (aux r1) (aux r2) aux (Diferencia r1 r2) = S.difference (aux r1) (aux r2) -- Verificación -- ============ verifica :: IO () verifica = hspec spec specG :: (Region -> [Punto]) -> Spec specG puntos = do it "e1" $ puntos' r0021 `shouldBe` [(0,0),(0,1),(1,0),(1,1),(2,0),(2,1)] it "e2" $ puntos' r3051 `shouldBe` [(3,0),(3,1),(4,0),(4,1),(5,0),(5,1)] it "e3" $ puntos' r4162 `shouldBe` [(4,1),(4,2),(5,1),(5,2),(6,1),(6,2)] it "e4" $ puntos' (Union r0021 r3051) `shouldBe` [(0,0),(0,1),(1,0),(1,1),(2,0),(2,1),(3,0),(3,1),(4,0),(4,1),(5,0),(5,1)] it "e5" $ puntos' (Diferencia r3051 r4162) `shouldBe` [(3,0),(3,1),(4,0),(5,0)] it "e6" $ puntos' (Union (Diferencia r3051 r4162) r4162) `shouldBe` sort [(3,0),(3,1),(4,0),(5,0),(4,1),(4,2),(5,1),(5,2),(6,1),(6,2)] where puntos' = sort. puntos spec :: Spec spec = do describe "def. 1" $ specG puntos1 describe "def. 2" $ specG puntos2 describe "def. 3" $ specG puntos3 -- La verificación es -- λ> verifica -- 18 examples, 0 failures -- Comprobación de equivalencia -- ============================ -- (regionArbitraria n) es un generador de regiones arbitrarias de orden -- n. Por ejemplo, -- λ> generate (regionArbitraria 2) -- Rectangulo (30,-26) (-2,-8) -- λ> generate (regionArbitraria 2) -- Union (Union (Rectangulo (-2,-5) (6,1)) (Rectangulo(3,7) (11,15))) -- (Diferencia (Rectangulo (9,8) (-2,6)) (Rectangulo (-2,2) (7,8))) regionArbitraria :: Int -> Gen Region regionArbitraria 0 = Rectangulo <$> arbitrary <*> arbitrary regionArbitraria n = oneof [Rectangulo <$> arbitrary <*> arbitrary, Union <$> subregion <*> subregion, Diferencia <$> subregion <*> subregion] where subregion = regionArbitraria (n `div` 2) -- Region está contenida en Arbitrary instance Arbitrary Region where arbitrary = sized regionArbitraria -- La propiedad es prop_equivalencia :: Region -> Bool prop_equivalencia r = all (== sort (puntos1 r)) [sort (puntos2 r), sort (puntos3 r)] -- La comprobación es -- λ> quickCheckWith (stdArgs {maxSize=10}) prop_equivalencia -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= ejRegion :: Int -> Region ejRegion n = foldl1 Union [Rectangulo (i, 0) (i + 5, 1) | i <- [0..n]] -- La comparación es -- λ> :set +s -- λ> length (puntos1 (ejRegion 700)) -- 1412 -- (2.90 secs, 96,433,144 bytes) -- λ> length (puntos2 (ejRegion 700)) -- 1412 -- (0.23 secs, 607,642,872 bytes) -- λ> length (puntos3 (ejRegion 700)) -- 1412 -- (0.03 secs, 5,684,560 bytes) -- -- λ> length (puntos2 (ejRegion 2000)) -- 4012 -- (2.21 secs, 5,362,251,560 bytes) -- λ> length (puntos3 (ejRegion 2000)) -- 4012 -- (0.04 secs, 16,103,704 bytes) -- Propiedad -- ========= -- La propiedad es prop_puntos :: Punto -> Region -> Bool prop_puntos p r = enRegion p r == (p `elem` puntos1 r) enRegion :: Punto -> Region -> Bool enRegion (x,y) (Rectangulo (x1,y1) (x2,y2)) = x1 <= x && x <= x2 && y1 <= y && y <= y2 enRegion p (Union r1 r2) = enRegion p r1 || enRegion p r2 enRegion p (Diferencia r1 r2) = enRegion p r1 && not (enRegion p r2) -- La comprobación es -- λ> quickCheckWith (stdArgs {maxSize=50}) prop_puntos -- +++ OK, passed 100 tests.