Ir al contenido principal

Producto cartesiano de una familia de conjuntos


Definir la función

producto :: [[a]] -> [[a]]

tal que (producto xss) es el producto cartesiano de los conjuntos xss. Por ejemplo,

λ> producto [[2,5],[6,4]]
[[2,6],[2,4],[5,6],[5,4]]
λ> producto [[1,3],[2,5],[6,4]]
[[1,2,6],[1,2,4],[1,5,6],[1,5,4],[3,2,6],[3,2,4],[3,5,6],[3,5,4]]
λ> producto [[1,3,5],[2,4]]
[[1,2],[1,4],[3,2],[3,4],[5,2],[5,4]]
λ> producto []
[[]]

Comprobar con QuickCheck que para toda lista de listas de enteros, xss, se verifica que el número de elementos de (producto xss) es igual al producto de los números de elementos de cada una de las listas de xss.


Soluciones

import Control.Monad (liftM2)
import Control.Applicative (liftA2)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck (quickCheck)

-- 1ª solución
-- ===========

producto1 :: [[a]] -> [[a]]
producto1 []       = [[]]
producto1 (xs:xss) = [x:ys | x <- xs, ys <- producto1 xss]

-- 2ª solución
-- ===========

producto2 :: [[a]] -> [[a]]
producto2 []       = [[]]
producto2 (xs:xss) = [x:ys | x <- xs, ys <- ps]
  where ps = producto2 xss

-- 3ª solución
-- ===========

producto3 :: [[a]] -> [[a]]
producto3 []       = [[]]
producto3 (xs:xss) = inserta3 xs (producto3 xss)

-- (inserta xs xss) inserta cada elemento de xs en los elementos de
-- xss. Por ejemplo,
--    λ> inserta [1,2] [[3,4],[5,6]]
--    [[1,3,4],[1,5,6],[2,3,4],[2,5,6]]
inserta3 :: [a] -> [[a]] -> [[a]]
inserta3 [] _       = []
inserta3 (x:xs) yss = [x:ys | ys <- yss] ++ inserta3 xs yss

-- 4ª solución
-- ===========

producto4 :: [[a]] -> [[a]]
producto4 = foldr inserta4 [[]]

inserta4 :: [a] -> [[a]] -> [[a]]
inserta4 []     _   = []
inserta4 (x:xs) yss = map (x:) yss ++ inserta4 xs yss

-- 5ª solución
-- ===========

producto5 :: [[a]] -> [[a]]
producto5 = foldr inserta5 [[]]

inserta5 :: [a] -> [[a]] -> [[a]]
inserta5 xs yss = [x:ys | x <- xs, ys <- yss]

-- 6ª solución
-- ===========

producto6 :: [[a]] -> [[a]]
producto6 = foldr inserta6 [[]]

inserta6 :: [a] -> [[a]] -> [[a]]
inserta6 xs yss = concatMap (\x -> map (x:) yss) xs

-- 7ª solución
-- ===========

producto7 :: [[a]] -> [[a]]
producto7 = foldr inserta7 [[]]

inserta7 :: [a] -> [[a]] -> [[a]]
inserta7 xs yss = xs >>= (\x -> map (x:) yss)

-- 8ª solución
-- ===========

producto8 :: [[a]] -> [[a]]
producto8 = foldr inserta8 [[]]

inserta8 :: [a] -> [[a]] -> [[a]]
inserta8 xs yss = (:) <$> xs <*> yss

-- 9ª solución
-- ===========

producto9 :: [[a]] -> [[a]]
producto9 = foldr inserta9 [[]]

inserta9 :: [a] -> [[a]] -> [[a]]
inserta9 = liftA2 (:)

-- 10ª solución
-- ============

producto10 :: [[a]] -> [[a]]
producto10 = foldr (liftM2 (:)) [[]]

-- 11ª solución
-- ============

producto11 :: [[a]] -> [[a]]
producto11 = sequence

-- Verificación
-- ============

verifica :: IO ()
verifica = hspec spec

