Отсортированный Грэп: как бы вы улучшили эту программу?


Эта программа реализует отсортированный грэп, то есть специализированная версия утилиты grep для сортировки файлов. Он использует двоичный поиск для строк файла, которые начинаются с определенной строкой.

Вы можете скопировать и вставить код в файл и запустить его как:

    $> runhaskell sgrep.hs "string to find" sorted_file

Я ищу предложения о стиль, эффективность и правильность.

    module Main where

    import Data.List (isPrefixOf)
    import Data.Maybe (isNothing, fromJust)
    import System.Environment (getArgs)
    import System.IO

    -- Chunk of a file
    data Chunk = Chunk Handle Integer Integer

    -- Is char newline?
    isNL :: Char -> Bool
    isNL c = c == '\n'

    -- Are we at the beginning of file?
    isBOF :: Handle -> IO Bool
    isBOF = (fmap (== 0)) . hTell

    -- Go to beginning of line
    goToBOL :: Handle -> IO ()
    goToBOL h = do
            bof <- isBOF h
            if bof        
               then return ()
               else do 
                       eof <- hIsEOF h
                       if eof
                          then do 
                                  hSeek h RelativeSeek (-2)
                                  goToBOL h

                          else do    
                                  c <- hGetChar h
                                  if isNL c
                                     then return ()
                                     else do
                                             hSeek h RelativeSeek (-2)
                                             goToBOL h

    getCurrentLine :: Handle -> IO String
    getCurrentLine h = goToBOL h >> hGetLine h

    getPrevLine :: Handle -> IO (Maybe String)
    getPrevLine h = do
            goToBOL h
            bof <- isBOF h
            if bof
               then return Nothing
               else do
                       hSeek h RelativeSeek (-2)
                       goToBOL h
                       bof <- isBOF h
                       if bof
                          then return Nothing
                          else do
                                  hSeek h RelativeSeek (-2)
                                  goToBOL h
                                  line <- hGetLine h
                                  return $ Just line

    goTo :: Handle -> Integer -> IO ()
    goTo h i = do
            hSeek h AbsoluteSeek i

    search :: Chunk -> String -> IO (Maybe String)
    search (Chunk h start end) str
            | start >= end = return Nothing
            | otherwise = do
                    if mid == (end - 1)
                       then return Nothing
                       else do
                               goTo h mid
                               midLine <- getCurrentLine h
                               prevLine <- getPrevLine h
                               --  putStrLn $ "*** " ++ show start ++ " " ++ show end ++ " " ++ show mid ++ " " ++ midLine ++ ", " ++ show prevLine
                               if str `isPrefixOf` midLine && ((isNothing prevLine) || not (str `isPrefixOf` (fromJust prevLine)))
                                  then return $ Just midLine
                                  else if str < midLine
                                          then search (Chunk h start mid) str
                                          else search (Chunk h mid end) str
               where mid = (start + end) `div` 2

    sgrep :: Handle -> String -> IO ()
    sgrep h s = do
            len <- hFileSize h
            match <- search (Chunk h 0 len) s
            --  putStrLn $ show match
            c <- hGetContents h
            putStrLn . unlines $ takeWhile (isPrefixOf s) (lines c)

    main :: IO ()
    main = do
            args <- getArgs
            let s = head args
            putStrLn s
            let fname = head $ tail args
            withFile fname ReadMode (\h -> sgrep h s)


514
5
задан 26 ноября 2011 в 06:11 Источник Поделиться
Комментарии
2 ответа

Используйте Ваши монады! Ваш код демонстрирует ходьба-право антиобразец. Вы можете избежать его , когда и охранник. Рассмотрим goToBOL. Это как я бы написал это:

-- Go to beginning of line
goToBOL :: Handle -> IO ()
goToBOL h = do
bof <- isBOF h
when (not bof) $ do
eof <- hIsEOF h
if eof then do hSeek h RelativeSeek (-2)
goToBOL h
else do c <- hGetChar h
when (not $ isNL c) $ do
hSeek h RelativeSeek (-2)
goToBOL h

В другие функции, а именно getPrevLine и поиск, вам лучше использовать MaybeT IO в Х , а не ИО (может быть х) как можно использовать монадические комбинаторы лучше, когда вы делаете это.

5
ответ дан 26 ноября 2011 в 09:11 Источник Поделиться

В дополнение к очков @FUZxxl это:

Вы называете sgrep только от последней строки основного, и параметры наоборот. Изменение

sgrep :: String -> Handle -> IO ()
sgrep s h = do
...

и

        ...
withFile fname ReadMode (sgrep s)

И я бы рисунок аргументы командной строки (Если вам не нужны преимущества системы.Консоль.Использования getopt):

main :: IO ()
main = do
(s : fname : _) <- getArgs
putStrLn s
withFile fname ReadMode (sgrep s)

2
ответ дан 27 ноября 2011 в 02:11 Источник Поделиться