grade_report.hs in Haskell
Averages each student's scores, assigns a letter grade, and prints a ranked table with a class summary.
import Data.List (sortOn)
import Data.Ord (Down (..))
students :: [(String, [Int])]
students =
[ ("Ada", [92, 88, 95, 79])
, ("Bruno", [71, 64, 80, 77])
, ("Chen", [88, 91, 84, 90])
, ("Dara", [55, 48, 58, 51])
, ("Esme", [100, 96, 93, 98])
]
-- An empty score list averages to zero rather than dividing by zero.
average :: [Int] -> Double
average [] = 0
average xs = fromIntegral (sum xs) / fromIntegral (length xs)
-- Guards read as a table of cutoffs, checked from the top down.
letterFor :: Double -> Char
letterFor avg
| avg >= 90 = 'A'
| avg >= 80 = 'B'
| avg >= 70 = 'C'
| avg >= 60 = 'D'
| otherwise = 'F'
-- Round to one decimal by scaling into Int and splitting the digits.
oneDecimal :: Double -> String
oneDecimal x = show whole ++ "." ++ show frac
where
(whole, frac) = round (x * 10) `divMod` (10 :: Int)
pad :: Int -> String -> String
pad n s = s ++ replicate (n - length s) ' '
lpad :: Int -> String -> String
lpad n s = replicate (n - length s) ' ' ++ s
row :: (String, [Int]) -> String
row (name, scores) =
pad 8 name ++ lpad 7 (oneDecimal avg) ++ lpad 7 [letterFor avg]
where
avg = average scores
rule :: String
rule = replicate 22 '-'
main :: IO ()
main = do
let ranked = sortOn (Down . average . snd) students
classAvg = average (concatMap snd students)
passing = length [s | s <- students, average (snd s) >= 60]
putStrLn "term grades, best first"
putStrLn (pad 8 "student" ++ lpad 7 "average" ++ lpad 7 "grade")
putStrLn rule
mapM_ (putStrLn . row) ranked
putStrLn rule
putStrLn (pad 8 "class" ++ lpad 7 (oneDecimal classAvg)
++ lpad 7 [letterFor classAvg])
putStrLn (show passing ++ " of " ++ show (length students) ++ " passing")
How it works
averagedividesfromIntegral (sum xs)by the count, answering zero for an empty list.letterForpicks a grade through guards, testingavg >= 90 = 'A'first and falling through tootherwise.sortOn (Down . average . snd)puts the best first, andoneDecimalrounds by scaling to Int and splitting withdivMod.
Keywords and builtins used here
CharDoubleDownIOIntStringaveragedoimportletletterForlpadmainoneDecimalpadrowrulesortOnstudentswhere
The run, in numbers
- Lines
- 60
- Characters to type
- 1663
- Tokens
- 419
- Three-star pace
- 75 tpm
At the three-star pace of 75 tokens a minute, this run takes about 335 seconds.
Step 2 of 3 in Encore, step 26 of 27 in Language basics.