Ir al contenido principal

Triparticiones de una lista


Definir la función

triparticiones :: [a] -> [([a],[a],[a])]

tal que (triparticiones xs) es la lista de las ternas (xs1,xs2,xs3) tales que su concatenación es xs. Por ejemplo,

λ> triparticiones "abc"
[("","","abc"),("","a","bc"),("","ab","c"),("","abc",""),
 ("a","","bc"),("a","b","c"),("a","bc",""),("ab","","c"),
 ("ab","c",""),("abc","","")]
λ> length (triparticiones [1..5000])
12507501

Comprobar con QuickCheck que para cada terna de (triparticiones xs) se cumple que la concatenación de sus elementos es xs.


Soluciones

import Data.List (inits, tails)
import Control.Monad (replicateM)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck

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

triparticiones1 :: [a] -> [([a], [a], [a])]
triparticiones1 xs = [(take i xs, take j (drop i xs), drop (i + j) xs)
                     | i <- [0..n],
                       j <- [0..n-i]]
  where n = length xs

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

triparticiones2 :: [a] -> [([a],[a],[a])]
triparticiones2 xs = [(xs1,xs2,xs3) | (xs1,ys)  <- biparticiones2 xs,
                                      (xs2,xs3) <- biparticiones2 ys]

-- (biparticiones xs) es la lista de los pares (xs1,xs2) tales que su
-- concatenación es xs. Por ejemplo,
--    λ> biparticiones "abc"
--    [("","abc"),("a","bc"),("ab","c"),("abc","")]
biparticiones2 :: [a] -> [([a],[a])]
biparticiones2 []     = [([],[])]
biparticiones2 (x:xs) = ([],x:xs) :
                        [(x:xs1,xs2) | (xs1,xs2) <- biparticiones2 xs]

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

triparticiones3 :: [a] -> [([a],[a],[a])]
triparticiones3 xs = [(xs1,xs2,xs3) | (xs1,ys)  <- biparticiones3 xs,
                                      (xs2,xs3) <- biparticiones3 ys]

biparticiones3 :: [a] -> [([a],[a])]
biparticiones3 xs = zip (inits xs) (tails xs)

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

triparticiones4 :: [a] -> [([a], [a], [a])]
triparticiones4 xs = do
  (xs1, ys)  <- biparticiones3 xs
  (xs2, xs3) <- biparticiones3 ys
  return (xs1, xs2, xs3)

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

triparticiones5 :: [a] -> [([a],[a],[a])]
triparticiones5 xs = [(xs1,xs2,xs3) | (xs1,ys)  <- biparticiones5 xs,
                                      (xs2,xs3) <- biparticiones5 ys]

biparticiones5 :: [a] -> [([a],[a])]
biparticiones5 = zip <$> inits <*> tails

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

verifica :: IO ()
verifica = hspec spec

specG :: (String -> [(String,String,String)]) -> Spec
specG triparticiones = do
  it "e1" $
    triparticiones "abc" `shouldBe`
    [("","","abc"),("","a","bc"),("","ab","c"),("","abc",""),
     ("a","","bc"),("a","b","c"),("a","bc",""),("ab","","c"),
     ("ab","c",""),("abc","","")]

spec :: Spec
spec = do
  describe "def. 1" $ specG triparticiones1
  describe "def. 2" $ specG triparticiones2
  describe "def. 3" $ specG triparticiones3
  describe "def. 4" $ specG triparticiones4
  describe "def. 5" $ specG triparticiones5

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

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

-- -- La propiedad es
prop_equivalencia :: [Int] -> Bool
prop_equivalencia xs =
  all (== triparticiones1 xs)
      [triparticiones2 xs,
       triparticiones4 xs,
       triparticiones5 xs]

-- La comprobación es
--    λ> quickCheck prop_equivalencia
--    +++ OK, passed 100 tests.

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

-- La comparación es
--    λ> :set +s
--    λ> length (triparticiones1 [1..500])
--    125751
--    (0.07 secs, 43,020,520 bytes)
--    λ> length (triparticiones2 [1..500])
--    125751
--    (2.55 secs, 3,088,127,152 bytes)
--    λ> length (triparticiones3 [1..500])
--    125751
--    (0.07 secs, 46,082,480 bytes)
--    λ> length (triparticiones4 [1..500])
--    125751
--    (0.06 secs, 65,201,560 bytes)
--    λ> length (triparticiones5 [1..500])
--    125751
--    (0.08 secs, 46,090,624 bytes)
--
--    λ> length (triparticiones1 [1..4000])
--    8006001
--    (1.94 secs, 2,691,961,264 bytes)
--    λ> length (triparticiones3 [1..4000])
--    8006001
--    (1.84 secs, 2,884,457,240 bytes)
--    λ> length (triparticiones4 [1..4000])
--    8006001
--    (1.56 secs, 4,101,401,376 bytes)
--    λ> length (triparticiones5 [1..4000])
--    8006001
--    (1.79 secs, 2,884,521,384 bytes)

-- Propiedad
-- =========

-- La propiedad es
prop_triparticiones :: [Int] -> Bool
prop_triparticiones xs =
  all (\(xs1,xs2,xs3) -> xs1 ++ xs2 ++ xs3 == xs) (triparticiones1 xs)

-- La comprobación es
--    λ> quickCheck prop_triparticiones
--    +++ OK, passed 100 tests.