Chapter 3 Programming in Racket
Section 3.1 The Racket Language & DrRacket
The Racket language has been around for a while, it is a descendant of Lisp 1 , which is a very important language in the history of computer science. For right now, we’ll just say that Racket is a functional programming language.
We will be using an Integrated Development Environment (or IDE) called DrRacket to do all of our Racket programming. When you start up DrRacket, the main window will be split in half. The top half is called the definitions pane, this is where we will eventually be writing all our custom-defined functions, but for now, we will be focusing on the bottom half, the interactions pane. In the interactions pane, you’ll see a
>
. This is called the prompt, because it is essentially prompting (or asking) you for input. You can type any valid Racket statement at the prompt, hit enter, and the DrRacket will interpret the statement, and provide the result below. So what do Racket statements look like?Subsection 3.1.1 Racket Syntax
One of the most appealing features of Racket is the simplicity of its syntax. All statements in Racket follow this rule:
(FUNCTION ARGUMENT_0 ARGUMENT_1 ...)
Remember above when I described Racket as a functional language? Well a big piece of that is every statement is the application of a function to a series of inputs, which we can also refer to as arguments. And in Racket, every combination of function name and input(s) is placed inside a set of
()
. Here are some examples of valid racket statements:(expt 2 3)
: Return \(2^3\text{.}\)(sqrt 16)
: Return \(\sqrt{16}\text{.}\)(max 4 78 -1.4)
: Return the largest value out of 4, 78, and -1.4.(+ 123 57)
: Return the sum of 123 and 57.
The keen observer will note the repeated use of the word return in the list above. This is another tenet of functional programming, that every application of a function results in a new value being returned to either be used in other programming statements or displayed to the user. In the interactions pane of DrRacket, the final return value for an entered statement will be printed to the screen below the statement.
The first few functions in the list above probably seem resaonable, but the last one might stick out. Put together, this means that every statement including basic arithmetic, must follow this pattern. So
(+ 123 57)
, don’t you mean \(123 + 57\text{?}\) Nope. There are two very improtant things to keep in mind when programming in Racket; 1) Every statement is the application of a fucntion, and 2) the one rule of Racket syntax: (FUNCTION ARGUMENT_0 ARGUMENT_1 ...)
(+ 123 57)
could also be thought of as applying the +
function to the arguments 123
and 57
. This type of arithmetic notation, where the operator comes before the operands, is called prefix notation. The kind of notation you’re used to in math class, where the operator goes between the operands, is called infix notation.There are a few big advantages to treating arithmetic operations in this one. First off, it means that we don’t need to mix and match syntactic rules for different kinds of operations. This provides a kind of clarity and simplicity that is often missing in programming languages. Second, it allows for statements that look like this: The
(+ 123 57 9.32 1.1618 -3.14 274578)
+
function (and the other basic arithmetic operators: - * /
) can take 1 or more arguments. The above statement, in infix would be: \(123 + 57 + 9.32 + 1.1618 + -3.14 + 274578\text{,}\) which requires repeating the \(+\) symbol multiple times.The final advantage to prefix notation for basic arithmetic has to do with combining multiple operations. Consider the following infix expression:
\begin{equation*}
56 + 23 * 7
\end{equation*}
If you were to read the expression from left to right, you would first do \(56 + 23\text{,}\) get \(79\) and then multiply that by \(7\) to get \(553\text{.}\) “But wait, that’s not right.” you’d say. “You forgot about PEMDAS! You have to do \(23 * 7\) first, get \(161\text{,}\) and then do \(56+161\) to get \(217\text{.}\)” The order of operations, as we know it, exists because infix notation is inherently ambiguous, in order for the rules of mathematics to not fall apart, we have to promise that we’ll adhere to a specific set of operation order, or precedence. Let’s look at the same expression in Racket. At first glance, you might try to do something like this: But that violates the one rule of Racket syntax. We can’t have a function without it being enclosed within
(+ 56 23 * 7)
()
, but there’s a rogue *
! We need to turn *
into a function that uses prefix notation. The arguments to *
are 23
and 7
, which gives us (* 23 7)
. What about +
? Well, the arguments to +
are 56 and the result of (+ 23 7)
, which gives us: (+ 56 (* 23 7))
What does this have to do with PEMDAS again? Well remember the P stands for Parentheses, and since all Racket statements must be contained within
()
, it means that we have to be explicit with the ordering of arithmetic operations, so we never have to worry about ambiguously combining them. (+ 56 (* 23 7))
is distinct from (* (+ 56 23) 7)
.Subsection 3.1.2 Racket Function Starter Pack!
Now that we’ve got the basics of Racket syntax down, it’s time to learn about some of the functions that already exist in Racket for us to use.
Subsubsection 3.1.2.1 Arithmetic Operators
(+ a b)
: Addition, takes 1 or more arguments and returns their sum.(- a b)
: Subtraction, takes 1 or more arguments and returns their difference. If only one argument is provided, returns \(0-a\text{.}\) If more than two arguments are provided, the subtraction is performed left to right. i.e.(- a b c)
will perform \(a-b\) and then subtract \(c\) from that result.(* a b)
: Multiplication, takes 1 or more arguments and returns their product.(/ a b)
: Division, takes 1 or more arguments and divides. If only one argument is provided, returns \(1/a\text{.}\) If more than two arguments are provided, the division is performed left to right. i.e.(/ a b c)
will perform \(a/b\) and then divide that value by \(c\text{.}\)
Subsubsection 3.1.2.2 Basic Arithmetic Functions
(abs a)
: Absolute Value, takes 1 argument and returns its absolute value.(sqrt a)
: Square Root, takes 1 argument and returns its square root.(expt base power)
: Exponent, takes 2 arguments, returns \(base^{power}\text{.}\)(remainder a b)
: Remainder, takes 2 arguments, returns the remainder when dividing \(a/b\text{.}\) Examples:(remainder 25 5)
returns0
(remainder 25 4)
returns1
(remainder 5 25)
returns5
(quotient a b)
: Integer Division, takes 2 arguments, returns the integer part when dividing \(a/b\text{.}\) Any digits that would come after the ones place are ignored. Examples:(quotient 25 5)
returns5
(quotient 25 4)
returns6
(quotient 5 25)
returns0
Section 3.2 Combining Functions
Earlier, I mentioned that every function in Racket returns a value. This becomes super useful because if every function returns a value, then that value could be used as an input to another function. Combining functions just means using a function as an argument. We’ve already seen an example of this: The two arguments to
(+ 56 (* 23 7))
+
are 56
and (* 23 7)
. Importantly, Racket cannot evaluate +
until after it gets the return value from *
. Now that we know of more functions, we can create more complex statements by using functions as arguments.