Functional Problem Solving (CSC 151 2016S) : EBoards

CSC151.02 2016S, Review Session 3 (2016-02-11)


Thank you to the students who showed up so that we get a review session that other students can read over later.

Overview

Your Exam Questions

None yet.

Reminders: It's good to ask questions. It's good to look at the exam asap.

Quiz Topics and Structure

Topics

Form

Your Questions

'll try to gather them all and then answer them.

Can we talk a bit more about section?

section is a procedure that makes procedures. One of three mechanisms we currently know (the others are compose and lambda).

Like compose, section makes procedures only from existing procedures.

We make new procedures by "filling in" one or more parameters of the existing procedure.

A few examples

    ; Know (expt base power) and computes base*base*base*...base
    ; power times.

    (define square (section expt <> 2))

    (define three-to-the (section expt 3 <>))

Note: section is part of the gigls library.

You can have more than one "open space"

    (define stupid (section - <> 3 <>))

> (define stupid (section - <> 3 <>))

> (stupid 2)
. . stupid: arity mismatch;
 the expected number of arguments does not match the given number
  expected: 2
  given: 1
  arguments...:
   2
> (stupid 5 2)
0
> ; That's 5 - 3 - 2

(define rg (section irgb <> <> 0))
> (rg 200 100)
13132800
> (irgb->string (rg 200 100))
"200/100/0"

