Skip to main content

Laboratory: Input, Output, and Side Effects

This lab may look long, but most of the problems should be short.

Summary: In this laboratory, you will explore procedures that have side effects, particularly procedures that generate textual output or read input. Along the way, you should reflect on the differences between the original “just compute a result” model you’ve mostly used in Scheme, and this new model.

Preparation

a. If you haven’t already, make a copy of io-lab.rkt, which contains the code from the readings as well as some additional code.

b. Review the file to see what has been added. Make sure that you understand the purpose of the new procedures.

Exercises

Exercise 1: Predicting Order of Evaluation

Predict the order in which operations are done in each of the following cases. After you’ve made each prediction, check your answer experimentally. (It’s important to make the prediction first so that you think about order of evaluation.)

a. (add (multiply 1 2) (multiply 3 4))

b. (multiply (add 1 2) (add 3 4))

c. (divide (add 1 2) (add 3 4))

d. (add (multiply 1 (multiply 2 3)) (multiply 4 5))

e. (add (multiply 1 2) (multiply 3 (multiply 4 5)))

Exercise 2: Extended Color Operations

a. Look at the code for irgb-redder_* and irgb-lighter_*. Explain in your own words what each procedure does.

b. What output do you expect for the following expression?

> (irgb-redder_* (irgb 220 0 150))

c. Check your answer experimentally.

d. What output do you expect for the following expression?

> (irgb-lighter_* (irgb-redder_* (irgb 220 0 150)))

e. Check your answer experimentally.

Exercise 3: Multiple Yield Procedures

a. Look at the code for yield and irgb-yield. Explain why we need irgb-yield in addition to yield.

b. Rewrite irgb-redder_* to use yield instead of irgb-yield.

c. Rerun the example from above.

> (irgb-redder_* (irgb 200 0 150))

d. Restore the call to irgb-yield in irgb-redder_*.

e. Given this additional information, revisit your answer to part a of this exercise.

Exercise 4: Annotating Your Own Procedures

As you may have noted, we’re using the _* suffix to indicate that we have an “annotated” version of a procedure, one that tells us what we’re doing.

a. Write your own irgb-darker_* procedure using irgb-lighter_* as a pattern.

b. Write your own irgb-average_* procedure.

Here’s a sample call to irgb-average_*.

> (irgb-average_* (color->irgb "red") (color->irgb "green"))
Output! Averaging 255/0/0 and 0/128/0 yields 127/64/0
8339200

Exercise 5: Building New Colors

Sometimes it’s helpful to see the construction of a new color step by step. In a recent laboratory, we had to write a procedure to swap the red and blue components of a color. Here’s how we might have written that procedure.

(define irgb-swap-rb
  (lambda (color)
    (irgb (irgb-blue color)
          (irgb-green color)
          (irgb-red color))))

What happens in this procedure? Let’s use annotated versions of the various procedures to figure out just what’s happening.

a. Add the following procedure to your definitions pane. (This procedure is just the one above, but with the annotated versions of the various procedures it’s calling.)

(define irgb-swap-rb 
  (lambda (color)
    (irgb_* (irgb-blue_* color)
            (irgb-green_* color)
            (irgb-red_* color))))

b. Predict the output you will get when you enter the following in the interactions pane.

> (irgb-swap-rb (irgb 200 50 100))

c. Check your answer experimentally.

Exercise 6: Exploring Composition

Consider the following procedure definition.

> (define fun6 (compose irgb-darker_* irgb-lighter_*))

a. What output do you expect for the following expressions?

> (fun6 (irgb 50 100 150))
> (fun6 (irgb 0 0 0))
> (fun6 (irgb 255 255 255))
> (fun6 (irgb 300 300 300))

b. Check your answer experimentally.

Exercise 7: Exploring Sectioning

Consider the following procedure definitions.

> (define fun7a (section irgb-average_* <> (irgb 100 100 100)))
> (define fun7b (section irgb-average_* (irgb 200 0 200) <>))
> (define fun7c (compose fun7a fun7b))
> (define fun7d (compose fun7b fun7a))

a. What output do you expect for each of the following expressions? (You need not give exact numbers; just indicate what order you things to be averaged.)

> (fun7a (irgb 0 100 200))
> (fun7b (irgb 0 100 200))
> (fun7c (irgb 0 100 200))
> (fun7d (irgb 0 100 200))

b. Check your answers experimentally.

Exercise 8: Exploring User Input

While the read procedure may seem straightforward, it has some subtleties that may take a bit of exploration to understand.

a. Add the following procedure to your definitions pane.

(define get-number
  (lambda ()
    (display "Please enter a number: ")
    (read)))

b. What do you think will happen when you enter the following expression? (Assume that you type 5 in response to the prompt.)

> (get-number)

c. Check your answer experimentally.

