forked from steger/pr3-sose2026
64 lines
1.8 KiB
Haskell
64 lines
1.8 KiB
Haskell
import Data.Function (on)
|
|
import Data.List (groupBy, nub, sortOn)
|
|
import System.IO
|
|
|
|
-- separator -> Input -> Separated Input
|
|
split :: Char -> String -> [String]
|
|
split _ "" = []
|
|
split sep str =
|
|
let (word, rest) = break (== sep) str
|
|
in word : case rest of
|
|
[] -> []
|
|
(_ : xs) -> split sep xs
|
|
|
|
-- Data structure for a student entry
|
|
data Entry = Entry
|
|
{ firstName :: String,
|
|
lastName :: String,
|
|
subject :: String,
|
|
scores :: [Int]
|
|
}
|
|
deriving (Show)
|
|
|
|
-- Parse CSV content into entries
|
|
parse :: String -> [Entry]
|
|
parse content =
|
|
let lns = lines content
|
|
(header : rows) = lns
|
|
in map parseRow rows
|
|
where
|
|
parseRow line =
|
|
let fields = split ',' line
|
|
[fn, ln, subj] = take 3 fields
|
|
pts = map read (drop 3 fields) :: [Int]
|
|
in Entry fn ln subj pts
|
|
|
|
-- Calculate averages per subject
|
|
averages :: [Entry] -> [(String, Float)]
|
|
averages entries =
|
|
let grouped = groupBy ((==) `on` subject) (sortOn subject entries)
|
|
avgPerSubject grp =
|
|
let subj = subject (head grp)
|
|
allScores = concat [scores e | e <- grp]
|
|
avg = fromIntegral (sum allScores) / fromIntegral (length allScores)
|
|
in (subj, avg)
|
|
in map avgPerSubject grouped
|
|
|
|
-- Find the best student (most accumulated points)
|
|
bestStudent :: [Entry] -> (String, Int)
|
|
bestStudent entries =
|
|
let best = maximum [(sum (scores e), firstName e ++ " " ++ lastName e) | e <- entries]
|
|
in (snd best, fst best)
|
|
|
|
main :: IO ()
|
|
main = do
|
|
putStrLn "Enter a filename: "
|
|
filename <- getLine
|
|
content <- readFile filename
|
|
let entries = parse content
|
|
(name, points) = bestStudent entries
|
|
avgs = averages entries
|
|
putStrLn $ "Best student: " ++ name ++ " (" ++ show points ++ " points)"
|
|
putStrLn "Average points per subject:"
|
|
mapM_ (\(subj, avg) -> putStrLn $ " " ++ subj ++ ": " ++ show avg) avgs
|