Ver código fonte

Style changes so the code fits on slides for a presentation

master
Peter J. Jones 5 anos atrás
pai
commit
402c023744
1 arquivos alterados com 55 adições e 24 exclusões
  1. 55
    24
      src/Main.hs

+ 55
- 24
src/Main.hs Ver arquivo

@@ -28,55 +28,80 @@ import Text.Printf (printf)

--------------------------------------------------------------------------------
-- | Keep track of a series of counters while processing files.
-- <<: counters
data Counters = C
{ lines :: Int -- ^ Number of lines.
, words :: Int -- ^ Number of words.
, chars :: Int -- ^ Number of characters.
} deriving Show
{ lines :: Int -- ^ Number of lines.
, words :: Int -- ^ Number of words.
, chars :: Int -- ^ Number of characters.
}

instance Monoid Counters where
mempty = C 0 0 0
mappend (C l1 w1 c1) (C l2 w2 c2) = C (l1 + l2) (w1 + w2) (c1 + c2)
mappend (C l1 w1 c1) (C l2 w2 c2) =
C (l1 + l2) (w1 + w2) (c1 + c2)
-- :>>

--------------------------------------------------------------------------------
-- | State necessary to process a file in chunks.
-- <<: state
data State = S
{ inWord :: Bool -- ^ Last character was part of a word.
, counters :: Counters -- ^ Current set of counters.
{ inWord :: Bool
-- ^ Last character was part of a word.

, counters :: Counters
-- ^ Current set of counters.
}
-- :>>

--------------------------------------------------------------------------------
-- | Update the given state based on the given character.
-- <<: wc
wc :: State -> Char -> State
wc state char = case char of
'\n' -> whitespace (\c -> c {lines = lines c + 1})
_ | isSpace char -> whitespace id
| otherwise -> S True (character id)
'\n' -> newline
_ | isSpace char -> whitespace
| otherwise -> nonspace
where
whitespace :: (Counters -> Counters) -> State
whitespace f = if inWord state
then S False (character (f . \c -> c {words = words c + 1}))
else S False (character f)
-- ...
-- :>>
newline :: State
newline = update (\c -> c {lines = lines c + 1}) whitespace

whitespace :: State
whitespace =
if inWord state
then S False (counters $ update (\c -> c {words = words c + 1}) character)
else S False (counters character)

character :: (Counters -> Counters) -> Counters
character f = let counters' = f (counters state)
in counters' {chars = chars counters' + 1}
nonspace :: State
nonspace = S True (counters character)

character :: State
character = update (\c -> c {chars = chars c + 1}) state

update :: (Counters -> Counters) -> State -> State
update f s = s {counters = f (counters s)}

--------------------------------------------------------------------------------
-- | Process all characters in the given stream.
-- <<: stream
stream :: InputStream ByteString -> IO Counters
stream = Streams.decodeUtf8 >=> go (S False mempty) >=> return . counters
stream = Streams.decodeUtf8 >=>
go (S False mempty) >=>
return . counters
where
go :: State -> InputStream Text -> IO State
go state upstream = do
textM <- Streams.read upstream

case textM of
Nothing -> return (eof state)
Just text -> go (T.foldl' wc state text) upstream
-- ...
-- :>>

eof :: State -> State
eof state@(S w c) = if w then S False (c {words = words c + 1}) else state
eof state@(S w c) =
if w then S False (c {words = words c + 1}) else state

--------------------------------------------------------------------------------
-- | Print a report to STDOUT.
@@ -93,12 +118,18 @@ width cs = go 1 $ maximum (0 : map chars cs) where

--------------------------------------------------------------------------------
-- | Do it!
-- <<: main
main :: IO ()
main = do
args <- getArgs

case args of
[] -> report (width []) "" =<< stream (Streams.stdin)
xs -> do cs <- mapM (flip Streams.withFileAsInput stream) xs
mapM_ (uncurry $ report (width cs)) (zip xs cs)
report (width cs) "total" (mconcat cs)
-- No command line arguments...
[] -> report 1 "" =<< stream (Streams.stdin)

-- List of files...
fs -> do
cs <- mapM (flip Streams.withFileAsInput stream) fs
mapM_ (uncurry $ report (width cs)) (zip fs cs)
when (length cs > 1) $ report (width cs) "total" (mconcat cs)
-- :>>

Carregando…
Cancelar
Salvar