We do not write an open paren before the procedure name in section (nor in compose, nor in image-variant, nor in others that we'll see soon).

Are there things we can do with section that we can't do with lambda, and vice versa?

Anything you can do with section, you can do with lambda. It's just more text. (Stylistic, rather than preferences.)

    (define rg
      (lambda (r g)
        (irgb r g 0)))

There are many things you can do with lambda that you can't do with section.

    (define pointless
      (lambda (x)
        (* x (+ x x))))

lambda, unlike section, is a standard part of Scheme (core to Scheme)

We tend to use lambda except when section and compose make our code more concise (or more readable).

Sample Quiz Questions

  1. Go to http://www.cs.grinnell.edu/~rebelsky
  2. Click on "CSC 151"
  3. Click on "Current: Eboard"

Understand (and document) image code

Consider the following procedure

(define fun 
   (compose image-show
            (section image-variant
                     (image-load "/home/rebelsky/Desktop/kitten.jpg")
                     <>)))

Write the first four P's of the six-P style documentation for this procedure.

A Solution

;;; Procedure:
;;;
;;; Parameters:
;;;
;;; Purpose:
;;;
;;; Produces:
;;;

Order of Evaluation

Consider the following procedures

(define yield
  (lambda (val)
    (display " yields ")
    (display val)
    (newline)
    val))

(define add
  (lambda (x y)
    (display x)
    (display " plus ")
    (display y)
    (yield (+ x y))))

(define square
  (lambda (x)
    (display x)
    (display " squared ")
    (yield (* x x))))

What output and result should you get for each of the following expressions?

(add 2 3)

(add x y)

(add (square 2) (square 3))

(square (add 2 3))

(add (square 2) (square (square 3)))

Averaging Colors

Write (irgb-average color1 color2), which averages the red, green, and blue components of color and color2.

(define irgb-average
  (lambda (color1 color2)
     ....))

Annotating Code

Consider the following procedure, which computes the largest of the three components of an integer-encoded RGB color.

(define irgb-max
  (lambda (color)
    (max (irgb-red color)
         (irgb-green color)
         (irgb-blue color))))

Write a procedure, irgb-max_*, which takes the same input as irgb-max, generates the same output as irgb-max, and also prints a short message describing these input and output. You may use display and yield in your instructions.

Using Annotated Code

Consider the following two procedures

(define f1
  (lambda (color)
    (irgb (* (irgb-red color)
             (quotient (irgb-red color)
                       (irgb-max_* color)))
          (* (irgb-green color)
             (quotient (irgb-green color)
                       (irgb-max_* color)))
          (* (irgb-blue color)
             (quotient (irgb-blue color)
                       (irgb-max_* color))))))

(define f2
  (lambda (color)
    (f2-helper color (irgb-max_* color))))

(define f2-helper
  (lambda (color cmax)
    (irgb (* (irgb-red color)
             (quotient (irgb-red color) cmax))
          (* (irgb-green color)
             (quotient (irgb-green color) cmax))
          (* (irgb-blue color)
             (quotient (irgb-blue color) cmax))

We should get the same return value from f1 and f2. But will we see the same output (generated by irgb-max_*)? Why or why not?

Making colors more cyan

Taylor and Jordan have decided to write a procedure that makes colors more like cyan. They call it cyanide. (Don't blame me; it's their idea.) (cyanide color) should produce a new color in which the red component decreases by 16 (or to 0, if the component is less than 16), the blue component increases by 16 (or to 255, if the component is more than 239), and the green component also increases by 16 (or to 255, if the component is more than 239).

Without using lambda, write cyanide. You will likely need to use section and compose.

p.s. Here's the 6P-style documentation.

;;; Procedure:
;;;   cyanide
;;; Parameters:
;;;   color, an integer-encoded RGB color
;;; Purpose:
;;;   Make color look more like "cyan"
;;; Produces:
;;;   poisoned, an integer-encoded RGB color
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   (irgb-red poisoned) = (max 0 (- (irgb-red color) 16))
;;;   (irgb-green poisoned) = (min 255 (+ (irgb-green color) 16))
;;;   (irgb-blue poisoned) = (min 255 (+ (irgb-blue color) 16))

And here's an alternate postcondition

;;; Postconditions:
;;;   If (irgb-red color) >= 16
;;;     (irgb-red poisoned) = (- (irgb-red color) 16)
;;;   If (irgb-red color) < 16
;;;     (irgb-red poisoned) = 0
;;;   If (irgb-green poisoned) <= 239
;;;     (irgb-green poisoned) = (+ (irgb-green color) 16)
;;;   If (irgb-green poisoned) > 239
;;;     (irgb-green poisoned) = 255
;;;   If (irgb-blue poisoned) <= 239
;;;     (irgb-blue poisoned) = (+ (irgb-blue color) 16)
;;;   If (irgb-blue poisoned) > 239
;;;     (irgb-blue poisoned) = 255

A Solution

; Three tasks: ; Subtract 16 from the red component. ; (section irgb-subtract <> (irgb 32 0 0)) ; Add 16 to the green component. ; (section irgb-add <> (irgb 0 16 0)) ; Add 16 to the blue component. ; (section irgb-add <> (irgb 0 0 16)) (define cyanide (compose (section irgb-subtract <> (irgb 32 0 0)) (section irgb-add <> (irgb 0 16 0)) (section irgb-add <> (irgb 0 0 16))))

Another solution

(define cyanide (compose (section irgb-subtract <> (irgb 32 0 0)) (section irgb-add <> (irgb 0 16 16))))

And nother solution

(define cyanide
  (compose irgb-greener irgb-bluer irgb-darker))

This isn't as good, because a blue component of 5 gets turned into 32 rather than 21.

But life is imperfect.

Code Reading

What does the following procedure do?

(define c (compose (section min 100 <>) (section max 0 <>)))

What does the following procedure do?

(define d (compose (section max 100 <>) (section min 0 <>)))

A Solution

(section max 0 <>) computes the larger of its parameter and 0. Positive numbers stay positive. Negative numbers become 0.

(section min 100 <>) computes the smaller of its parameter and 100. Numbers below 100 stay the same.

Note

Email me questions and I will respond as quickly and in as much detail as I can. rebelsky@grinnell.edu

Enjoy the lovely sunny day.

More of Your Questions

Why do we have val as the last element of yield?

(define f1
  (lambda (x)
    1
    2
    3))

 > (f1 7)
 3
 ; Scheme gives you the *last* value computed in a procedure if there
 ; are a series of computations

 (define f2
   (lambda (x)
     (display x)
     (newline)
     2
     3))
 > (f2 7)
 7 ; purple to indicate "displayed"
 3 ; blue to indicate "returned/computed"
 > (+ 1 (f2 7))
 7 ; purple to indicate "displayed"
 4 ; blue to indicate "returned/computed"

 (define f3
   (lambda (x)
     (display x)
     (newline)))
 > (f3 7)
 7 ; purple to indicate "displayed"
 > (+ 1 (f3 7))
 ; Error!  Can't add 1 to #<void>
 > (+ 1 (newline))
 ; Error!  Can't add 1 to #<void>
 > (+ 1 (display x))
 ; Error!  Can't add 1 to #<void>

(define yield
  (lambda (val)
    (display " yields ")
    (display val)
    (newline)
    val)) ; Now the calling procedure can use `val`.
; yield is like `f3` but with `val` at the end.

Why do we have the newline?

It means that the next thing we display will be on the next line.

How do we transform colors without using lambda?

Two approaches.

Using compose and existing one-parameter procedures.

E.g., to reduce the red component by 32 ... we know how to increemnt the red component by 32 (irgb-redder), but that's backwards. What else would help? We know how to "invert" the values in a color (irgb-complement). Theory: Invert, increment by 32, and invert, we've effectively decremented.

Solution (define irgb-less-red (compose irgb-complement irgb-redder irgb-complement))

We also have some useful two-parameter procedures. irgb-add and irgb-subtract might be useful. (Component-wise addition or subtraction.)

(define irgb-less-red (section irgb-subtract <> (irgb 32 0 0)))

That will subtract 32 from the red, 0 from the green, 0 from the blue.