[Haskell-beginners] Directory statistics
Alexander.Vladislav.Popov
alexander.vladislav.popov at gmail.com
Tue Oct 26 03:14:17 EDT 2010
Hi, haskellers.
I’ve written a program to assess directory utilization in the selected
path. It displays on the console a list indicating directory size and
path. The list is sorted by descending size. I need some criticism and
some models I could follow to write a more concise and expressive
program.
Thanks in advance.
Alexander.
> {-# LANGUAGE ScopedTypeVariables #-}
> module Main (main) where
> import Control.Exception (SomeException, finally, bracket, handle)
> import Control.Monad
> import Data.List
> import System.Directory
> import System.Environment
> import System.IO
> import Text.Printf
> main :: IO ()
> main = do
> args <- getArgs
> if null args
> then
> putStr "Displays directory utilization in the selected path.\nUsage: dirstat <path>\n"
> else
> mapM_ ds3 args
It safely returns a file size. If an error occurs during file opening,
it will return 0.
> filesize :: FilePath -> IO Integer
> filesize path = (withFile path ReadMode hFileSize) `catch` const (return 0)
Get size of a directory.
> ds :: FilePath -> IO Integer
> ds path = do
> contents <- getDirectoryContents path `catch` const (return [])
> let visibles = getVisible contents
> let path' = clrSlash path
> a <- (liftM sum) $ sequence $ map (\p -> filesize (path' `mkpath` p)) visibles -- size of a current dir
> (liftM ((+a) . sum)) $ mapM (\p -> ds (path' `mkpath` p)) visibles -- current + children
Returns a list of pairs: (file size, path)
> ds2 :: FilePath -> IO [(Integer, FilePath)]
> ds2 path = do
> contents <- getDirectoryContents path
> let visibles = getVisible contents
> let path' = clrSlash path
> let paths = map (\p -> path' `mkpath` p) visibles
> let pairs = map (\p -> (ds p, p)) paths
> a <- sequenceFst pairs
> return $ (reverse . sort . filter (\e -> fst e > 0)) a
Compare it to the function *sequence :: (Monad m) => [m a] -> m [a]*
> sequenceFst :: (Monad m) => [(m t, t1)] -> m [(t, t1)]
> {-# INLINE sequenceFst #-}
> sequenceFst ms = foldr k (return []) ms
> where
> k (ms, p) m' = do { s <- ms; xs <- m'; return ((s,p):xs) }
Driver
> ds3 :: FilePath -> IO ()
> ds3 path = do
> s <- ds2 path
> prn s
> where
> prn [] = return ()
> prn (s:ss) = (putStr . showDir) s >> prn ss
Auxiliary
> skipDots = (`notElem` [".", ".."])
> getVisible = filter skipDots
> mkpath p1 p2 = p1 ++ "/" ++ p2
> clrSlash = reverse . dropWhile (\c -> c =='/' || c == '\\') . reverse
Displays information about a directory.
> showDir :: (Integer, String) -> String
> showDir (s,p) = printVolume s 3 ++ "\t" ++ show p ++ "\n"
*Main> shred "1234567890"
["123","456","789","0"]
> shred [] = []
> shred ss = (take 3 ss) : (shred (drop 3 ss))
*Main> prettyNum "1234567890"
"1 234 567 890"
> prettyNum = concat . intersperse " " . (map reverse) . reverse . shred . reverse
*Main> units (1024*1024*1024)
[1073741824,1048576,1024,1,0]
> units :: Integer -> [Integer]
> units 0 = [0]
> units x = x : units' x
> where
> units' 0 = []
> units' x = y : units' y
> where
> y = round (fromIntegral x / 1024)
Just SI prefixes
> prefix = ["B", "K", "M", "G", "T", "P", "E", "Z", "Y"]
*Main> tagged (1024*1024*1024)
[("1 073 741 824","B"),("1 048 576","K"),("1 024","M"),("1","G"),("0","T")]
> tagged x = (map (prettyNum . show) $ units x) `zip` prefix
*Main> printVolume (1024*1024*1024) 5
1 024 M
> printVolume x width = printf "%*s %s" width (fst one) (snd one)
> where
> one = head $ dropWhile (\p -> length (fst p) > width) $ tagged x
More information about the Beginners
mailing list