Sopa de letras
Las matrices se puede representar mediante tablas cuyos índices son pares de números naturales:
type Matriz a = Array (Int,Int) a
Definir la función
enLaSopa :: Eq a => [a] -> Matriz a -> Bool
tal que (enLaSopa c p) se verifica si c está en la matriz p en horizontal o en vertical. Por ejemplo, si ej1 es la matriz siguiente:
ej1 :: Matriz Char ej1 = listaMatriz ["mjtholueq", "juhoolauh", "dariouhyj", "rngkploaa"]
donde la función listaMatriz
está definida por
listaMatriz :: [[a]] -> Matriz a listaMatriz xss = listArray ((1,1),(m,n)) (concat xss) where m = length xss n = length (head xss)
entonces,
enLaSopa "dar" p == True -- En horizontal a la derecha en la 3ª fila enLaSopa "oir" p == True -- En horizontal a la izquierda en la 3ª fila enLaSopa "juan" p == True -- En vertical descendente en la 2ª columna enLaSopa "kio" p == True -- En vertical ascendente en la 3ª columna enLaSopa "Juan" p == False enLaSopa "hola" p == False
Soluciones
import Data.Array (Array, (!), bounds, listArray) import Data.List (isInfixOf, transpose) import Test.Hspec (Spec, describe, hspec, it, shouldBe) import Test.QuickCheck type Matriz a = Array (Int,Int) a listaMatriz :: [[a]] -> Matriz a listaMatriz xss = listArray ((1,1),(m,n)) (concat xss) where m = length xss n = length (head xss) ej1 :: Matriz Char ej1 = listaMatriz ["mjtholueq", "juhoolauh", "dariouhyj", "rngkploaa"] -- 1ª solución -- =========== enLaSopa1 :: Eq a => [a] -> Matriz a -> Bool enLaSopa1 c p = or [c `isInfixOf` xs | xs <- [[p!(i,j) | j <- [1..n]] | i <- [1..m]] ++ [[p!(i,j) | j <- [n,n-1..1]] | i <- [1..m]] ++ [[p!(i,j) | i <- [1..m]] | j <- [1..n]] ++ [[p!(i,j) | i <- [m,m-1..1]] | j <- [1..n]]] where (_,(m,n)) = bounds p -- 2ª solución -- =========== enLaSopa2 :: Eq a => [a] -> Matriz a -> Bool enLaSopa2 c p = estaEnHorizontal c p || estaEnVertical c p -- (numFilas p) es el número de filas de la matriz p. Por ejemplo, -- numFilas ej1 == 4 numFilas :: Matriz a -> Int numFilas = fst . snd . bounds -- (numColumnas p) es el número de columnas de la matriz p. Por ejemplo, -- numColumnas ej1 == 9 numColumnas :: Matriz a -> Int numColumnas = snd . snd . bounds -- (fila i p) es la fila i-ésima de la matriz p. Por ejemplo, -- fila 2 ej1 == "juhoolauh" fila :: Int -> Matriz a -> [a] fila i p = [p!(i,j) | j <- [1..n]] where n = numColumnas p -- (columna j p) es la columna j-ésima de la matriz p. Por ejemplo, -- columna 2 ej1 == "juan" columna :: Int -> Matriz a -> [a] columna j p = [p!(i,j) | i <- [1..m]] where m = numFilas p -- (filas p) es la lista de las filas de la matriz p. Por ejemplo, -- λ> filas ej1 -- ["mjtholueq","juhoolauh","dariouhyj","rngkploaa"] filas :: Matriz a -> [[a]] filas p = [fila i p | i <- [1..numFilas p]] -- (columnas p) es la lista de las columnas de la matriz p. Por ejemplo, -- λ> columnas ej1 -- ["mjdr","juan","thrg","hoik","ooop","llul","uaho","euya","qhja"] columnas :: Matriz a -> [[a]] columnas p = [columna j p | j <- [1..numColumnas p]] -- (estaEnHorizontal c p) se verifica si c está en la matriz p en -- horizontal. Por ejemplo, -- estaEnHorizontal "dar" ej1 == True -- estaEnHorizontal "oir" ej1 == True -- estaEnHorizontal "juan" ej1 == False estaEnHorizontal :: Eq a => [a] -> Matriz a -> Bool estaEnHorizontal c p = or [c `isInfixOf` xs | xs <- filas p ++ map reverse (filas p)] -- (estaEnVertical c p) se verifica si c está en la matriz p en -- vertical. Por ejemplo, -- estaEnVertical "juan" ej1 == True -- estaEnVertical "kio" ej1 == True -- estaEnVertical "dar" ej1 == False estaEnVertical :: Eq a => [a] -> Matriz a -> Bool estaEnVertical c p = or [c `isInfixOf ` xs | xs <- columnas p ++ map reverse (columnas p)] -- 3ª solución -- =========== enLaSopa3 :: Eq a => [a] -> Matriz a -> Bool enLaSopa3 c p = any (c `isInfixOf`) lineas where fs = filas p cs = transpose fs lineas = fs ++ map reverse fs ++ cs ++ map reverse cs -- Verificación -- ============ verifica :: IO () verifica = hspec spec specG :: ([Char] -> Matriz Char -> Bool) -> Spec specG enLaSopa = do it "e1" $ enLaSopa "dar" ej1 `shouldBe` True it "e2" $ enLaSopa "oir" ej1 `shouldBe` True it "e3" $ enLaSopa "juan" ej1 `shouldBe` True it "e4" $ enLaSopa "kio" ej1 `shouldBe` True it "e5" $ enLaSopa "Juan" ej1 `shouldBe` False it "e6" $ enLaSopa "hola" ej1 `shouldBe` False spec :: Spec spec = do describe "def. 1" $ specG enLaSopa1 describe "def. 2" $ specG enLaSopa2 describe "def. 3" $ specG enLaSopa3 -- La verificación es -- λ> verifica -- 18 examples, 0 failures -- Equivalencia de las definiciones -- ================================ newtype Matriz2 = M (Array (Int,Int) Int) deriving Show -- Generador de matrices arbitrarias. Por ejemplo, -- λ> generate matrizArbitraria -- M (array ((1,1),(3,4)) -- [((1,1),18),((1,2),6), ((1,3),-23),((1,4),-13), -- ((2,1),-2),((2,2),22),((2,3),-25),((2,4),-5), -- ((3,1),2), ((3,2),16),((3,3),-15),((3,4),7)]) matrizArbitraria :: Gen Matriz2 matrizArbitraria = do m <- chooseInt (1,10) n <- chooseInt (1,10) xs <- vectorOf (m*n) arbitrary return (M (listArray ((1,1),(m,n)) xs)) -- Matriz es una subclase de Arbitrary. instance Arbitrary Matriz2 where arbitrary = matrizArbitraria -- La propiedad es prop_enLaSopa :: [Int] -> Matriz2 -> Bool prop_enLaSopa xs (M p) = all (== enLaSopa1 xs p) [enLaSopa2 xs p, enLaSopa3 xs p] -- La comprobación es -- λ> quickCheck prop_enLaSopa -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> ejemplo = listaMatriz (replicate 1000 (replicate 1000 'a')) -- λ> enLaSopa1 "b" ejemplo -- False -- (1.88 secs, 1,458,976,016 bytes) -- λ> enLaSopa2 "b" ejemplo -- False -- (1.72 secs, 1,491,736,440 bytes) -- λ> enLaSopa3 "b" ejemplo -- False -- (0.70 secs, 553,799,536 bytes)