Chapter 1.1.2 - 1.1.4 - Environment, Functional Programming and Substitution
Refreshing Myself On the Basics
I made some revisions this morning to how I’ll work through these exercises. Instead of running Racket in a Docker container, which was using way too much overhead, and taking forever to spin up, I just installed Racket directly on the VM and will run the Racket programs that way.
1sudo apt update && sudo apt install -y racket
2
3# install sicp package:
4raco pkg install --auto sicp
5
6# write racket program to do exercises
7vim exercises/chapter1-exercises.rkt
8
9# run the program
10racket exercises/chapter1-exercises.rkt
11
12# alternatively, use the repl:
13
14racket
15
16# use the sicp package, specifically:
17
18racket -I sicp
I worked through the basic exercises to help me understand at least the syntax of lisp, which is pretty straightforward. In (+ 10 2), the + is the operator, and the other elements in the expression are called operands.
The value of a combination is obtained by applying the procedure specified by the operator to the arguments that are the values of the operands.
This is known as prefix notation.
define can be used to name objects or things. For example:
1(define size 3)
size will return 3:
1size
23
size can then be used in new expressions in the program:
1(+ size 6)
29
I had to remind myself of the basic formulas for finding the area and circumference of a circle, as it’s been awhile since I’ve had to think about this stuff.
$$A = \pi r^2$$ $$C = 2\pi r$$
1(define pi 3.14159)
2pi
3;; returns
43.14159
5
6(define radius 10)
7radius
8;; returns
910
10
11(* pi (* radius radius))
12314.159
13
14(define circumference (* 2 pi radius))
15circumference
1662.8318
This allows us to create the building blocks for larger programs, by being able to define functions that can be used later in the program. Building complex elements from simpler elements. Another way I’ve heard this talked about is “functional programming”.
Next they get into procedure definitions. Using define allows you to associate names with values, and by extension, creating a procedure definition means to define a procedure, rather than just a value.
The procedure they start with defining is square.
1> (define (square x) (* x x))
2> square
3;; returns
4> #<procedure:square>
So just in the same way that we define something to be a value using (define size 3), we are defining the procedure (square x) to be (* x x):
(define (square x) (* x x))
| | | | | |
To square something, multiply it by itself.
x is like a pronoun in natural language. It’s a placeholder that will be replaced with an actual value.
There are three parts to a procedure definition:
(define ([name] [formal parameters]) [body])
The name is obviously what we are calling the procedure definition.
The formal parameters are the placeholders that receive values when the procedure is called.
And the body is the expression that defines what to do with those formal parameters.
When we call the procedure, we pass in arguments (the actual values), which get bound to the formal parameters. In this case:
1(square 3)
29
The logical flow
We defined square to be:
1(define (square x) (* x x))
So when we run (square 3) what’s actually happening is:
3 is the substitution for x in square x.
Which gives us:
square 3
3 is then used in the body:
3 * 3
Which returns:
9
I do at least understand this concept when it comes to defining functions in python. For example, to do the same thing in python:
1>>> def square(x):
2... return x * x
3...
4>>> square(3)
59
x is the parameter, and return x * x is the body.
I can imagine someone reading this and thinking I’m going through way more trouble than needed to understand the basics. Though first, why would someone be reading this obscure text on the first chapter of SICP, and second, I’m realizing I need to go very slowly and methodically to understand these basic building blocks. Otherwise, the actual exercises will be incredibly daunting.
Even just copying stuff into the repl made me realize the importance of the order in which these are defined:
1> (define (sum-of-squares x y)
2 (+ (square x) (square y)))
3> sum-of-squares
4#<procedure:sum-of-squares>
5
6> (sum-of-squares 3 4)
7square: undefined;
8 cannot reference an identifier before its definition
9 in module: top-level
10 [,bt for context]
11
12;; here I realized I had started a new repl and my
13;; initial definition of the square procedure disappeared
14;; from memory, or the environment. croosh mistake.
15
16> (define (square x) (* x x))
17
18> sum-of-squares
19#<procedure:sum-of-squares>
20
21> (sum-of-squares 3 4)
2225
23
24> (define (function a)
25 (sum-of-squares (+ a 1) (* a 2)))
26
27> function
28#<procedure:function>
29
30> (function 5)
31136
Soundtrack: Mystic Familiar by Dan Deacon