d. What do you think will happen when you enter the following expression? (Assume that you type 5 10 in response to the prompt.)

> (get-number)

e. Check your answer experimentally.

f. What do you expect to happen if you call (get-number) one more time? (Assume that you answer 5 in response to the prompt.)

g. Check your answer experimentally.

h. What, if anything, did you learn from this exercise?

Exercise 9: Multiple Inputs, Revisited

As we noted in the reading, one of the complications of operations with side effects is that the order in which we do those operations can have significant impact. Let’s check a particular example.

a. Add yet another subtraction procedure to your definitions pane.

(define sub
  (lambda (x y)
    (- x y)))

b. What result do you expect to get from the following expression? Assume that you answer 5 and then 10 when prompted for numbers.

> (sub (get-number) (get-number))

c. Check your answer experimentally.

What do you expect to happen if you enter 5 10 in response to the first prompt?

d. Check your answer experimentally.

Exercise 10: Working from the Command Line

a. Here are the two programs from the reading.

#lang racket
(display "Please enter your name: ")
(define name (read-line))
(display "Hello, ")
(display name)
(newline)
#lang racket
(display "Please enter a number: ")
(define val (read))
(display val)
(display " cubed is ")
(display (* val val val))
(newline)

a. Save the first as /home/username/Desktop/hello.rkt.

b. Save the second as /home/username/Desktop/cube.rkt.

c. Open a terminal window and type the following instruction (without the dollar-sign prompt).

$ cd /home/username/Desktop

d. Type the following instruction and observe what happens. (Once again, don’t type the dollar-sign prompt.)

$ racket hello.rkt

e. Type the following instruction and observe what happens. (We think you know what belongs in this parenthetical remark.)

$ racket cube.rkt

Exercise 11: A New Program

Consider the following program.

#lang racket
(require gigls/unsafe)
(display "Input image: ")
(image-show (image-variant (image-load (read-line)) irgb-complement))

a. What do you expect to happen when you run this program in DrRacket? (You can type the file name /home/rebelsky/Desktop/kitten.jpg.)

b. Check your answer experimentally.

c. Save the file on your desktop and run it from the terminal.

Exercise 12: Working from the Command Line, Revisited

a. Save the following program as /home/username/Desktop/invert.rkt.

#lang racket
(require gigls/unsafe)
(display "Input image: ")
(define infile (read-line))
(display "Output image: ")
(define outfile (read-line))
(image-save (image-variant (image-load infile) irgb-complement) outfile)

b. Predict what this program does.

c. Check your answer experimentally.

For those with Extra Time

If you find that you have extra time, you might want to consider doing the extra problems, which emphasize algorithms and programming, or the explorations, which are more open ended.

Extra 1: Exploring Order of Evaluation, Revisited

Right now, we get a lot of output from the annotated arithmetical procedures. For example,

> (add 2 4)
Output! add 2 and 4
Output!   yields 6
6

Rewrite the procedures so that we get much more concise, one-liner, output in normal mathematical form. For example,

> (add 2 4)
Output! 2 + 4 = 6
6

Extra 2: Custom Filters

Write a program that prompts the user for the red, green, and blue components of a color, and also for an image. Your program should build a color from the three numbers, load the image, and then make a new image by averaging every color in the image with that newly built color.

$ racket extra2.rkt
What is the red component of your favorite color? 200
What is the green component of your favorite color? 50
What is the blue component of your favorite color? 240
Please enter the full path to an image:  /home/rebelsky/Desktop/kitten.jpg
Thanks!  In a minute, you'll see a new version of that image.

Explorations

Exploration 1: Mad Libs

Using the input and output techniques we’ve started to explore, write a simple Mad Libs game. If you’ve never played Mad Libs, it involves taking a fixed set of text and prompting the user for certain parts of speech that get filled in. Here’s a fairly lame example.

Output! Give me some words and I'll tell you a story.
Output! Animal:  Heffalump
Output! Another Animal:  Bandersnatch
Output! City:  Grinnell
Output! Color:  blue
Output! Month:  May
Output! Adjective:  frumious
Output! Exclamation:  Wicked Neat!
Output! -ing form of verb:  excavating
Output! verb:  program

Output! Here's your story ...

Output! One day in May, Winnie the Heffalump was excavating through
Output! the wilds of Grinnell.  Winnie thought "The month of May is so blue. I
Output! wish I could program."  Winnie felt very frumious.  As Winnie continued
Output! excavating, Grinnell's famous Bandersnatch appeared.  "Wicked Neat!" thought
Output! Winnie.

As you might guess, the code for this is fairly straightforward.

#lang racket
(display "Give me some words and I'll tell you a story.") (newline)
(display "Animal: ")
(define animal (read-line))
...
(display "One day in ")
(display month)
(display ", Winnie the ")
(display animal)
...