Many of the fundamental ideas of computer science are best learned by reading, writing, and executing small computer programs that illustrate those ideas. One of our most important tools for this course, therefore, is a program-development environment, a computer program designed specifically to make it easier to read, write, and execute other computer programs. In this class, we will often use a program development environment named DrRacket along with the programming langauge Racket, a dialect of a language called Scheme, which is itself a dialect of a language called LISP. Although Racket is a dialect of Scheme, we will often refer to it as “Scheme”.
In this lab, we explore the Scheme language and the DrRacket program development environment.
Start by comparing your answers on the self-check from the reading on DrRacket with your partner’s answers. After you’ve done the comparison and discussed any differences in your responses, you are ready to begin the computer work.
If you successfully completed the MathLAN laboratory, you should have an icon for DrRacket at the bottom of your screen. Click on that icon to start DrRacket.
If you haven’t done so already, open a terminal window and type the following. (Don’t type the dollar-sign prompt.)
$ /home/rebelsky/bin/csc151-setup
The setup script should have configured DrRacket to behave properly.
You can tell it’s been configured correctly because the top pane has
the text #lang racket
or something similar and the bottom left
says “Determine langauge from source”, as in the following screenshot.
If you don’t see that, in the Language menu, select Choose Language…. A dialog box will appear. Click the radio button next to “The Racket Language”. Then click OK. The dialog should disappear. Click the Run button to make sure the changes take effect.
(sqrt 144)
(+ 3 4)
(+ 3 (* 4 5))
(* (+ 3 4) 5)
(string-append "Hello" " " "Professor Rebelsky")
> (* (+ 4 2) 2)
> (- 1 (/ 1 2))
> (/ (- (+ 2 3) 1) 2)
> (expt 2 3)
As you may remember from the reading on DrRacket, the DrRacket window has two panes, one for definitions and one for interactions. Just as in the reading, we’ll begin by considering the interactions pane.
The best way to understand the interactions pane is to use it. So, let’s try a few examples. Type each in the interactions pane, hit Return or Enter, and see if you get the same value.
> (sqrt 144)
12
> (+ 3 4)
7
> (+ 3 (* 4 5))
23
> (* (+ 3 4) 5)
35
> (string-append "Hello" " " "Professor Rebelsky")
"Hello Professor Rebelsky"
> (string-split "Twas brillig and the slithy toves" " ")
'("Twas" "brillig" "and" "the" "slithy" "toves")
> (length (string-split "Twas brillig and the slithy toves" " "))
6
Of course, one should not just thoughtlessly type expressions and see what value they get. Particularly as you learn Scheme, it is worthwhile to think a bit about the expressions and the values you expect. The self-check in the reading asked you to predict some values. Determine whether your prediction matches what DrRacket computes.
> (* (+ 4 2) 2)
?
> (- 1 (/ 1 2))
?
> (string-length "Snicker snack")
?
> (string-split "Snicker snack" "ck")
?
> (circle 10 'solid "teal")
?
If you get an unexpected error message in one or more cases, that may be part of the intent of this exercise. Go on to the next exercise and, if you still get error messages, talk to your instructor or a class mentor.
As you may have noted, you get an error when you try to make a circle.
> (circle 10 'solid "teal")
> (square 4)
Error! circle: undefined;
Error! cannot reference an identifier before its definition
Why do you get an error? Because the circle
procedure is not
built in to standard Racket. You will soon learn how to define
your own version of circle
. Until then, you can tell DrRacket
to use the standard one. At the top of the definitions pane (but
after #lang racket
), add a line that reads
(require 2htdp/image)
Click the Run button and try making the circle again. If you get an error message, make sure that you followed the instructions for configuring DrRacket. If you still get errors, ask for help.
Now that we’ve configured DrRacket to load the image library, let’s check the image examples from the reading Enter each of the following in the interactions pane and ensure that you get the expected output.
> (circle 15 'outline "blue")
?
> (circle 10 'solid "red")
?
> (above (circle 10 'outline "blue")
(circle 15 'outline "red"))
?
> (beside (circle 10 'solid "blue")
(circle 10 'outline "blue"))
?
> (above (rectangle 15 10 'solid "red")
(beside (rectangle 15 10 'solid "blue")
(rectangle 15 10 'solid "black")))
?
Why did we have you check these examples, given that they already appear in the reading? For a few reasons. First, to remind you that you should not always trust what you read. (We will generally not intentionally deceive you, but there may be times in which we make a mistake.) Second, the same program may not behave the same for all users, depending on how their system is configured. Third, you learn a bit by typing the text by hand and reminding yourself of what you expect. You may have also identified other reasons.
(sqrt 137641)
.Of course, the computer is using some algorithm to compute values for the expressions you enter. How do you know that the algorithm is correct? One reason that you might expect it to be correct is that Scheme is a widely-used programming language (and one that we’ve asked you to use). However, there are bugs even in widely-used programs. You may recall a controversy a few years back in which it was discovered that a common computer chip computed a few specific values incorrectly, and no one had noticed. More recently, it was found that the output routine in Microsoft Excel produced the wrong output for a few values. And you may have some evidence that your faculty like to trick you. Hence, you might be a bit suspicious.
Each time you do a computation, particularly a computation for which you have designed the algorithm, you should consider how you might verify the result. (You need not verify every result, but you should have an idea of how you might do so.) When writing an algorithm, you can then also use the verification process to see if your algorithm is right.
Let’s start with a relatively simple example. Suppose we ask you to ask DrRacket to compute the square root of 137641. You should be able to do so by entering an appropriate Scheme expression:
> (sqrt 137641)
DrRacket will give you an answer. How can you test the correctness of this answer? What if you don’t trust DrRacket’s multiplication procedure? (Be prepared to answer this question for the class as a whole.)
Note: Code from the interactions pane will generally show the greater-than-sign prompts. If you don’t see such prompts and we don’t tell you otherwise, assume that code belongs in the definitions pane.
As you may recall from the reading, the upper text area in the DrRacket window, which is called the definitions pane, is used when you want to prepare a program “off-line”, that is, without immediately executing each step. Instead of processing what you type line by line, DrRacket waits for you to click on the button labeled Run (the second button from the right, in the row just below the menu bar) before starting to execute the program in the definitions pane. If you never click on that button, fine – your program is never executed.
Let’s try using the definitions pane.
a. Enter the following definitions from the reading in that pane.
#lang racket
(define trial01 11.2)
(define trial02 12.5)
(define trialO3 8.5)
(define trial04 10.6)
b. Try computing the average of the four trials in the interactions pane.
> (* 1/4 (+ trial01 trial02 trial03 trial04))
Error! trial01: undefined;
Error! cannot reference an identifier before its definition
c. It is likely that you will get an error message, just as the example suggests. Why? (Please make sure you have an answer before going on.)
d. DrRacket does not know about the information in the definitions pane until you click the Run button. Do so now. What do you think will happen if you enter the expression again?
> (* 1/4 (+ trial01 trial02 trial03 trial04))
e. Enter the expression again. (Remember, Crtl-↑ will bring back the previous expression.) Then type Enter or Return to evaluate it.
f. It is likely that you got an error message. Why? (Please make sure you have an answer before going on.)
g. As you’ve likely hypothesized, the definition for trial03
was
mistakenly typed as trialO3
. (That is, it contains the letter
“O
” rather than the numeral “0
”.) Correct the definition
and click Run again. Then try entering the expression again.
h. You’ve computed the average trial score. Compute the maximum.
i. You’ve copied code from elsewhere. That means that you have a responsibility to insert a “comment” that cites the original authors. In Scheme, comments start with a semicolon and end at the end of the line. Here’s one possible citation.
; The following definitions are taken from
; Curtsinger, C., Davis, J., Hamid, F., Klinge, T., Rebelsky, S., and Weinman, J. (2019).
; An introduction to Racket and DrRacket. Online document available at
; _URL_.
Insert that citation, using the appropriate URL.
Note: You may encounter different expectations about the appropriate form of citations. Make it a habit to start by copying and pasting the URL of a document whenever you copy and paste code. Doing so shows that you have the appropriate intent. If you are expected to provide a full citation, you can go back later and add it.
Let’s try another definition. Define name
as your name in quotation marks.
For example,
(define name "Professor Rebelsky")
Click Run and then find the value of the following expression.
> (string-append "Hello " name)
Next, find the number of characters in the string with the following expression.
> (string-length name)
a. Open a new definitions tab with File > New Tab or Ctrl-T.
b. In the definitions pane, write definitions for blue-circle
, a
solid circle of radius 10, red-square
, a solid red square of
edge-length 20, and black-rectangle
, an outlined black rectangle
of width 30 and height 10.
#lang racket
(require 2htdp/image)
(define blue-circle ...)
(define red-square ...)
(define black-rectangle ...)
c. Click Run and make sure that each of those values is defined correctly.
> blue-circle
?
> red-square
?
> black-rectangle
?
d. You may recall that we can combine images with above
and
beside
. Predict the output of each of the following.
> (beside blue-circle red-square)
?
> (beside red-square blue-circle)
?
> (beside blue-circle blue-circle)
?
> (beside blue-circle red-square blue-circle red-square blue-circle)
?
> (beside red-square black-rectangle)
?
> (above blue-circle black-rectangle)
?
> (beside red-square (above black-rectangle black-rectangle) red-square)
?
> (above black-rectangle (beside red-square blue-circle))
?
Let’s make sure that you can save and restore the work you do in the
definitions pane. The source and destination names are displayed as paths
below, but DrRacket uses a file browser to help you choose a file to load or
save. Remember that the ~
character is shorthand for your home directory,
which you can access by clicking the Home button on the left of the file
browser DrRacket shows when you are saving or loading files.
~/Desktop/shapes.rkt
~/Desktop/shapes.rkt
.(above black-rectangle (beside red-square blue-circle))
.You can also use the definitions you created without having them open in the definitions pane.
a. Add a new shape definition for a shape that we’ll call my-shape
to shapes.rkt
. You can make the shape whatever type, color, and
size you want.
(define my-shape ...)
b. Verify that you’ve created the shape correctly by clicking Run
and entering my-shape
.
> my-shape
?
c. Add the following incantation to the top of your definitions window, immediately below the line that says #lang racket
. This line tells Racket that other programs can access your definitions.
(provide (all-defined-out))
d. Save shapes.rkt
, but do not run it.
e. Quit DrRacket.
f. Restart DrRacket. You should have a new, Untitled, environment.
g. In the interactions pane of the new window, type my-shape
. You should get an error message, which tells you that my-shape
is not yet defined.
h. In the interactions pane of the new window, type
> (require Desktop/shapes)
i. In the interactions pane, type my-shape
. You should now see a value.
In the future, we will be creating some .rkt
files that contain definitions that we change infrequently. Those files we will require, rather than open.
As you’ve learned, Scheme expects you to use parentheses and prefix notation when writing expressions. What happens if you use more traditional mathematical notation? Let’s explore that question.
Type each of the following expressions at the Scheme prompt and see what reaction you get.
(2 + 3)
7 * 9
sqrt(49)
(+ (87) (23))
You may wish to read the notes on this problem for an explanation of the results that you get.
If you find that you have finished this laboratory before the end of class, you may try any of the following exercises.
Write instructions to make an image of a simple house. If you are
so inclined, you can use the (triangle edge-length 'solid "color")
procedure to create a triangle for the roof.
As you observed in the primary exercises for this laboratory, you can use the definitions pane to name values that you expect to use again (or that you simply find it more convenient to refer to with a mnemonic). So far, the only numbers we’ve named are simple values. However, you can also name the results of expressions.
a. In the definitions pane, write a definition that assigns the
name seconds-per-minute
to the value 60.
b. In the definitions pane, write a definition that assigns the
name minutes-per-hour
to the value 60.
c. In the definitions pane, write a definition that assigns the
name hours-per-day
to the value 24.
d. In the definitions pane, write a definition that assigns the
name seconds-per-day
to the product of those three values. Note
that you should use the following expression to express that product.
(* seconds-per-minute minutes-per-hour hours-per-day)
e. Run your definitions and confirm in the interactions pane that
seconds-per-day
is defined correctly.
f. Optionally, create a .rkt
file to store those definitions.
Let’s play for a bit with how one might use DrScheme to compute
grades. (We teach you this, in part, so that you can figure out
your estimated grade in this class and others.) Let’s define five
names, grade1
through grade5
that potentially represent grades
on five homework assignments.
(define grade1 95)
(define grade2 93)
(define grade3 105)
(define grade4 30)
(define grade5 80)
Looking at those grades, you might observe that the student seems to have spent a bit of extra work on the third assignment, but that the extra work so disrupted the student’s life that the next assignment was a disaster. (You may certainly analyze the grades differently.)
a. Write a definition that assigns the name average-grade
to the
average of the grades.
Many faculty members discard these “outliers”, with a grading policy of “I take the average of your grades after dropping the highest grade and the lowest grade”.
b. Write a definition that assigns the name highest-grade
to a
computed highest grade. (That is, highest-grade
should remain
correct, even if I change the values associated with grade1
through
grade5
.) In writing this definition, you may find the max
procedure useful.
c. Write a definition that assigns the name lowest-grade
to a
computed lowest grade. You may find the min
procedure useful.
d. Write a definition that assigns the name modified-average
to
the modified average grade (that is, the grade that results from
dropping the lowest and highest grades and then averaging the
result).
> (2 + 3)
Error! application: not a procedure;
Error! expected a procedure that can be applied to arguments
Error! given: 2
Error! arguments...:
When the Scheme interpreter sees the left parenthesis at the beginning of the expression (2 + 3)
, it expects the expression to be a procedure call, and it expects the procedure to be identified right after the left parenthesis. But 2
does not identify a procedure; it stands for a number. (A “procedure application” is the same thing as a procedure call.)
> 7 * 9
7
#<procedure:*>
9
In the absence of parentheses, the Scheme interpreter sees 7 * 9
as
three separate and unrelated expressions – the numeral 7
; *
, a name
for the primitive multiplication procedure; and 9
, another numeral. It
interprets each of these as a command to evaluate an expression:
“Compute the value of the numeral 7
! Find out what the name *
stands for! Compute the value of the numeral 9
!” So it performs the
first of these commands and displays 7
; then it carries out the second
command, reporting that *
is the name of the primitive procedure *
;
and finally it carries out the third command and displays the result,
9
. This behavior is confusing, but it’s strictly logical if you look
at it from the computer’s point of view (remembering, of course, that
the computer has absolutely no common sense).
> sqrt(49)
#<procedure:sqrt>
Error! application: not a procedure;
Error! expected a procedure that can be applied to arguments
Error! given: 49
Error! arguments...: [none]```
As in the preceding case, DrRacket sees sqrt(49)
as two separate
commands: sqrt
means “Find out what sqrt
is!” and (49)
means
“Call the procedure 49
, with no arguments!” DrRacket responds to
the first command by reporting that sqrt
is the primitive procedure for
computing square roots and to the second by pointing out that the number
49
is not a procedure.