Lista cuadrada
Definir la función
listaCuadrada :: Int -> a -> [a] -> [[a]]
tal que (listaCuadrada n x xs)
es una lista de n
listas de longitud n
formadas con los elementos de xs
completada con x
, si no xs
no tiene suficientes elementos. Por ejemplo,
listaCuadrada 3 7 [0,3,5,2,4] == [[0,3,5],[2,4,7],[7,7,7]] listaCuadrada 3 7 [0..] == [[0,1,2],[3,4,5],[6,7,8]] listaCuadrada 2 'p' "eva" == ["ev","ap"] listaCuadrada 2 'p' ['a'..] == ["ab","cd"]
Nota: Escribir las soluciones en Haskell, en Python y en Common Lisp.
Soluciones
A continuación se muestran
- las soluciones en Haskell,
- las soluciones en Python y
- las soluciones en Common Lisp
1. Soluciones en Haskell
module Lista_cuadrada where import Data.List.Split (chunksOf) import Test.Hspec (Spec, describe, hspec, it, shouldBe) import Test.QuickCheck -- 1ª solución -- =========== listaCuadrada1 :: Int -> a -> [a] -> [[a]] listaCuadrada1 n x xs = take n (grupos n (xs ++ repeat x)) -- (grupos n xs) es la lista obtenida agrupando los elementos de xs en -- grupos de n elementos, salvo el último que puede tener menos. Por -- ejemplo, -- grupos 2 [4,2,5,7,6] == [[4,2],[5,7],[6]] -- take 3 (grupos 3 [1..]) == [[1,2,3],[4,5,6],[7,8,9]] grupos :: Int -> [a] -> [[a]] grupos _ [] = [] grupos n xs = take n xs : grupos n (drop n xs) -- 2ª solución -- =========== listaCuadrada2 :: Int -> a -> [a] -> [[a]] listaCuadrada2 n x xs = take n (grupos2 n (xs ++ repeat x)) grupos2 :: Int -> [a] -> [[a]] grupos2 _ [] = [] grupos2 n xs = ys : grupos n zs where (ys,zs) = splitAt n xs -- 3ª solución -- =========== listaCuadrada3 :: Int -> a -> [a] -> [[a]] listaCuadrada3 n x xs = take n (chunksOf n (xs ++ repeat x)) -- Verificación -- ============ verifica :: IO () verifica = hspec spec specG :: (Int -> Int -> [Int] -> [[Int]]) -> Spec specG listaCuadrada = do it "e1" $ listaCuadrada 3 7 [0,3,5,2,4] `shouldBe` [[0,3,5],[2,4,7],[7,7,7]] it "e2" $ listaCuadrada 3 7 [0..] `shouldBe` [[0,1,2],[3,4,5],[6,7,8]] spec :: Spec spec = do describe "def. 1" $ specG listaCuadrada1 describe "def. 2" $ specG listaCuadrada2 describe "def. 3" $ specG listaCuadrada3 -- La verificación es -- λ> verifica -- -- 6 examples, 0 failures -- Comprobación de la equivalencia -- =============================== -- La propiedad es prop_listaCuadrada :: Int -> Int -> [Int] -> Bool prop_listaCuadrada n x xs = all (== listaCuadrada1 n x xs) [listaCuadrada2 n x xs, listaCuadrada3 n x xs] -- La comprobación es -- λ> quickCheck prop_listaCuadrada -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> length (listaCuadrada1 (10^4) 5 [1..]) -- 10000 -- (2.02 secs, 12,801,918,616 bytes) -- λ> length (listaCuadrada2 (10^4) 5 [1..]) -- 10000 -- (1.89 secs, 12,803,198,576 bytes) -- λ> length (listaCuadrada3 (10^4) 5 [1..]) -- 10000 -- (1.85 secs, 12,801,518,728 bytes)
2. Soluciones en Python
from itertools import chain, islice, repeat from sys import setrecursionlimit from timeit import Timer, default_timer from typing import TypeVar from hypothesis import given from hypothesis import strategies as st from more_itertools import chunked setrecursionlimit(10**6) A = TypeVar('A') # 1ª solución # =========== # grupos(n, xs) es la lista obtenida agrupando los elementos de xs en # grupos de n elementos, salvo el último que puede tener menos. Por # ejemplo, # >>> grupos(2, [4,2,5,7,6]) # [[4, 2], [5, 7], [6]] def grupos(n: int, xs: list[A]) -> list[list[A]]: if not xs: return [] return [list(islice(xs, n))] + grupos(n, xs[n:]) def listaCuadrada1(n: int, x: A, xs: list[A]) -> list[list[A]]: return grupos(n, list(islice(chain(xs, repeat(x)), n * n))) # 2ª solución # =========== def grupos2(n: int, xs: list[A]) -> list[list[A]]: if not xs: return [] ys, zs = xs[:n], xs[n:] return [ys] + grupos(n, zs) def listaCuadrada2(n: int, x: A, xs: list[A]) -> list[list[A]]: return grupos2(n, list(islice(chain(xs, repeat(x)), n * n))) # 3ª solución # =========== def listaCuadrada3(n: int, x: A, xs: list[A]) -> list[list[A]]: return list(chunked(list(islice(chain(xs, repeat(x)), n * n)), n)) # listaCuadrada3 :: Int -> a -> [a] -> [[a]] # listaCuadrada3 n x xs = # take n (chunksOf n (xs ++ repeat x)) # Verificación # ============ def test_listaCuadrada() -> None: for listaCuadrada in [listaCuadrada1, listaCuadrada2, listaCuadrada3]: assert listaCuadrada(3, 7, [0,3,5,2,4]) == [[0, 3, 5], [2, 4, 7], [7, 7, 7]] print(f"Verificado {listaCuadrada.__name__}") # La verificación es # >>> test_listaCuadrada() # Verificado listaCuadrada1 # Verificado listaCuadrada2 # Verificado listaCuadrada3 # Comprobación de la equivalencia # =============================== # La propiedad es @given(st.integers(min_value=1, max_value=100), st.integers(), st.lists(st.integers(), max_size=100)) def test_listaCuadrada_equiv(n: int, x: int, xs: list[int]) -> None: r = listaCuadrada1(n, x, xs) assert listaCuadrada2(n, x, xs) == r assert listaCuadrada3(n, x, xs) == r # La comprobación es # >>> test_listaCuadrada_equiv() # >>> # 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('listaCuadrada1(10**3, 5, range(100))') # 3.55 segundos # >>> tiempo('listaCuadrada2(10**3, 5, range(100))') # 3.44 segundos # >>> tiempo('listaCuadrada3(10**3, 5, range(100))') # 0.06 segundos
3. Soluciones en Common Lisp
(ql:quickload "fiveam" :silent t) (ql:quickload "serapeum" :silent t) (defpackage :lista-cuadrada (:use :cl :fiveam) (:import-from :serapeum batches)) (in-package :lista-cuadrada) ;;; 1ª solución ;;; =========== ;;; (take n xs) es la lista de los n primeros elementos de xs. Por ;;; ejemplo, ;;; > (take 2 '(4 2 5 7 6)) ;;; (4 2) (defun take (n xs) (cond ((= n 0) ()) ((null xs) ()) (t (cons (first xs) (take (- n 1) (rest xs)))))) ;;; (drop n xs) es la lista obtenida eliminando los n primeros elementos ;;; de xs. Por ejemplo, ;;; > (drop 2 '(4 2 5 7 6)) ;;; (5 7 6) (defun drop (n xs) (cond ((= n 0) xs) ((null xs) ()) (t (drop (- n 1) (rest xs))))) ;;; (grupos n xs) es la lista obtenida agrupando los elementos de xs en ;;; grupos de n elementos, salvo el último que puede tener menos. Por ;;; ejemplo, ;;; > (grupos 2 '(4 2 5 7 6)) ;;; ((4 2) (5 7) (6)) (defun grupos (n xs) (if (null xs) () (cons (take n xs) (grupos n (drop n xs))))) (defun lista-cuadrada1 (n x xs) (take n (grupos n (append xs (make-list (max 0 (- (* n n) (length xs))) :initial-element x))))) ;;; 2ª solución ;;; =========== ;;; (split-at n xs) son las listas de los n elementos de xs y lo que ;;; queda de xs al eliminar dichos elementos. Por ejemplo, ;;; > (split-at 0 '(3 4 5 6 7)) ;;; NIL ;;; (3 4 5 6 7) ;;; > (split-at 2 '(3 4 5 6 7)) ;;; (3 4) ;;; (5 6 7) ;;; > (split-at 9 '(3 4 5 6 7)) ;;; (3 4 5 6 7) ;;; NIL (defun split-at (n xs) (defun aux (n ys zs) (if (or (= n 0) (null zs)) (values (nreverse ys) zs) (aux (1- n) (cons (first zs) ys) (rest zs)))) (aux n () xs)) ;;; (grupos n xs) es la lista obtenida agrupando los elementos de xs en ;;; grupos de n elementos, salvo el último que puede tener menos. Por ;;; ejemplo, ;;; > (grupos2 2 '(4 2 5 7 6)) ;;; ((4 2) (5 7) (6)) (defun grupos2 (n xs) (if (null xs) () (multiple-value-bind (ys zs) (split-at n xs) (cons ys (grupos2 n zs))))) (defun lista-cuadrada2 (n x xs) (take n (grupos2 n (append xs (make-list (max 0 (- (* n n) (length xs))) :initial-element x))))) ;;; 3ª solución ;;; =========== ;;; (take-iter n xs) es la lista de los n primeros elementos de xs. Por ;;; ejemplo, ;;; > (take-iter 2 '(4 2 5 7 6)) ;;; (4 2) ;;; > (take-iter 9 '(4 2 5 7 6)) ;;; (4 2 5 7 6) (defun take-iter (n xs) (loop for i below n for x in xs collect x)) ;;; (drop-iter n xs) es la lista obtenida eliminando los n primeros elementos ;;; de xs. Por ejemplo, ;;; > (drop-iter 2 '(4 2 5 7 6)) ;;; (5 7 6) ;;; > (drop-iter 9 '(4 2 5 7 6)) ;;; NIL (defun drop-iter (n xs) (loop for i below (1+ n) for x on xs finally (return x))) ;;; (grupos-iter n xs) es la lista obtenida agrupando los elementos de xs en ;;; grupos de n elementos, salvo el último que puede tener menos. Por ;;; ejemplo, ;;; > (grupos-iter 2 '(4 2 5 7 6)) ;;; ((4 2) (5 7) (6)) (defun grupos-iter (n xs) (let ((result '())) (loop while xs do (push (take-iter n xs) result) (setf xs (drop-iter n xs))) (nreverse result))) (defun lista-cuadrada3 (n x xs) (take-iter n (grupos-iter n (append xs (make-list (max 0 (- (* n n) (length xs))) :initial-element x))))) ;;; 4ª solución ;;; =========== (defun lista-cuadrada4 (n x xs) (take n (serapeum:batches (append xs (make-list (max 0 (- (* n n) (length xs))) :initial-element x)) n))) ;;; Verificación ;;; ============ (test lista-cuadrada (mapc (lambda (lista-cuadrada) (is (equal (funcall lista-cuadrada 3 7 '(0 3 5 2 4)) '((0 3 5) (2 4 7) (7 7 7)))) (is (equal (funcall lista-cuadrada 2 7 '(0 3 5 2 4)) '((0 3) (5 2))))) '(lista-cuadrada1 lista-cuadrada2 lista-cuadrada3 lista-cuadrada4 ))) (defun verifica () (run 'lista-cuadrada)) ;;; La verificación es ;;; > (verifica) ;;; ;;; Running test LISTA-CUADRADA ........ ;;; Comprobación de la equivalencia ;;; =============================== ;;; La propiedad es (test lista-cuadrada-equiv (for-all ((n (gen-integer :min 1 :max 10)) (x (gen-integer :min 1 :max 10)) (xs (gen-list))) (let ((ys (lista-cuadrada1 n x xs))) (is (equal ys (lista-cuadrada2 n x xs))) (is (equal ys (lista-cuadrada3 n x xs))) (is (equal ys (lista-cuadrada4 n x xs))) ))) (defun comprueba () (run 'lista-cuadrada-equiv)) ;;; La comprobación es ;;; > (comprueba) ;;; ;;; Running test LISTA-CUADRADA-EQUIV ... ;;; Comparación de eficiencia ;;; ========================= ;;; La comparación es ;;; > (time (lista-cuadrada1 2000 5 (make-list 100 :initial-element 0))) (values) ;;; 0.322 seconds of real time ;;; > (time (lista-cuadrada2 2000 5 (make-list 100 :initial-element 0))) (values) ;;; 0.192 seconds of real time ;;; (time (lista-cuadrada3 2000 5 (make-list 100 :initial-element 0))) (values) ;;; 0.207 seconds of real time ;;; > (time (lista-cuadrada3 2000 5 (make-list 100 :initial-element 0))) (values) ;;; 0.155 seconds of real time