Literal data
Number
1 2 -5 2.7122 2/3 3e10
Boolean
#t #f
String
"cat" "dog" ""
Function application: (op exp-1 exp-2 ...)
What do the following examples evaluate to?
(4 + (5 * 6))
(< 4 5)
(> 4 5)
(/ 36 4)
((* 2 3))
(string-length "duck")
(string-append "hello" "world")
(string-length "")
(string<? "canoe" "can")
(string<? "canoe" "cat")
For more operations and documentation on them, see DrScheme's Help Desk.
Conditional: case analysis
(cond
(test-1 result-1)
(test-2 result-2)
...)
What string does the following evaluate to, assuming { x = 1, y = -2 }
(cond
((and (> x 0) (> y 0)) "I")
((and (< x 0) (> y 0)) "II")
((and (< x 0) (< y 0)) "III")
((and (> x 0) (< y 0)) "IV")
(else "axis"))
What number does the following evaluate to, assuming { b = -3, a = 4, c = 2 }
(cond
((> (* b b) (+ 4 a c)) 2)
((= (* b b) (+ 4 a c)) 1)
(else 0))
Why did I start with only one parenthesis when I wrote
(test-1 result-1)
, yet
I started with two parentheses in ((> (* b b) (+ 4 a c)) 2)
?
Function definition:
(define f
 (lambda (x-1 x-2 ...)
 body))
