I started learning Haskell a couple of months ago, quit, then re-started two or three weeks ago, determined to get a decent amount of knowledge. I have to admit I'm fascinated by now...so, here's my first attempt at a real-world useful program: a kill-log parser.
Windows users: just run hparse.exe <kill-logs-dir>
Linux users: you'll need to compile using ghc --make hparse.hs -o hparse (you'll need GHC, the haskell compiler)
Here's the source for anyone who is interested in what a functional programming app looks like:
import Data.List
import System.Directory
import System.Environment
-- basic endl
endl = "\n"
-- checks if the given string matches *.txt
isLog :: String -> Bool
isLog log = isSuffixOf ".txt" log
-- gets logs from a directory
getLogs :: String -> IO [String]
getLogs cwd = do
files <- getDirectoryContents cwd
return $ filter isLog files
-- sorts & groups all elements together then counts
listGroupCount :: (Ord a) => [a] -> [(a, Int)]
listGroupCount xs = map (\xs -> (head xs, length xs)) ((group . sort) xs)
-- splits logs into a three-tuple with the form (killer,victim,weapon)
logSplit :: FilePath -> IO [(String, String, String)]
logSplit logName = do
raw <- readFile logName
return . logSplit' . tail . lines $ raw
logSplit' :: [a] -> [(a,a,a)]
logSplit' [] = []
logSplit' (a:b:c:d:xs) = (b,c,d) : logSplit' xs
logSplit' _ = error "Corrupt log"
-- first message printed
printParsing :: [String] -> FilePath -> IO ()
printParsing logs dir = putStrLn $ "Parsing: " ++ (show n) ++ " log" ++ xs ++ " found in " ++ dir ++ endl
where n = length logs; xs = if n > 1 || n == 0 then "s" else ""
-- prettifies output of collected stats
printPretty :: [(String, Int)] -> IO ()
printPretty = (putStrLn . concat . printPretty')
printPretty' :: (Show t) => [(String, t)] -> [String]
printPretty' [] = []
printPretty' (x:xs) = (\(name,count) -> name ++ " => " ++ (show count) ++ endl) x : printPretty' xs
usage = error "Usage: hparse <dir>\n\t<dir>: Directory where the kill-logs are stored"
-- main
main :: IO ()
main = do
args <- getArgs
let dir = if null args then usage else head args
logs <- getLogs dir
printParsing logs dir
logSp <- mapM logSplit logs
let allData = foldr (++) [] logSp
--let killers = map (\(b,c,d) -> b) allData
--let victims = map (\(b,c,d) -> c) allData
let weapons = map (\(b,c,d) -> d) allData
printPretty $ listGroupCount weapons
--printPretty $ listGroupCount killers
--printPretty $ listGroupCount victims
As you can see (-- is the one-line comment operator in hs), the code can be extended to search for top killers, top victims, etc, but as usual, I lost interest in coding such features which in turn I leave for anyone who has the will to add them.
Attached: the source & the windows binary in the zip.