Elementos adicionales
Definir la función
adicionales :: Ord a => Int -> [a] -> [a] -> [a]
tal que (adicionales n xs ys) es la lista de los n elementos de xs que no pertenecen a ys (se supone que n es el número de elementos de xs que no pertenecen a ys, que las listas xs e ys están estrictamente ordenadas y que pueden ser infinitas). Por ejemplo,
adicionales 0 [1,3] [1,3] == [] adicionales 1 [1,3] [1] == [3] adicionales 2 [1,3,5] [1] == [3,5] adicionales 2 [1,3,5,7,9] [1,5,7] == [3,9] adicionales 2 ([1,3,5]++[7..]) ([1]++[7..]) == [3,5]
Soluciones
import Data.List ((\\), nub, sort) import Data.List.Ordered (minus) import Test.Hspec (Spec, describe, hspec, it, shouldBe) import Test.QuickCheck -- 1ª solución -- =========== adicionales1 :: Ord a => Int -> [a] -> [a] -> [a] adicionales1 0 _ _ = [] adicionales1 _ xs [] = xs adicionales1 n (x:xs) (y:ys) | x < y = x : adicionales1 (n-1) xs (y:ys) | x == y = adicionales1 n xs ys | otherwise = adicionales1 n (x:xs) ys -- 2ª solución -- =========== adicionales2 :: Ord a => Int -> [a] -> [a] -> [a] adicionales2 n xs ys = take n [x | x <- xs, x `noPertenece` ys] -- (noPertenece x ys) se verifica si x no pertenece a la lista ordenada -- (posiblemente infinita ys). Por ejemplo. -- noPertenece 2 [3,5] == True -- noPertenece 4 [3,5] == True -- noPertenece 7 [3,5] == True -- noPertenece 4 [3,5..] == True noPertenece :: Ord a => a -> [a] -> Bool noPertenece x ys = null zs || head zs /= x where zs = dropWhile (<x) ys -- 3ª solución -- =========== adicionales3 :: Ord a => Int -> [a] -> [a] -> [a] adicionales3 n xs ys = take n (minus xs ys) -- Verificación -- ============ verifica :: IO () verifica = hspec spec specG :: (Int -> [Int] -> [Int] -> [Int]) -> Spec specG adicionales = do it "e1" $ adicionales 0 [1,3] [1,3] `shouldBe` [] it "e2" $ adicionales 1 [1,3] [1] `shouldBe` [3] it "e3" $ adicionales 2 [1,3,5] [1] `shouldBe` [3,5] it "e4" $ adicionales 2 [1,3,5,7,9] [1,5,7] `shouldBe` [3,9] it "e5" $ adicionales 2 [1,3,5,7,9] [1,5] `shouldBe` [3,7] it "e6" $ adicionales 2 ([1,3,5]++[7..]) (1 : [7..]) `shouldBe` [3,5] spec :: Spec spec = do describe "def. 1" $ specG adicionales1 describe "def. 2" $ specG adicionales2 describe "def. 3" $ specG adicionales3 -- La verificación es -- λ> verifica -- 18 examples, 0 failures -- Comprobación de equivalencia -- ============================ -- genEjemplo es un generador de ejemplos de entrada. Por ejemplo, -- λ> generate genEjemplo -- (3,[-18,-13,-9,-1,12,16],[-13,-9,-1]) genEjemplo :: Gen (Int, [Int], [Int]) genEjemplo = do n <- choose (0,5) m <- choose (n,10) as <- vectorOf n arbitrary bs <- vectorOf m arbitrary let as' = nub as bs' = nub bs n' = length as' ys = sort (bs' \\ as') xs = sort (as' ++ ys) return (n', xs, ys) -- La propiedad es prop_equivalencia :: Property prop_equivalencia = forAll genEjemplo $ \(n, xs, ys) -> adicionales1 n xs ys == adicionales2 n xs ys -- La comprobación es -- λ> quickCheck prop_equivalencia -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> adicionales1 1 ([1..6000]++[0]) [1..6000] -- [0] -- (0.01 secs, 3,142,040 bytes) -- λ> adicionales2 1 ([1..6000]++[0]) [1..6000] -- [0] -- (1.50 secs, 5,254,744 bytes)