;; discriminant : Num * Num * Num -> Num
;; Produces the discriminant for ax^2 + bx + c
(define discriminant
(lambda (a b c)
(- (* b b) (* 4 a c))))
Pairs
(cons exp-1 exp-2)
(car exp)
(cdr exp)
(pair? exp)
(define paired
(cons (cons 0 1) (cons (cons 2 3) (cons 4 5))))
What value do each of the following evaluate to? (Remember to look at expressions starting innermost first.)
(car (cdr paired))
(cdr (car paired))
car
and cdr
for our purposes)
to destructure complex datacond
for case analysis; make sure you know how
to distinguish each case in the "data definition" properlyrequire
form to pull in
bindings from other modules,
and the provide
form
to export your definitions to the outside world.
(module name lang
(require module)
(provide name)
definitions ...
expressions ...
)
In this class, we will tend to
require
") variables from other modules by refering to the module's file name
(lib "eopl.ss" "eopl")
as the lang
for the modules
(module ...)
" language to develop code within a module. (In the "(module ...)
" language, you can access variable definitions that would normally be hidden with in the module.)
;; Save this in a file, "bootcamp1.scm"
(module bootcamp1 (lib "eopl.ss" "eopl")
(provide hi-there)
(define hi-there "Hi There!")
(define i-am-hidden "You normally can't see me.")
)
;; Save this in a file, "bootcamp2.scm"
(module bootcamp2 (lib "eopl.ss" "eopl")
(require "bootcamp1.scm")
(display hi-there)
(newline)
;;; Does the below work when uncommented?
; (display i-am-hidden)
)
Try hitting the "Run" button while looking at "bootcamp1.scm"
,
and typing the following expressions at the read-eval-print loop:
> hi-there
...
> i-am-hidden
...
What about doing the same, but with "bootcamp2.scm"
?
;; A LoN (list of numbers) is one of:
;; - '()
;; - (cons Number LoN)
Example lists:
;; the empty list []
(define list-1
'())
;; the three element list [1, 2, 3]
(define list-2
(cons 1 (cons 2 (cons 3 '()))))
;; the two element list ["hello", "world"]
(define list-3
(cons "hello" (cons "world" '())))
Is paired
a LoN
? Why or why not?
Is list-2
a LoN
? Why or why not?
Is list-3
a LoN
? Why or why not?
The list
procedure provides a useful shorthand
for the common pattern (cons A (cons B ... (cons Z '())))
You use it like so: (list A B ... Z)
;; the empty list []
(define list-4
(list))
;; the three element list [1, 2, 3]
(define list-5
(list 1 2 3))
;; the two element list ["hello", "world"]
(define list-6
(list "hello" "world"))
BEWARE: you almost never want to say list
in the definition of a
function; if you are tempted to write list
, first ask yourself,
"wait, do I mean cons
here?"
list
takes m arguments to make a list of length m cons
always takes two arguments; when the second argument is a list
of length n, cons
makes a list of length n+1;; lon-add3 : LoN -> LoN
;; Produces a new list where each number in l has been incremented by 3
;; examples: (lon-add3 (list 5 7 10)) is (list 8 10 13)
;; (lon-add3 '()) is '()
(define lon-add3
(lambda (l)
(cond
((null? l) ...)
((pair? l) ... (car l) ...
... (lon-add3 (cdr l)) ...))))
Note that for this class of data, else
is just
as good as pair?
in the last clause. (Why is this true?)
But that might not be true in general; double-check your case analysis when you start working with a new class of data.
;; lon-sum : LoN -> Number
;; Produces the sum of all the numbers in l
;; examples: (lon-sum (list 5 7 10)) is 22
;; (lon-sum '()) is 0
(define lon-sum
(lambda (l)
(cond
((null? l) ...)
(else ... (car l) ...
... (lon-sum (cdr l)) ...))))
;; A LoS (list of strings) is one of:
;; - '()
;; - (cons String LoS)
;; append-the-strings : LoS -> String
;; Produces the concatenation of all the strings in l
;; examples: (append-the-strings (list "hi" "thar")) is "hithar"
;; (append-the-strings '()) is ""
(define append-the-strings
(lambda (l)
(cond
((null? l) ...)
((pair? l) ... (car l) ...
... (append-the-strings (cdr l)) ...))))
A common way to approach solving these problems: pretend that the
function "magically works", at least for the recursive call. Based on
that assumption, figure out how to combine the result of the recursive
call with the other values you have in your hands (usually the car
and maybe some extra data) to get the desired answer. It is just as
"magical" as mathematical induction!
Consider the following specification and template:
;; how-many-of : Number * LoN -> Number
;; Produces the number of times i occurs in the list l
;; examples: (how-many-of 3 (list 1 2 3 2 4 3)) is 2
;; (how-many-of 4 '()) is 0
(define how-many-of
(lambda (i l)
(cond
((null? l) ...)
(else ... (car l) ...
... (how-many-of (cdr l)) ...))))
What are some problems with the above template? Do these code fragments make sense?
Exercises:
(Remember: when you're about to write a function, start off by writing a few tests for the function! Then see if you can develop the "template" or "skeleton" for the function, based solely on the data definition for one of its inputs.)Ex 1. Implement any-greater-than-five
,
which has the following contract and purpose:
;; any-greater-than-five : LoN -> Boolean
;; Produces #t iff there exists i in l such that i > 5
;; examples: (any-greater-than-five (list 1 5 3)) is #f
;; (any-greater-than-five (list 1 6 2)) is #t
;; (any-greater-than-five '()) is #f
(define any-greater-than-five
(lambda (l)
...))
Ex 2. Implement first-negative
,
which has the following contract and purpose:
;; first-negative : LoN -> Number or #f
;; Produces the first negative number in l,
;; or #f if there are no negative numbers in l
;; examples: (first-negative (list 1 -2 -3 5)) is -2
;; (first-negative (list 10 20 30)) is #f
;; (first-negative '()) is #f
(define first-negative
(lambda (l)
...))
Ex 3. Implement first-less-than
, which has the following contract and purpose:
;; first-less-than : Number * LoN -> Number or #f
;; Produces the first number j in l such that j < i.
;; (Produces #f if no such j exists in l)
(define first-less-than
(lambda (i l)
...))
Ex 4. Implement first-longer-than
, which has the following contract and purpose:
;; first-longer-than : Number * LoS -> String or #f
;; Produces the first string s in l such that the length of s
;; is greater than i; produces #f if no such s exists.
(define first-longer-than
(lambda (i l)
...))
Ex 5. Implement all-greater-than
, which has the following contract and purpose:
;; all-greater-than : Number * LoN -> LoN
;; Produces a list with all and only the elements from l that
;; are greater than i
;; examples: (all-greater-than 5 (list 1 6 2 8 3)) is (list 6 8)
(define all-greater-than
(lambda (i l)
...))
Ex 6. Here's an example of why just guessing the code to write is not going to work:
;; lon-append : LoN * LoN -> LoN
;; Produces a list that has all of the elements of l-1
;; first, followed by all the elements of l-2.
;; examples: (lon-append (list 5 7 8) (list 4 9)) is (list 5 7 8 4 9)
;; (lon-append '() '()) is '()
(define lon-append
(lambda (l-1 l-2)
(cond
((null? l-2) l-1)
((pair? l-1) (cons (car l-2)
(lon-append (cdr l-1) (cdr l-2)))))))
What problems do you see with the above implementation of
``lon-append``? What inputs can you come up with that cause it to
fail, and in what ways?
Implement `lon-append`
(Start from scratch rather than trying to adapt the broken code above.)
A LoLoN
is defined as follows:
;; A LoLoN (list of lists of numbers)
;; - '()
;; - (cons LoN LoLoN)
Exercises:
Ex 7. Give three distinct examples of LoLoNs
.
Ex 8. Implement sum-the-lons
(first fill in examples,
then write the code):
;; sum-the-lons : LoLoN -> Number
;; Produces the sum of summing all the lists in lol
;; examples: ???
(define sum-the-lons
(lambda (lol)
...))
(Rule of thumb: make a separate function to process each kind of
data, rather than trying to shoehorn all of the code into one
function. In this particular case, don't forget about lon-sum
above!)
Ex 9. Implement largest-of
;; largest-of : LoLoN * Number -> Number
;; Produces the largest number in lol greater than j.
;; If no such number exists, produces j.
(define largest-of
(lambda (lol j)
...))
(Don't forget the rule of thumb from Ex 8!)
A LoPT
is defined as follows:
;; A LoPT (list of paired things) is one of:
;; - '()
;; - (cons (cons Number String) LoPT)
Ex 10. Give three examples of LoPTs
.
Ex 11. Make up examples for lopt-lookup
and implement it:
;; lopt-lookup : LoPT * Number -> String or #f
;; Produces the first string paired with a-num in a-lopt, or #f if
;; a-num does not occur in a-lopt.
(define lopt-lookup
(lambda (a-lopt a-num)
...))
Hint: here is a template for processing a LoPT
(define lopt-lookup
(lambda (a-lopt)
(cond
((null? a-lopt) ...)
(else ... (car (car a-lopt)) ...
... (cdr (car a-lopt)) ...
... (lopt-lookup (cdr a-lopt)) ...))))
Ex 12. Implement plot-loopkup
, where a PLoT
is as follows:
;; A PLoT (pairwise list of things) is one of:
;; - '()
;; - (cons Number (cons String PLoT))
;; plot-lookup : PLoT * Number -> String or #f
;; Produces the string that follows the first occurrence of a-num
;; in a-lopt, or #f if a-num does not occur in a-lopt.
Trees
;; A ToN (tree of numbers) is one of:
;; - Symbol
;; - (cons Number (cons ToN ToN))
;; Example Trees:
(define tree-1 'a) ;; a leaf
(define tree-2 'b) ;; another leaf
(define tree-3 (cons 3 (cons 'a 'b))) ;; an interior node with two leaves
(define tree-4 (cons 15 (cons (cons 3 (cons 'a 'b)) 'c)))
(define tree-5 (cons 20 (cons (cons 13 (cons 'x
'y))
(cons 7 (cons 'm
'n)))))
Trees are an interesting class of data.
Lets consider writing a function to process them.
;; ton-weight : ToN -> Number
;; sums all the interior nodes of a-ton
(define ton-weight
(lambda (a-ton)
(cond
((null? a-ton) ...)
(else ... (car a-ton) ...
... (ton-weight (cdr a-ton)) ...))))
What's wrong with this template?
What should the template look like?
As a class, lets implement ton-weight
(don't bother trying to adapt the code above).
Ex 13. Implement ton-contains?
;; ton-contains? : ToN * Symbol -> Boolean
;; produces #t if a-ton contains s as a leaf somewhere.
;; otherwise produces #f.
(define ton-contains?
(lambda (a-ton s)
...))
define-datatype
formIt is really painful to try to make complex
structures when all you have is car
and cdr
.
After all, (car (cdr (car (car ...))))
is a bit ugly.
Scheme programmers work around this in various ways.
We will use a new form, the define-datatype
form,
as a tool for making (non-list) structured data.
(define-datatype type-name predicate-name
(variant1-name (field-name field-predicate)
...)
(variant2-name (field-name field-predicate)
...)
...)
Lets look at ToN
again.
(define-datatype dton-type dton?
(make-leaf (sym symbol?))
(make-node (num number?)
(left dton?)
(right dton?))
)
;; A DTon is one of:
;; - (make-leaf Symbol)
;; - (make-node Number DTon DTon)
;; Below are some examples of DTons.
(define dtree-1 (make-leaf 'a))
(define dtree-2 (make-leaf 'b))
(define dtree-3 (make-node 3 (make-leaf 'a) (make-leaf 'b)))
(define dtree-4 (make-node 15
(make-node 3
(make-leaf 'a)
(make-leaf 'b))
(make-leaf 'c)))
(define dtree-5 (make-node 20
(make-node 13 (make-leaf 'x) (make-leaf 'y))
(make-node 7 (make-leaf 'm) (make-leaf 'n))))
The definitions for dtree-#
above
are examples of how to use the constructors
that the define-datatype
form introduces.
Use the interactions window in DrScheme to see what dtree-5
looks like. What happens if you try to do (make-leaf dtree-4)
?
What about pulling out the sub-components of one of these structures?
(That is, what do we have that is analogous to car
and
cdr
, but for these new structures rather than pairs?)
To select the subcomponents of a structure, we use the cases
special form.
(cases type-name expression-producing-element-of-type
(variant1-name (names-for-variant1-fields ...) result1-expression)
(variant2-name (names-for-variant2-fields ...) result2-expression)
...
)
So at the interactions window, we can directly investigate
how to use the cases
special form with the
trees we made above.
What do the following expressions do?
> (cases dton-type dtree-1 (make-leaf (x) 'leaf) (make-node (n l r) 'node))
...
> (cases dton-type dtree-2 (make-leaf (x) 'leaf) (make-node (n l r) 'node))
..
> (cases dton-type dtree-3 (make-leaf (x) 'leaf) (make-node (n l r) 'node))
...
> (cases dton-type dtree-4 (make-leaf (x) 'leaf) (make-node (n l r) 'node))
...
> (cases dton-type dtree-5 (make-leaf (x) 'leaf) (make-node (n l r) 'node))
...
And how about these?
> (cases dton-type dtree-1 (make-leaf (x) x) (make-node (n l r) n))
...
> (cases dton-type dtree-2 (make-leaf (x) x) (make-node (n l r) n))
..
> (cases dton-type dtree-3 (make-leaf (x) x) (make-node (n l r) n))
...
> (cases dton-type dtree-4 (make-leaf (x) x) (make-node (n l r) n))
...
> (cases dton-type dtree-5 (make-leaf (x) x) (make-node (n l r) n))
...
What goes wrong with these expressions?
Can you explain what is inherently wrong with these expressions, in terms
of the data definition for DTon
?
> (cases dton-type dtree-1 (make-leaf (x) x) (make-node (n l r) (symbol->string n)))
...
> (cases dton-type dtree-2 (make-leaf (x) x) (make-node (n l r) (symbol->string n)))
..
> (cases dton-type dtree-3 (make-leaf (x) x) (make-node (n l r) (symbol->string n)))
...
> (cases dton-type dtree-4 (make-leaf (x) x) (make-node (n l r) (symbol->string n)))
...
> (cases dton-type dtree-5 (make-leaf (x) x) (make-node (n l r) (symbol->string n)))
...
Here is the template for processing one of these DTon
's.
Note that you use the same process to develop this template
that you use to develop any other: by asking
cond
, because the cases
form
is designed to work with the define-datatype
form.
;; f : DTon -> ???
(define f
(lambda (dtree)
(cases dton-type dtree
(make-leaf (x) ... x ...)
(make-node (n l r) ... n ... (f l) ... (f r) ...))))
Ex 14. Implement a function analogous to ton-weight
that operate on
DTon
s instead of ToN
s.
Ex 15. Implement a function analogous to ton-contains?
that operate on
DTon
s instead of ToN
s.