UNB/ CS/ David Bremner/ teaching/ cs2613/ labs/ CS2613 Lab 7

Background

Part I: Point free style

Time
25 minutes
Activity
Small groups / individuals

We already learned about higher order functions, functions which take other functions as arguments. Certain higher order functions return other functions; these are sometimes called combinators, especially when the "combine" two or more functions.

    #lang racket
    (require plot)
    (require "../L06/calculus.rkt")
    (lambda (x) (- (deriv sin x) (cos x)))
    (define (fsub f g)
      (lambda (x) (                ))

    (module+ test
      (require rackunit)
      (define (sin2 x) (integrate cos x))
      (define (cos2 x) (deriv sin x))
      (define epsilon 0.001)
      (define test-points (build-list 20 (lambda (x) (* 2 pi (random)))))
      (for ([x test-points])
        (check-= ((fsub cos cos2) x) 0 epsilon)
        (check-= ((fsub sin sin2) x) 0 epsilon)))
    (plot (function (fsub (lambda (x) (deriv sin x)) cos) -2pi 2pi))
    (define (fderiv f)
      (lambda (x) (deriv f x)))

    (plot (function (fsub (fderiv sin) cos) -2pi 2pi))
    (define (binop->fbinop op)
      (lambda (f g)
        (lambda (x)                 )))

    (define fsub2 (binop->fbinop -))

    (plot (function (fsub2 (fderiv sin) cos) -2pi 2pi)))

    (define fadd (binop->fbinop +))
    (plot (function (fadd  sin cos) -2pi 2pi))

Part II Combinator Discussion

Time
10 minutes
Activity
Group discussion

Part III Currying

Time
15 minutes
Activity
Demo
    (define (mogrify g f)
      (lambda (x) (g f x)))

    (define (fderiv2 f) (mogrify deriv f))

    (plot (function (fsub (fderiv2 sin) cos) -2pi 2pi))
    (define (currify g)
      (lambda (f) (lambda (x) (g f x))))

    (define fderiv3 (currify deriv))
    (module+ test
      (for ([x test-points])
        (check-= (cos x) ((fderiv3 sin) x) epsilon)))

Macros

Time
20 minutes
Activity
Small Groups/individual

In the first half of the lab we discussed a method of building programs at runtime using combinators. In the second half, we'll discuss the construction of programs at compile time using what are called macros.

Some Racket forms like and and or look like function calls, but we know they are not because they support short-circuit evaluation. We can define forms that behave differently than function calls (or that look like function calls, but actually do other things as well, like the rackunit check- forms). These forms are sometimes called macros.

    #lang racket
    (define-syntax-rule (And a b)
      (if b a #f))

    (module+ test
      (require rackunit)
      (define (die)
        (error 'die "don't run this"))

      (check-equal? (And (die) #f) #f)
      (check-exn exn:fail? (lambda () (and (die) #f))))
    (module+ test
      (define-syntax-rule (check-fail expr)
        (check-exn exn:fail? (lambda () expr)))
      (check-fail (and (die) #f))
      (check-fail (And #f (die))))
    (module+ test
      (check-equal? (Or #t #t) #t)
      (check-equal? (Or #f #t) #t)
      (check-equal? (Or #t #f) #t)
      (check-equal? (Or (die) #t) #t)
      (check-fail (or (die) #t)))

Macro Discussion

Time
5 minutes
Activity
Group discussion

Recursive Macros

Time
20 min
Activity
Demo

You will probably not be surprised to learn that macros in racket can be recursive. This is the usual way of handling variable numbers of "arguments".

Start a new file ~fcshome/cs2613/labs/L07/let-macros.rkt

We have discussed the difference between let and let* a bit in the labs. It turns out that let* can be implimented in terms of let using a macro.

Let's start by reviewing let*:

    (module+ test
      (check-equal? (let* ([x 5]
                           [y (- x 3)])
                          (+ y y))
                    4)
      (check-equal? (let* ([x 5]
                           [y (- x 3)]
                           [y x])
                           (* y y))
                    25))

]

    (module+ test
      (check-equal? (let ([x 5])
                      (let ([y (- x 3)])
                        (+ y y)))
                    4)
      (check-equal? (let ([x 5])
                      (let ([y (- x 3)])
                        (let ([y x])
                          (* y y))))
                    25))
    (require (for-syntax racket/list))

    (define-syntax (Let* stx)
      (let ([lst (syntax->datum stx)])
        (let ([bindings (second lst)]
              [body (third lst)])
          (datum->syntax
           stx
           (cond
             [(empty? bindings) body]
             [else
              (list 'let (list (first bindings))
                    (list 'Let* (rest bindings) body))])))))

    (Let* ([x 5]
           [y (- x 3)]
           [y x])
      (* y y))
    (define-syntax (Let* stx)
      (syntax-case stx ()
        [(Let* () body) #'body]
        [(Let* ((first-id first-val) (id val) ...) body)
         #'(let ([first-id first-val])
             (Let* [(id val) ...] body))]))

    (module+ test
      (check-equal? (Let* ([x 5]
                           [y (- x 3)])
                          (+ y y))
                    4)
      (check-equal? (Let* ([x 5]
                           [y (- x 3)]
                           [y x])
                           (* y y))
                    25))