#lang racket ;; Note: this file requires DrRacket 5.2 (or presumably greater) ;; CSE 341 Fall 2011, Lecture 24 ;; Abstraction with dynamic types ;; This is one of several files. This file implements ;; rational numbers using a Racket module. ;; It exports the struct definition, but uses /contracts/ ;; to check /dynamically/ that any rationals provided from ;; /outside/ the module obey the invariants. Contracts can ;; perform any computation (side-effects /really/ bad style). ;; We are forcing /clients/ to call: ;; * only construct rationals with integer fields and ;; positive denominators ;; * only print reduced rationals ;; We are not keeping rationals in reduced form. (provide (contract-out (rat (-> integer? (lambda (y) (and (integer? y) (> y 0))) rat?)) (rat-num (-> rat? integer?)) (rat-den (-> rat? integer?)) (rat? (-> any/c boolean?)) (add (-> rat? rat? rat?)) (print-rat (-> reduced-rat void?)) (reduce-rat (-> rat? reduced-rat)))) ;; the add contract is interesting -- as with the unneeded checks in ;; reduced-rat below, we can rely on the contract for the rat constructor ;; and add chooses to allow unreduced arguments -- we'll require ;; the client to reduce results before calling print-rat (struct rat (num den)) (define (reduced-rat r) ;; private -- used in contracts (and (rat? r) ; (integer? (rat-num r)) ;; unneeded ; (integer? (rat-den r)) ;; unneeded ; (> (rat-den r) 0) ;; unneeded (= 1 (gcd (abs (rat-num r)) (rat-den r))))) ;; the unneeded checks cannot fail because ;; (a) the contract on rat requires these things ;; (b) rats are immutable ;; (c) within the module (where contracts are not checked) ;; we never make a rational that break these rules (define (gcd x y) ;; still private (cond [(= x y) x] [(< x y) (gcd x (- y x))] [#t (gcd y x)])) (define (reduce x y) ;; private, make the client use it via reduce-rat (if (= x 0) (rat 0 1) (let ([d (gcd (abs x) y)]) (rat (quotient x d) (quotient y d))))) (define (reduce-rat r) (reduce (rat-num r) (rat-den r))) ;; no more make-frac: have client use rat with a contract ;; add no longer requires reduced arg or produces reduced result (define (add r1 r2) (let ([a (rat-num r1)][b (rat-den r1)] [c (rat-num r2)][d (rat-den r2)]) (rat (+ (* a d) (* b c))(* b d)))) ;; contract ensures clients won't call unless argument is reduced ;; (within the module we have to be careful though) (define (print-rat r) (write (rat-num r)) (unless (or (= (rat-num r) 0) (= (rat-den r) 1)) (write '/) (write (rat-den r))))