2011-01-16 1 views
19

Je n'arrive pas à trouver un état d'esprit fonctionnel pour résoudre ce problème d'une manière simple qui pourrait aussi fonctionner pour de très longues listes. Si vous avez une liste comme:Comment trouver le mot le plus long dans la liste?

["one", "two", "three", "four", "five"] 

Je peux dire ce que la longueur du mot le plus long est assez simplement avec:

maximum $ map length ["one", "two", "three", "four", "five"] 

Comment puis-je modifier la déclaration précédente pour retourner la chaîne trois ?

+0

Byers Retour @ Mark la première occurrence du mot le plus long. –

Répondre

37

En utilisant maximumBy, on et compare vous pouvez écrire l'expression comme ceci:

import Data.List (maximumBy) 
import Data.Function (on) 

maximumBy (compare `on` length) ["one", "two", "three", "four", "five"] 
+6

'compare \' sur \ 'longueur' est' comparaison de la longueur' où 'comparaison' vient de' Data.Ord'. – Peaker

+0

Vraiment élégant. Belle solution. –

3

maximumBy(\x -> (x, length x)), fst, et snd dans une composition simple font l'affaire.

12

BTW, si l'on avait pas prêt à l'emploi maximumBy, une façon simple serait le motif undecorate décorez tri/idiome (qui fonctionne dans d'autres langues comme Python ou Scheme, ainsi):

snd $ maximum $ map (\x -> (length x, x)) ["one", "two", "three", "four", "five"] 

Mais puisque la charge utile d'origine fait également partie du type clé en main, le résultat est pas toujours la première occurrence du mot le plus long (dans ce cas, il n'y avait qu'un mot avec la plus grande longueur)

+3

Même avec 'maximumBy', c'est utile. La fonction 'maximumBy' recalculera la longueur (ou la fonction de comparaison utilisée) du jeton courant le plus long à chaque comparaison, alors que decorate-sort-undecorate ne calcule la longueur qu'une seule fois. Cette version est sensiblement plus efficace même avec des entrées de taille modeste. Bien sûr, si vous utilisez des listes de n'importe quelle longueur, vous ne devriez probablement pas utiliser de listes. –

+0

@John Si vous faites un seul passage sur un flux de données, une liste de n'importe quelle longueur est parfaitement bien. Maintenant cordes d'autre part ... – sclv

+0

@John, merci d'avoir souligné le problème de 'maximum (By)' réévaluation 'max', dont je n'étais pas au courant et m'a surpris un peu (mise en œuvre' maximum' par simplement 'foldl1 max' est sans doute élégant néanmoins) – hvr

8

Cette fonction (ou même la bibliothèque) ne semble pas être bien connue, mais Haskell a en fait un module appelé Data.Ord qui contient la fonction comparing qui est presque comme en utilisant Data.Function.on dans la première réponse, sauf que le code se termine plus idiomatique.

g>import Data.Ord 
g>import Data.List 
g>let getLongestElement = maximumBy (comparing length) 
getLongestElement :: [[a]] -> [a] 
g>getLongestElement ["one", "two", "three", "four", "five"] 
"three" 

Le code se lit pratiquement comme l'anglais. "Obtenez le maximum en comparant la longueur."

+0

D'autres ne sont pas d'accord, et pensent que «comparer» est un cas particulier stupide. Je m'en fiche, mais j'ai entendu des gens expérimentés se plaindre à ce sujet. – dfeuer

1

Pour calculer length a, vous devez parcourir toute la liste a. Dans ce cas d'utilisation particulier, vous n'êtes préoccupé que par le mot le plus long, et pas exactement par sa durée, vous pouvez donc écrire une fonction qui ne va pas plus loin que nécessaire dans chaque liste pour déterminer celle qui est la plus longue. Cela peut vous sauver un certain traitement:

module Main where 

main = putStrLn $ longestWordInList ["one", "two", "three", "four"] 

longestWordInList = go "" 
    where go result [] = result 
     go result (x:xs) = let result' = longestWord result x in 
           result' `seq` go result' xs 

longestWord a b = go a b a b 
    where go a _ _ [] = a 
     go _ b [] _ = b 
     go a b (_:as) (_:bs) = go a b as bs 
+0

Vous n'avez pas besoin d'autant d'arguments pour 'go'. Utilisez simplement des noms différents et référez-vous à ceux de la portée externe. 'longestWord un b = aller un b où aller un '_ = a ...' – dfeuer

0
foldl (\accmax xs -> if length accmax < length xs then xs else accmax) [] ["one", "two", "three", "four", "five"] 
Questions connexes