;; ask-ian: num, num, num --> (list {>, <=} num 'target) ;; Play the part of the MC: ;; Given a guess, tell wehtehr it's greater than the target, ;; or less-or-equal the target. ;; ;; The last two inputs (lo,hi) aren't actually used, ;; but the MC prints out annoying messages which include them ;; (helpful for debugging). ;; (define (ask-ian guess lo hi) (begin (printf "Between ~s and ~s, is ~s your final answer?~n" lo hi guess) (cond ;[(= guess target) 'bingo!] [(> guess target) (list > guess 'target)] [(<= guess target) (list <= guess 'target)] [else 'huh?]))) ; The hidden number being guessed. ; (define target 374) ; ; (To think about: ; How could ask-ian make the placeholder "target" ; impervious to programs that might want to peek at it?) ;; hilo: num, num --> num ;; Return the hidden number, and integer in ;; in the range [lo, hi]. (That is, the range including lo and hi.) ;; (define (hilo-v1 lo hi) (local [(define mid (floor (/ (+ hi lo) 2))) (define answer (ask-ian mid lo hi))] (cond [(= 0 (- hi lo)) lo] [(equal? > (first answer)) (hilo-v1 lo mid)] [(equal? <= (first answer)) (hilo-v1 mid hi)] [else (error 'hilo-v1 "something wrong.")]))) (define MAX 1000) (define MIN 0) ; THIS VERSION LOOPS FOREVER! (but only on some inputs): ;(hilo-v1 MIN MAX) ;; hilo-v2: num, num --> num ;; Return the hidden number, and integer in ;; in the range [lo, hi). ;; (That is, note that hi is one bigger than the actual ;; biggest candidate.) ;; ;; Argument of termination: ;; (hi-lo) is a smaller every call: ;; - if hi-lo < 2, we terminate. [by code] ;; - If hi-lo >= 2, then [lo,mid) and [mid,hi) are each *strictly* ;; smaller than [lo,hi), and we recur on one of these ranges. ;; [by math: ;; If hi-lo >= 2, then mid = (hi+lo)/2 = lo + (hi-lo)/2 is at least ;; 1 bigger than lo, and hi - (hi-lo)/2 is at least 1 less than hi, ;; so taking the floor it will still be at least 1 away from ;; each of lo,hi.] ;; - Since any recursion is on a smaller interval (by at least 1), ;; and we halt on intervals size 1 (or less), we will halt. ;; (define (hilo-v2 lo hi) (local [(define mid (floor (/ (+ hi lo) 2))) (define answer (ask-ian mid lo hi))] (cond [(<= (- hi lo) 1) lo] [(equal? > (first answer)) (hilo-v2 lo mid)] [(equal? <= (first answer)) (hilo-v2 mid hi)] [else (error 'hilo-v2 "something wrong.")]))) ;;; ;;; A FAULTY argument of termination: ;;; - Every time we recur on a smaller range, ;;; so eventually we'll hone in on the answer, ;;; and taking the floor still narrows the range. ;;; This is WRONG since it applies equally well to our first, ;;; buggy version. (hilo-v2 MIN (add1 MAX)) ;; ------------------------ (define sz 300) (define offset 5) (define a (make-posn offset offset)) (define b (make-posn (+ sz offset) offset)) (define c (make-posn (+ offset (/ sz 2)) (+ offset (round (* sz (/ (sqrt 3) 2)))))) (define d (make-posn (+ offset (* 2 sz)) (* .7 sz))) ;; avg2: num, num --> num ;; Return the average of a and b. ;; (define (avg2 a b) (/ (+ a b) 2)) ;Examples omitted. ;; midpoint: posn, posn --> posn ;; Return a point with integer coordinates, ;; near the midpoint of p1, p2. ;; (define (midpoint p1 p2) (make-posn (avg2 (posn-x p1) (posn-x p2)) (avg2 (posn-y p1) (posn-y p2)))) ; Examples omitted. ;; manhattan-distance: posn, posn --> number ;; Manhattan distance between p and q ;; (i.e., distance if you can travel n/s or e/w, ;; but not diagonal.) ;; (define (manhattan-distance p q) (+ (abs (- (posn-x p) (posn-x q))) (abs (- (posn-y p) (posn-y q))))) ; Examples omitted. ;; too-small?: posn,posn,posn --> boolean ;; Return true if any of p1,p2,p3 near each other. ;; (define close 3) (define (too-small? p1 p2 p3) (or (<= (manhattan-distance p1 p2) close) (<= (manhattan-distance p2 p3) close) (<= (manhattan-distance p3 p1) close))) ; Examples omitted. ;; draw-triangle: posn, posn, posn --> true. ;; Draw a triangle on the screen. ;; (define (draw-triangle p1 p2 p3) (begin (draw-solid-line p1 p2) (draw-solid-line p2 p3) (draw-solid-line p3 p1))) ;; sierpinski: posn posn posn --> true ;; Draw a sierpinski curve, ;; except stop when it gets really small. ;; (define (sierpinski p1 p2 p3) (cond [(too-small? p1 p2 p3) (draw-triangle p1 p2 p3)] [else (local [(define p1-p2 (midpoint p1 p2)) (define p2-p3 (midpoint p2 p3)) (define p3-p1 (midpoint p1 p3))] (begin (sierpinski p1 p1-p2 p3-p1) (sierpinski p2 p1-p2 p2-p3) (sierpinski p3 p3-p1 p2-p3)))])) ;; call "Start" to set up the canvas. (define rim 1.1) ; How much area bigger than the triangle, to make the canvas. (start (round (* sz rim 2)) (round (* sz rim))) ; A couple of tests: (define (s1) (sierpinski a b c)) (define (s2) (sierpinski a b d)) (s1) ;; NOTE: If you are round'ing posns to the nearest int (in avg2), ;; then defining "too-close" to be 19 or 75 generates ;; interesting drawings -- roundoff anomolies. ;; --------------- ;; merge: list-of-number, list-of-number --> list-of-number ;; l1 and l2 are each ALREADY SORTED in ascending order. ;; Return a single list with *all* the elements of l1,l2 ;; sorted in ascending order. ;; (define (merge l1 l2) (cond [(and (empty? l1) (empty? l2)) empty] [(and (empty? l1) (cons? l2)) l2] [(and (cons? l1) (empty? l2)) l1] [(and (cons? l1) (cons? l2)) (if (< (first l1) (first l2)) (cons (first l1) (merge (rest l1) l2)) (cons (first l2) (merge l1 (rest l2))))])) ; Tests omitted (see previous lectures). ;; unzip: list-of-alpha --> (list list-of-alpha list-of-alpha) ;; Return two lists, each with alternating items from "stuff". ;; (define (unzip stuff) (local [(define (unzip-help remaining bin-a bin-b) (cond [(empty? remaining) (list bin-a bin-b)] [(cons? remaining) (unzip-help (rest remaining) bin-b (cons (first remaining) bin-a))]))] (unzip-help stuff empty empty))) ; Tests omitted (see hw solns). ;; mergesort: list-of-nums --> list-of-nums ;; Return the lements of "nums" in ascending order. ;; ;; Implementation details: ;; Divide and conquer! ;; divide "nums" into two halves, sort each half, ;; and we can just give those sorted halves to "merge" nad be done. ;; ;; What is the base-case? (When are we done before we start?) ;; ;; #| (define (mergesort nums) (cond [..?some-base-case?.. ..answer..] [else (local [(define unzipped (unzip nums)) (define half-a (first unzipped)) (define half-b (second unzipped))] ..?body-of-local?..)])) ;; We complete this by thinking about the implementaiton details: |# (define (mergesort nums) (cond [(<= (length nums) 1) nums] [else (local [(define unzipped (unzip nums)) (define half-a (first unzipped)) (define half-b (second unzipped))] (merge (mergesort half-a) (mergesort half-b)))])) (equal? (mergesort empty) empty) (equal? (mergesort (list 17)) (list 17)) (equal? (mergesort (list 3 17)) (list 3 17)) (equal? (mergesort (list 17 3)) (list 3 17)) (equal? (mergesort (list 1 2 3 4)) (list 1 2 3 4)) (equal? (mergesort (list 4 3 2 1)) (list 1 2 3 4)) (equal? (mergesort (list 4 1 3 2)) (list 1 2 3 4)) (equal? (mergesort (list 17 17 17 17)) (list 17 17 17 17)) (equal? (mergesort (list 2 38 27 4 -5 2 3 8)) (list -5 2 2 3 4 8 27 38))