2009-07-07 7 views
3

Je suis un programmeur Java qui apprend Haskell. J'ai écrit un petit programme qui recherche dans les fichiers des mots avec un suffixe particulier.Comment rendre ce code plus compact et plus lisible?

J'aimerais lire votre critique. Que proposeriez-vous pour rendre ce code plus compact et plus lisible?

module Main where 

import Control.Monad 
import Data.String.Utils 
import Data.List 
import Data.Char 
import System.Directory 
import System.FilePath 
import System.IO 
import System.IO.HVFS.Utils 
import Text.Regex 

alphaWords :: String -> [String] 
alphaWords = words . map (\c -> if isAlpha c then c else ' ') -- by ephemient 
-- was: 
-- words2 s = case dropWhile isSpace2 s of 
--  "" -> [] 
--  ss -> w : words2 sss 
--   where (w, sss) = break isSpace2 ss 
--  where isSpace2 = not . isAlpha 

findFiles :: FilePath -> IO [FilePath] 
findFiles path = do 
    cur_path <- getCurrentDirectory 
    files <- recurseDir SystemFS $ normalise $ combine cur_path path 
    filterM doesFileExist files 

wordsWithSuffix :: String -> String -> [String] 
wordsWithSuffix suffix text = 
    let tokens = (nub . alphaWords) text 
     endswithIgnoringCase = endswith suffix . map toLower 
    in filter endswithIgnoringCase tokens 

searchWords :: String -> String -> [String] -> IO [String] 
searchWords suffix path exts = do 
    let isSearchable = (`elem` exts) . takeExtension -- by yairchu 
    --was let isSearchable s = takeExtension s `elem` exts 

    --files <- filterM (fmap isSearchable) $ findFiles path -- by ephemient (compile error) 
    files <- liftM (filter isSearchable) $ findFiles path 

    wordsPerFile <- forM files $ fmap (wordsWithSuffix suffix) . readFile -- by ephemient 
    -- was: wordsPerFile <- forM files (\x -> liftM (wordsWithSuffix suffix) (readFile x)) 

    return . sort . nub $ concat wordsPerFile -- by ephemient 
    -- was: return $ (sort . nub . concat) wordsPerFile 

main = do 
    words <- searchWords "tick" "/path/to/src" [".as", ".java", ".mxml"] 
    print $ length words 
    putStrLn $ unlines words 

MISE À JOUR: J'ai corrigé 2 points verbeux trouvés en utilisant "hlint", thanks
MISE À JOUR 2: Plus correctifs. Merci @ephemient
MISE À JOUR 3: Un petit correctif. Merci @yairchu, ne peut pas utiliser tout votre code - trop difficile pour l'esprit d'un développeur Java

+1

pas une vraie question? – maxwellb

+2

Est-ce que ce sont les devoirs? –

+1

Essayez d'écrire une implémentation, puis demandez de l'aide pour optimiser plutôt que d'essayer d'amener les autres à le faire pour vous. Sinon, je suggère d'embaucher un programmeur Haskell pour développer cela pour vous. – Lazarus

Répondre

4

Ne pas import System.FilePath.Posix si vous n'avez pas besoin. System.FilePath exporte System.FilePath.Posix ou System.FilePath.Windows en fonction de la plateforme sur laquelle vous compilez.

Votre mise en œuvre words2 est très bien, mais n'a aucune explication quant à pourquoi il fait ce qu'il fait. Ceci est plus explicite, et la différence d'efficacité n'est pas significative.

alphaWords = words . map (\c -> if isAlpha c then c else ' ') 

petites améliorations dans searchWords:

- wordsPerFile <- forM files (\x -> 
-  liftM (wordsWithSuffix suffix) (readFile x)) 
+ wordsPerFile <- forM files $ fmap (wordsWithSuffix suffix) . readFile 
- return $ (sort . nub . concat) wordsPerFile 
+ return . sort . nub $ concat wordsPerFile 

annotations de type à l'intérieur let constructions ne sont pas communs à moins que le typechecker a vraiment besoin de l'aide ... mais si je fait attention à eux, je ne l'aurais pas fait ma précédente erreur d'essayer de déplacer isSearchable :)

en outre, dans main, je changerais ceci:

- putStrLn $ unlines words 
+ mapM_ putStrLn words 

Je ne connais pas les modules exposés par MissingH; est System.IO.HVFS.Utils.recurseDir paresseux? Si ce n'est pas le cas, l'ajout de System.IO.Unsafe.unsafeInterleaveIO peut aider avec la consommation de mémoire lors de la traversée d'une grande arborescence de répertoires.

+0

Problème ici: "files <- filterM isSearchable $ findFiles chemin" le type est "filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]" mais attendu être "filterM :: (Monad m) => (a -> Bool) -> m [a] -> m [a]" – oshyshko

+0

Ah, je ne l'ai pas compilé-test. Il devrait être 'filterM (fmap isSearchable)', ce qui le rend préférable. – ephemient

+0

Modifié dans "files <- filterM (fmap isSearchable) $ findFiles chemin" - got "N'a pas pu correspondre au type attendu' [m FilePath] 'contre le type déduit 'IO [FilePath]'" – oshyshko

2

Tout d'abord, commencez par demander hlint.

Il vous donnerait quelques suggestions utiles et des leçons, comme:

homework.hs:46:1: Error: Use print 
Found: 
    putStrLn $ show $ length words 
Why not: 
    print (length words) 

Nous voyons donc que print = putStrLn . show

etc

+0

hlint roches. Merci. Comment simplifieriez-vous la fonction "searchWords"? – oshyshko

2

Je n'aime pas les noms de variables.

Ainsi voici une courte searchWords:

searchWords :: String -> String -> [String] -> IO [String] 
searchWords suffix path exts = 
    fmap (sort . nub . concatMap (wordsWithSuffix suffix)) . 
    mapM readFile . filter ((`elem` exts) . takeExtension) 
    =<< findFiles path 
+1

Plus court, oui; plus lisible, dépend du lecteur: -D me semble bien, de toute façon. (Digression: pourquoi utiliser 'Control.Monad.liftM' quand' Prelude.fmap' le fera?) – ephemient

+0

@ephemient: c'est une mauvaise habitude. d'écrire du code générique pour les Monades. depuis Monad n'implique pas automatiquement Functor .. – yairchu

+1

C'est plus une méchante verrue dans Haskell qu'une mauvaise habitude. Monad devrait impliquer à la fois Applicative et Functor. – Alasdair

Questions connexes