(* 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 = real ps1 * ps +
real ps2 * ps +
real ps3 * ps +
real ps4 * ps +
real ps5 * ps +
real ps6 * ps +
real ps7 * ps +
real 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;