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