Solution

ID: past-exam-of-the-year-1-of-the-computer-science-course-of-the-university-of-oxford/2017/imperative-programming-resit/1/c/solution

Use the ordinary fraction identities, returning fresh records rather than changing either argument:
PROCEDURE addRat(x,y: rational): rational;
BEGIN
  RETURN mkRat(x.num*y.den + y.num*x.den, x.den*y.den)
END addRat;

PROCEDURE mulRat(x,y: rational): rational;
BEGIN
  RETURN mkRat(x.num*y.num, x.den*y.den)
END mulRat;

PROCEDURE negRat(x: rational): rational;
BEGIN
  RETURN mkRat(-x.num,x.den)
END negRat;
Every result is reduced by mkRat. This preserves the representation invariant even if an input was initially unreduced, provided its denominator is nonzero. Aliasing between the arguments causes no problem because their fields are only read.

New to topics? Read the docs here!