Aplica según propiedad
Definir la función
filtraAplica :: (a -> b) -> (a -> Bool) -> [a] -> [b]
tal que filtraAplica f p xs
es la lista obtenida aplicándole a los elementos de xs
que cumplen el predicado p
la función f
. Por ejemplo,
filtraAplica (4+) (<3) [1..7] == [5,6]
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
Soluciones en Haskell
import Test.QuickCheck.HigherOrder (quickCheck') -- 1ª solución -- =========== filtraAplica1 :: (a -> b) -> (a -> Bool) -> [a] -> [b] filtraAplica1 f p xs = [f x | x <- xs, p x] -- 2ª solución -- =========== filtraAplica2 :: (a -> b) -> (a -> Bool) -> [a] -> [b] filtraAplica2 f p xs = map f (filter p xs) -- 3ª solución -- =========== filtraAplica3 :: (a -> b) -> (a -> Bool) -> [a] -> [b] filtraAplica3 _ _ [] = [] filtraAplica3 f p (x:xs) | p x = f x : filtraAplica3 f p xs | otherwise = filtraAplica3 f p xs -- 4ª solución -- =========== filtraAplica4 :: (a -> b) -> (a -> Bool) -> [a] -> [b] filtraAplica4 f p = foldr g [] where g x y | p x = f x : y | otherwise = y -- 5ª solución -- =========== filtraAplica5 :: (a -> b) -> (a -> Bool) -> [a] -> [b] filtraAplica5 f p = foldr (\x y -> if p x then f x : y else y) [] -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_filtraAplica :: (Int -> Int) -> (Int -> Bool) -> [Int] -> Bool prop_filtraAplica f p xs = all (== filtraAplica1 f p xs) [filtraAplica2 f p xs, filtraAplica3 f p xs, filtraAplica4 f p xs, filtraAplica5 f p xs] -- La comprobación es -- λ> quickCheck' prop_filtraAplica -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> sum (filtraAplica1 id even [1..5*10^6]) -- 6250002500000 -- (2.92 secs, 1,644,678,696 bytes) -- λ> sum (filtraAplica2 id even [1..5*10^6]) -- 6250002500000 -- (1.17 secs, 1,463,662,848 bytes) -- λ> sum (filtraAplica3 id even [1..5*10^6]) -- 6250002500000 -- (3.18 secs, 1,964,678,640 bytes) -- λ> sum (filtraAplica4 id even [1..5*10^6]) -- 6250002500000 -- (2.64 secs, 1,924,678,752 bytes) -- λ> sum (filtraAplica5 id even [1..5*10^6]) -- 6250002500000 -- (2.61 secs, 1,824,678,712 bytes)
Soluciones en Python
from functools import reduce from sys import setrecursionlimit from timeit import Timer, default_timer from typing import Callable, TypeVar from hypothesis import given from hypothesis import strategies as st setrecursionlimit(10**6) A = TypeVar('A') B = TypeVar('B') # 1ª solución # =========== def filtraAplica1(f: Callable[[A], B], p: Callable[[A], bool], xs: list[A]) -> list[B]: return [f(x) for x in xs if p(x)] # 2ª solución # =========== def filtraAplica2(f: Callable[[A], B], p: Callable[[A], bool], xs: list[A]) -> list[B]: return list(map(f, filter(p, xs))) # 3ª solución # =========== def filtraAplica3(f: Callable[[A], B], p: Callable[[A], bool], xs: list[A]) -> list[B]: if not xs: return [] if p(xs[0]): return [f(xs[0])] + filtraAplica3(f, p, xs[1:]) return filtraAplica3(f, p, xs[1:]) # 4ª solución # =========== def filtraAplica4(f: Callable[[A], B], p: Callable[[A], bool], xs: list[A]) -> list[B]: def g(ys: list[B], x: A) -> list[B]: if p(x): return ys + [f(x)] return ys return reduce(g, xs, []) # 5ª solución # =========== def filtraAplica5(f: Callable[[A], B], p: Callable[[A], bool], xs: list[A]) -> list[B]: r = [] for x in xs: if p(x): r.append(f(x)) return r # Comprobación de equivalencia # ============================ # La propiedad es @given(st.lists(st.integers())) def test_filtraAplica(xs: list[int]) -> None: def f(x: int) -> int: return x + 4 def p(x: int) -> bool: return x < 3 r = filtraAplica1(f, p, xs) assert filtraAplica2(f, p, xs) == r assert filtraAplica3(f, p, xs) == r assert filtraAplica4(f, p, xs) == r assert filtraAplica5(f, p, xs) == r # La comprobación es # src> poetry run pytest -q aplica_segun_propiedad.py # 1 passed in 0.25s # Comparación de eficiencia # ========================= def tiempo(e: str) -> None: """Tiempo (en segundos) de evaluar la expresión e.""" t = Timer(e, "", default_timer, globals()).timeit(1) print(f"{t:0.2f} segundos") # La comparación es # >>> tiempo('filtraAplica1(lambda x: x, lambda x: x % 2 == 0, range(10**5))') # 0.02 segundos # >>> tiempo('filtraAplica2(lambda x: x, lambda x: x % 2 == 0, range(10**5))') # 0.01 segundos # >>> tiempo('filtraAplica3(lambda x: x, lambda x: x % 2 == 0, range(10**5))') # Process Python violación de segmento (core dumped) # >>> tiempo('filtraAplica4(lambda x: x, lambda x: x % 2 == 0, range(10**5))') # 4.07 segundos # >>> tiempo('filtraAplica5(lambda x: x, lambda x: x % 2 == 0, range(10**5))') # 0.01 segundos # # >>> tiempo('filtraAplica1(lambda x: x, lambda x: x % 2 == 0, range(10**7))') # 1.66 segundos # >>> tiempo('filtraAplica2(lambda x: x, lambda x: x % 2 == 0, range(10**7))') # 1.00 segundos # >>> tiempo('filtraAplica5(lambda x: x, lambda x: x % 2 == 0, range(10**7))') # 1.21 segundos