é·å¹´ã®çåã®çãã Enumerator ã ã£ããä»æ¥ã¯ãããªã話ã§ãã
findãå®è£ ãã
Real World Haskell ã®ç¬¬9ç« ã« UNIX ã® find ã³ãã³ããä½ãä¾ãããã¾ããæåã¯å®ç´ã«å®è£ ãã¦ã¿ã¾ããããã¾ãããã£ã¬ã¯ããªã®å 容ãå¾ãè£å©é¢æ°ã¨ããã£ã¬ã¯ããªã辿ããã調ã¹ãè£å©é¢æ°ãç¨æãã¾ãã
getValidContents :: FilePath -> IO [String] getValidContents path = filter (`notElem` [".", "..", ".git", ".svn"]) <$> getDirectoryContents path isSearchableDir :: FilePath -> IO Bool isSearchableDir dir = (&&) <$> doesDirectoryExist dir <*> (searchable <$> getPermissions dir)
次ã«ããã£ã¬ã¯ããªãå帰çã«èµ°æ»ãã¦ããã¡ã¤ã«åã®ãªã¹ããè¿ãé¢æ°ãä½ãã¾ãã
getRecursiveContents :: FilePath -> IO [FilePath] getRecursiveContents dir = do cnts <- map (dir </>) <$> getValidContents dir concat <$> forM cnts $. \path -> do isDirectory <- isSearchableDir path if isDirectory then getRecursiveContents path else return [path]
ããæååãååã®ä¸é¨ã¨ãã¦æã¤ãã¡ã¤ã«ã®ã¿ãé¸å¥ããé¢æ° grep ã¨ãããããçµã¿åãããé¢æ° find ã以ä¸ã®ããã«å®è£ ãã¾ãã
grep :: String -> [FilePath] -> [FilePath] grep pattern = filter (pattern `isInfixOf`) find :: FilePath -> String -> IO () find dir pattern = grep pattern <$> getRecursiveContents dir >>= mapM_ putStrLn
ããã§ãfind "ãã£ã¬ã¯ããª" "ãã¿ã¼ã³" ãè©ä¾¡ããã°ãä¸å¿ç®çã¯éæã§ãã¾ããããããgetRecursiveContents ã¯ãä¸ãããããã£ã¬ã¯ããªã®é ã ãèµ°æ»ãçµãã£ãå¾ã§ãªãã¨ããã¡ã¤ã«åã®ãªã¹ããè¿ãã¾ããã
ã§ãããããã® find ã¯ãã°ããã¦ã¼ã¶ã¼ãå¾ ããã¦ãããä¸æ°ã«ãã¡ã¤ã«åã表示ãã¾ãã
Enumerator ã§å®è£ ãã find
RWH ã§ã¯ããã¡ã¤ã«ãåæããè ã¨æ¶è²»ããè ã®éã«ç°¡å㪠API ãå®ç¾©ãã¦ããã®åé¡ã解決ãã¦ãã¾ããããã¯ãããèãã㨠Enumerator ãã®ãã®ã§ããããã§ããã® API ã Enumerator ã® API ã«ç½®ãæãã¦å®è£ ãã¦ã¿ããã¨ã«ãã¾ãã
æ¶è²»è ã®æ¹ã¯ç°¡åã§ãã
grepE :: String -> Enumeratee String String IO b grepE pattern = E.filter (pattern `isInfixOf`) printI :: Iteratee String IO () printI = EL.head >>= maybe (return ()) $. \file -> do liftIO . putStrLn $ file printI
çç£è ã®æ¹ã¯ãEnumerator ã (<==<) ã§åæã§ãããã¨ãç¥ã£ã¦ããã°ã以ä¸ã®ããã«å®è£ ã§ããã§ãããã
enumDir :: FilePath -> Enumerator String IO b enumDir dir = list where list (Continue k) = do (files,dirs) <- liftIO getFilesDirs if null dirs then k (Chunks files) else k (Chunks files) >>== walk dirs list step = returnI step walk dirs = foldr1 (<==<) $ map enumDir dirs getFilesDirs = do cnts <- map (dir </>) <$> getValidContents dir (,) <$> filterM doesFileExist cnts <*> filterM isSearchableDir cnts
ãã㧠Enumerator ç find ã®å®æã§ãã
findE :: FilePath -> String -> IO () findE dir pattern = run_ $ enumDir dir $$ grepE pattern =$ printI
findE ã¯ãæã¿éããã¡ã¤ã«ãè¦ã¤ãã¿ã¤ãã³ã°ã§ããã¡ã¤ã«ã表示ãã¾ãã
çµäºæ¡ä»¶ã®æ¤æ»ããã®è§£æ¾
ä¸è¨ã® findE ã N å表示ãã¦çµããããã«ãæ¹é ãã¾ããããEL.isolate ãéã«å·®ãè¾¼ãã ãã§ãã
findE' :: FilePath -> String -> Integer -> IO () findE' dir pattern n = run_ $ enumDir dir $$ grepE pattern =$ EL.isolate n =$ printI
ãã®ä¾ãããEnumerator ã¯çµäºæ¡ä»¶ã®æ¤æ»ãã解æ¾ããã¦ãããã¨ãã¯ã£ããåããã¾ãããIO ã絡ãã ã¨ãã«ãçµäºæ¡ä»¶ã®æ¤æ»ãã解æ¾ãããã³ã¼ããã©ãæ¸ãã®ããé·å¹´ã®çåã®çãã¯ãEnumerator ã§ããã
ãã¾ã
å¿ è¦ã¯ã¢ã¸ã¥ã¼ã«ã¯ä»¥ä¸ã®éãã
import Control.Applicative ((<$>),(<*>)) import Control.Monad (filterM, forM) import Control.Monad.IO.Class (liftIO) import Data.Enumerator (Iteratee, Enumeratee, Enumerator, Stream(..), Step(..), (>>==), (<==<), returnI, yield, run_, joinI, ($$), (=$)) import qualified Data.Enumerator as E (filter) import Data.Enumerator.List as EL import Data.List (isInfixOf) import System.Directory (getDirectoryContents, doesFileExist, doesDirectoryExist, getPermissions, Permissions(..)) import System.FilePath ((</>)) infixr 5 $. ($.) :: (a -> b) -> a -> b ($.) = ($)