(* grading.sml *) (* Course Grading Function If ps1, ps2, ps3, ps4, ps5, ps6, ps7 and fin are a student's points (on our 100-point scale) for Problem Sets 1-7 and the Final Exam/Project, respectively, then grade ps1 ps2 ps3 ps4 ps5 ps6 ps7 fin will evaluate to the pair (g, p), where: g is the student's letter grade; and p is the student's numerical grade (useful for seeing how close the student is to a neighboring grade). *) (* according to the syllabus, each of the 7 problem sets is worth 100 points, and the final exam/project is worth 200 points (and so counts as 2 problem sets) so let x be the fraction of 1.0 corresponding to 100 points: *) val x = 100.0 / 900.0 (* thus: each problem set is worth x * 100.0 (11.1111111111) percent of the course grade the final exam is worth x * 2.0 * 100.0 (22.2222222222) percent of the course grade *) val ps = x val fi = x * 2.0 local exception CannotHappen val grades = ["F", "D-", "D", "D+", "C-", "C", "C+", "B-", "B", "B+", "A-", "A", "A+"] (* grade to points *) fun gtp "F" = 0.0 | gtp "D-" = 12.0 | gtp "D" = 20.0 | gtp "D+" = 28.0 | gtp "C-" = 36.0 | gtp "C" = 44.0 | gtp "C+" = 52.0 | gtp "B-" = 60.0 | gtp "B" = 68.0 | gtp "B+" = 76.0 | gtp "A-" = 84.0 | gtp "A" = 92.0 | gtp "A+" = 100.0 | gtp _ = raise CannotHappen (* points to grade *) local (* in a call ptg' p zs, zs is a nonempty suffix of grades, and p >= gtp(hd zs) *) fun ptg' p nil = raise CannotHappen | ptg' p [x] = x | ptg' p (x :: y :: zs) = if p < (gtp x + gtp y)/2.0 then x else if p < gtp y then y else ptg' p (y :: zs) in fun ptg p = ptg' p grades end in fun grade ps1 ps2 ps3 ps4 ps5 ps6 ps7 fin = let val p = ps1 * ps + ps2 * ps + ps3 * ps + ps4 * ps + ps5 * ps + ps6 * ps + ps7 * ps + fin * fi val g = ptg p in ((case g of "D-" => "D" (* make legal BU grades *) | "D+" => "D" | "A+" => "A" | g => g), p) end end;