Ir al contenido principal

Elementos más frecuentes


Definir la función

masFrecuentes :: Ord a => Int -> [a] -> [(Int,a)]

tal que (masFrecuentes n xs) es la lista de los pares formados por n elementos de xs que aparecen más veces junto con el número de veces que aparecen. Por ejemplo,

λ> masFrecuentes 2 "trianera"
[(2,'r'),(2,'a')]
λ> masFrecuentes 2 "interdisciplinariedad"
[(5,'i'),(3,'d')]
λ> masFrecuentes 3 (show (product [1..10000]))
[(5803,'0'),(3416,'2'),(3341,'4')]

Soluciones

import Data.List (group, sort)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck

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

masFrecuentes1 :: Ord a => Int -> [a] -> [(Int,a)]
masFrecuentes1 n xs =
  take n (reverse (sort [(length ys,head ys) | ys <- group (sort xs)]))

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

masFrecuentes2 :: Ord a => Int -> [a] -> [(Int,a)]
masFrecuentes2 n = take n . reverse . sort . cuenta . group . sort
  where cuenta xss = [(length xs,head xs) | xs <- xss]

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

masFrecuentes3 :: Ord a => Int -> [a] -> [(Int,a)]
masFrecuentes3 i =
  take i . reverse . sort . (zip . map length <*> map head) . group . sort

-- En la definición anterior se usa el operador (<*>). Su significado se
-- explica con el siguiente ejemplo,
--    (zip <*> tail) [2,3,5]
--    = zip [2,3,5] (tail [2,3,5])
--    = zip [2,3,5] [3,5]
--    = [(2,3),(3,5)]
--
-- En general,
--    (f <*> g) xs
-- es equivalente a
--    f xs (g xs)
--
-- El significado de (zip . map length <*> map head) se puede apreciar
-- con el siguiente cálculo
--    (zip . map length <*> map head) ["aa","bbb"]
--    = ((zip . map length) <*> (map head)) ["aa","bbb"]
--    = (zip . map length) ["aa","bbb"] (map head ["aa","bbb"])
--    = (zip . map length) ["aa","bbb"] "ab"
--    = zip (map length ["aa","bbb"]) "ab"
--    = zip [2,3] "ab"
--    = [(2,'a'),(3,'b')]

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

masFrecuentes4 :: Ord a => Int -> [a] -> [(Int,a)]
masFrecuentes4 i =
  take i . reverse . sort . (map $ (,) <$> length <*> head) . group . sort

-- En la definición de anterior se ha usado el operador (<$>). Su
-- significado se explica con el siguiente ejemplo,
--    ((+) <$> length) "abc" 4
--    = (+) (length "abc") 4
--    = (+) 3 4
--    = 3 + 4
--    = 7
--
-- También se ha usado el operador (,) para construir pares. Por
-- ejemplo,
--    (,) 3 4 = (3,4)
--
-- El significado de (map $ (,) <$> length <*> head) se puede apreciar
-- con el siguiente cálculo
--    (map $ (,) <$> length <*> head) ["aa","bbb"]
--    = [((,) <$> length <*> head) "aa", ((,) <$> length <*> head) "bbb"]
--    = [(((,) <$> length) <*> head) "aa", (((,) <$> length) <*> head) "bbb"]
--    = [((,) <$> length) "aa" (head "aa"), ((,) <$> length) "bbb" (head "bbb")]
--    = [((,) <$> length) "aa" 'a', ((,) <$> length) "bbb" 'b']
--    = [(,) (length "aa") 'a', (,) (length "bbb") 'b']
--    = [(,) 2 'a', (,) 3 'b']
--    = [(2,'a'),(3,'b')]

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

verifica :: IO ()
verifica = hspec spec

specG :: (Int -> String -> [(Int,Char)]) -> Spec
specG masFrecuentes = do
  it "e1" $
    masFrecuentes 2 "trianera" == [(2,'r'),(2,'a')]
  it "e2" $
    masFrecuentes 2 "interdisciplinariedad" == [(5,'i'),(3,'d')]

spec :: Spec
spec = do
  describe "def. 1" $ specG masFrecuentes1
  describe "def. 2" $ specG masFrecuentes2
  describe "def. 3" $ specG masFrecuentes3
  describe "def. 4" $ specG masFrecuentes4

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

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

-- La propiedad es
prop_equivalencia :: Int -> [Int] -> Bool
prop_equivalencia n xs =
  all (== masFrecuentes1 n xs)
      [masFrecuentes2 n xs,
       masFrecuentes3 n xs,
       masFrecuentes4 n xs]

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

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

-- La comparación es
--    λ> masFrecuentes1 3 (show (product [1..100000]))
--    [(68620,'0'),(43470,'7'),(43275,'2')]
--    (2.00 secs, 9,693,045,704 bytes)
--    λ> masFrecuentes2 3 (show (product [1..100000]))
--    [(68620,'0'),(43470,'7'),(43275,'2')]
--    (1.98 secs, 9,693,046,408 bytes)
--    λ> masFrecuentes3 3 (show (product [1..100000]))
--    [(68620,'0'),(43470,'7'),(43275,'2')]
--    (1.99 secs, 9,693,047,424 bytes)
--    λ> masFrecuentes4 3 (show (product [1..100000]))
--    [(68620,'0'),(43470,'7'),(43275,'2')]
--    (2.02 secs, 9,693,046,808 bytes)