specG :: ([[Int]] -> [[Int]]) -> Spec
specG producto = do
  it "e1" $
    producto [[1,3],[2,5]]
    `shouldBe` [[1,2],[1,5],[3,2],[3,5]]
  it "e2" $
    producto [[1,3],[2,5],[6,4]]
    `shouldBe` [[1,2,6],[1,2,4],[1,5,6],[1,5,4],[3,2,6],[3,2,4],[3,5,6],[3,5,4]]
  it "e3" $
    producto [[1,3,5],[2,4]]
    `shouldBe` [[1,2],[1,4],[3,2],[3,4],[5,2],[5,4]]

spec :: Spec
spec = do
  describe "def. 1" $ specG producto1
  describe "def. 2" $ specG producto2
  describe "def. 3" $ specG producto3
  describe "def. 4" $ specG producto4
  describe "def. 5" $ specG producto5
  describe "def. 6" $ specG producto6
  describe "def. 7" $ specG producto7
  describe "def. 8" $ specG producto8
  describe "def. 9" $ specG producto9
  describe "def. 10" $ specG producto10
  describe "def. 11" $ specG producto11

-- La verificación es
--    λ> verifica
--    33 examples, 0 failures

-- Comprobación de equivalencia
-- ============================

-- La propiedad es
prop_producto :: [[Int]] -> Bool
prop_producto xss =
  all (== producto1 xss)
      [ producto2 xss
      , producto3 xss
      , producto4 xss
      , producto5 xss
      , producto6 xss
      , producto7 xss
      , producto8 xss
      , producto9 xss
      , producto10 xss
      , producto11 xss
      ]

-- La comprobación es
--    λ> quickCheckWith (stdArgs {maxSize = 9}) prop_producto
--    +++ OK, passed 100 tests.

-- Comparación de eficiencia
-- =========================

-- La comparación es
--    λ> length (producto1 (replicate 7 [0..9]))
--    10000000
--    (10.04 secs, 10,507,268,856 bytes)
--    λ> length (producto2 (replicate 7 [0..9]))
--    10000000
--    (1.71 secs, 1,333,943,632 bytes)
--    λ> length (producto3 (replicate 7 [0..9]))
--    10000000
--    (2.94 secs, 1,956,176,072 bytes)
--    λ> length (producto4 (replicate 7 [0..9]))
--    10000000
--    (1.06 secs, 1,600,616,296 bytes)
--    λ> length (producto5 (replicate 7 [0..9]))
--    10000000
--    (1.77 secs, 1,333,943,248 bytes)
--    λ> length (producto6 (replicate 7 [0..9]))
--    10000000
--    (1.06 secs, 1,600,608,064 bytes)
--    λ> length (producto7 (replicate 7 [0..9]))
--    10000000
--    (0.34 secs, 1,600,607,784 bytes)
--    λ> length (producto8 (replicate 7 [0..9]))
--    10000000
--    (1.03 secs, 978,390,888 bytes)
--    λ> length (producto9 (replicate 7 [0..9]))
--    10000000
--    (1.20 secs, 1,067,273,920 bytes)
--    λ> length (producto10 (replicate 7 [0..9]))
--    10000000
--    (0.58 secs, 2,311,718,360 bytes)
--    λ> length (producto11 (replicate 7 [0..9]))
--    10000000
--    (1.22 secs, 1,067,273,840 bytes)
--
--    λ> length (producto7 (replicate 7 [1..14]))
--    105413504
--    (3.71 secs, 16,347,812,624 bytes)
--    λ> length (producto10 (replicate 7 [1..14]))
--    105413504
--    (5.12 secs, 23,613,234,792 bytes)
--    λ> length (producto11 (replicate 7 [1..14]))
--    105413504
--    (17.83 secs, 10,898,744,528 bytes)

-- Comprobación de la propiedad
-- ============================

-- La propiedad es
prop_longitud :: [[Int]] -> Bool
prop_longitud xss =
  length (producto7 xss) == product (map length xss)

-- La comprobación es
--    λ> quickCheckWith (stdArgs {maxSize = 7}) prop_longitud
--    +++ OK, passed 100 tests.