Functional Problem Solving (CSC 151 2015F) : Assignments
Primary: [Front Door] [Schedule] - [Academic Honesty] [Disabilities] [Email] - [FAQ] [Teaching & Learning] [Grading] [Taking Notes] [Rubric] [Remote Access]
Current: [Assignment] [EBoard] [Lab] [Outline] [Reading]
Sections: [Assignments] [EBoards] [Labs] [Outlines] [Readings] - [Examples] [Handouts]
Reference: [Setup] [VM] [Errors] - [Functions A-Z] [Functions By Topic] - [Racket] [Scheme Report (R5RS)] [R6RS] [TSPL4]
Related Courses: [Curtsinger (2015F)] [Davis (2013F)] [Rebelsky (2015S)] [Weinman (2014F)]
Misc: [Submit Questions] - [SamR] [Glimmer Labs] [CS@Grinnell] [Grinnell] - [Issue Tracker (Course)]
Assigned: Tuesday, 24 November 2015
Due: The due dates for various tasks are as follows.
Important! This examination has an initial code file that you should copy and use as the starting point for your work.
Please read the updated instructions for this exam.
Topics: Trees, higher-order procedures
We have used map to apply a procedure to every element in a
list, but what if we want to apply a procedure to every element in a
different data structure? Unfortunately, map will not help
us in this case.
Document and write a procedure, (,
that takes a tree tree-map
proc param-tree)param-tree as input and produces a
new tree result-tree with the same structure. Each
element in result-tree should be the result of
calling the procedure proc on the corresponding value
in param-tree. You do not need to
support multiple trees the same way map supports multiple
lists for procedures that take more than one parameter.
Here are a few examples.
> (tree-map increment empty) 'empty
> (tree-map increment (leaf 11)) '#(node 12 empty empty)
> (tree-map increment (node 7 (leaf 11) empty)) '#(node 8 (node 12 empty empty) empty)
> (tree-map (l-s + 1) (node 3 (node 2 (leaf 1) empty) (node 5 (leaf 4) (leaf 6)))) '#(node 4 #(node 3 #(node 2 empty empty) empty) #(node 6 #(node 5 empty empty) #(node 7 empty empty)))
> (tree-map (l-s = 5)
(node 3
empty
(node 4 empty (node 5 empty (node 6 empty (leaf 7))))))
'#(node
#f
empty
#(node
#f
empty
#(node #t empty #(node #f empty #(node #f empty empty)))))
> (tree-map (lambda (s) (if (equal? s "five") "five" "not five"))
(node "three"
empty
(node "four"
empty
(node "five"
empty
(node "six" empty (leaf "seven"))))))
'#(node
"not five"
empty
#(node
"not five"
empty
#(node
"five"
empty
#(node "not five" empty #(node "not five" empty empty)))))
Topics: Files, file recursion, input, output, strings, numeric recursion, repetition.
In our initial explorations with files, we found that some of the procedures we'd like to have are not available. In this problem, you will write some of those procedures.
a. Write, but do not document, a procedure,
(, that reads the contents
of the given file and returns it as a string.
read-file
file-name)
> (define target (open-output-file "/home/student/Desktop/example.txt" #:exists 'replace)) > (display "Sample text" target) > (newline target) > (for-each (lambda (val) (display val target) (newline target)) (iota 5)) > (close-output-port target) > (read-file "/home/student/Desktop/example.txt") "Sample text\n0\n1\n2\n3\n4\n"
Hint: You can use repeated calls to
read-line along with string-append.
b. Write, but do not document, a procedure,
(, that reads the contents of the given
file and displays it in the interactions pane.
display-file
file-name)
> (display-file "/home/student/Desktop/example.txt") Sample text 0 1 2 3 4
Hint: You should use your solution to the previous subproblem.
c. In our exploration with files, you've found that we cannot write
to an existing file by default. As you may have noted, many Web
browsers handle this situation by creating an alternate version of the
file name. For example, for alpha.txt, it will create
alpha-1.txt, alpha-2.txt, and so on and
so forth.
Write, but do not document, a procedure,
(,
that finds the next available version of a file. For example,
next-version filename)
> (file-exists? "/home/student/Desktop/alpha.txt")
#f
> ; Create an empty file and return its name
(define make-empty-file
(lambda (filename)
(let ([target (open-output-file filename #:exists 'replace)])
(close-output-port target))
filename))
> (make-empty-file "/home/student/Desktop/alpha.txt")
"/home/student/Desktop/alpha.txt"
> (file-exists? "/home/student/Desktop/alpha.txt")
#t
> (delete-file "/home/student/Desktop/alpha.txt")
> (file-exists? "/home/student/Desktop/alpha.txt")
#f
> (next-version "/home/student/Desktop/alpha.txt") "/home/student/Desktop/alpha.txt" > (make-empty-file "/home/student/Desktop/alpha.txt") "/home/student/Desktop/alpha.txt" > (next-version "/home/student/Desktop/alpha.txt") "/home/student/Desktop/alpha-1.txt" > (file-exists? "/home/student/Desktop/alpha-1.txt") #f > (next-version "/home/student/Desktop/alpha.txt") "/home/student/Desktop/alpha-1.txt" > (make-empty-file (next-version "/home/student/Desktop/alpha.txt")) "/home/student/Desktop/alpha-1.txt" > (file-exists? "/home/student/Desktop/alpha-1.txt") #t > (make-empty-file (next-version "/home/student/Desktop/alpha.txt")) "/home/student/Desktop/alpha-2.txt" > (make-empty-file (next-version "/home/student/Desktop/alpha.txt")) "/home/student/Desktop/alpha-3.txt"
> (file-exists? "/home/student/Desktop/alpha-1.txt") #t > (make-empty-file (next-version "/home/student/Desktop/alpha-1.txt")) "/home/student/Desktop/alpha-1-1.txt"
> (file-exists? "/home/student/Desktop/beta") #f > (make-empty-file (next-version "/home/student/Desktop/beta")) "/home/student/Desktop/beta" > (make-empty-file (next-version "/home/student/Desktop/beta")) "/home/student/Desktop/beta-1" > (make-empty-file (next-version "/home/student/Desktop/beta")) "/home/student/Desktop/beta-2"
> (file-exists? "/home/student/Desktop/CSC151.2015F/gamma.txt") #f > (make-empty-file (next-version "/home/student/Desktop/CSC151.2015F/gamma.txt")) "/home/student/Desktop/CSC151.2015F/gamma.txt" > (make-empty-file (next-version "/home/student/Desktop/CSC151.2015F/gamma.txt")) "/home/student/Desktop/CSC151.2015F/gamma-1.txt" > (make-empty-file (next-version "/home/student/Desktop/CSC151.2015F/gamma.txt")) "/home/student/Desktop/CSC151.2015F/gamma-2.txt"
> (make-empty-file (next-version "/home/student/Desktop/CSC151.2015F/alpha.txt")) "/home/student/Desktop/CSC151.2015F/alpha.txt" > (make-empty-file (next-version "/home/student/Desktop/CSC151.2015F/alpha.txt")) "/home/student/Desktop/CSC151.2015F/alpha-1.txt"
Hints: Think about how to split the file name into two parts. Use numeric recursion to try potential alternate names. There are some useful procedures in the code file.
d. Write, but do not document, a procedure,
(, that creates the next version
of create-file lines
filename)filename and writes each value
in lines to that file, one per line. Your
procedure should return the name of the created file.
> (create-file (list "; File:" "; 00000.rkt"
"; Contents: " "; Solutions to exam 4")
"/home/student/Desktop/00000.rkt")
"/home/student/Desktop/00000.rkt"
> (create-file (list "; File:" "; 00000.rkt"
"; Contents: " "; More solutions to exam 4")
"/home/student/Desktop/00000.rkt")
"/home/student/Desktop/00000-1.rkt"
> (display-file "/home/student/Desktop/00000.rkt")
; File:
; 00000.rkt
; Contents:
; Solutions to exam 4
> (display-file "/home/student/Desktop/00000-1.rkt")
; File:
; 00000.rkt
; Contents:
; More solutions to exam 4
> (create-file (list ";I'm sick of this exam")
"/home/student/Desktop/00000.rkt")
"/home/student/Desktop/00000-2.rkt"
> (display-file "/home/student/Desktop/00000-2.rkt")
;I'm sick of this exam
Hint: Think about how you can deal with each
line in lines.
Note: You can find the documentation for these procedures in the exam code.
Note: You may find some of the following standard
Racket procedures useful: (,
file-exists?
path)(, and
read-line
input-port)(.
number->string
number)
Topics: Pairs, pair structures, deep recursion
When we first started exploring pairs and pair structures, we found it helpful to sketch the underlying representation of these pairs and pair structures. In sketching those drawings, we followed a fairly straightforward approach.
While it was useful to do that work by hand to start getting a better understanding of thse structures, we should now be at the point that we'd rather have the computer follow this algorithm than follow it by hand.
Write, but do not document, a procedure,
(.
Your procedure should produce images like the following for the
given structures.
render-pair-structure!
image structure
left top)
![]() |
(list 'a 'b) |
![]() |
(cons 'a 'b) |
![]() |
(cons null null) |
![]() |
(list (list 'a 'b) 'c (cons 'd 'e)) |
![]() |
(list (list (list 'a 'b (list 'c)) 'd 'e 'f) 'g) |
You can use the naive approach to this problem. In particular, you do not need to worry about whether or not the elements in a list overlap (as they might if each element is itself a list).
To help you in this endeavor, the exam contains three useful procedures,
,
render-pair!, and
render-value!. In writing
simple-arrow!, you may
assume that each pair is 40 units wide and 20 units high.
render-pair-structure!
Topics: Vectors, documentation
Document and write a procedure (
that takes two vectors as input and produces a new vector that
contains the values of both. In other words, vector-join
vec1 vec2)vector-join
should join two vectors together. You may not
use vector->list or list->vector in your
implementation of vector-join.
Here are a few examples.
> (vector-join (vector 1 2 3) (vector 4 5 6))
'#(1 2 3 4 5 6)
> (vector-join (vector "string" 'symbol 1 #t) (vector "another string" 'symbol2 2 #f))
'#("string" symbol 1 #t "another string" symbol2 2 #f)
> (vector-join (vector) (vector 1 2 3))
'#(1 2 3)
Topics: Higher-order procedures, numeric recursion
As you may have noted, sometimes we “nest” repeated calls
to the same procedure. For example, to compute x to the
eighth power, we might write (square (square (square x))).
Write, but do not document, a procedure, (, that
returns a new procedure that takes one input and applies
nest
n fun)fun the specified number of times.
> (define octo (nest 3 square)) > octo #<procedure> > (octo 2) 256 > (octo 3) 6561
> (define no-square (nest 0 square)) > (no-square 5) 5
> (define much-redder (nest 6 irgb-redder)) > (irgb->string (much-redder (irgb 0 0 0))) "192/0/0" > (irgb->string (much-redder (irgb 10 20 40))) "202/20/40"
> (map (nest 7 increment) (iota 5)) '(7 8 9 10 11)
Topics: Vectors, binary search
Write, but do not document, a procedure
( that uses binary search to find the
smallest value in the sorted vector next-largest val
vec)vec that is larger than
val. Documentation for this procedure is included below:
;;; Procedure: ;;; next-largest ;;; Parameters: ;;; val, a real number ;;; vec, a vector of real numbers ;;; Purpose: ;;; Find the smallest value in vec that is greater than val ;;; Produces: ;;; result, a real number or #f. ;;; Preconditions: ;;; vec is a vector of real numbers sorted from smallest to largest ;;; Postconditions: ;;; If vec contains a number greater than val, then result is the ;;; smallest such number. ;;; If vec does not contain a number greater than val, then result is #f.
Here are a few examples.
> (next-largest 4 '#(2 4 6 8 10 12)) 6
> (next-largest 100 '#(1 3 10 29)) #f
> (next-largest 10 '#(7 8 9 10)) #f
> (next-largest 0 '#(100 200 300 400)) 100
> (next-largest 225 '#(100 200 300 400)) 300
Topics: Map, lists, algorithm analysis
When we first learned how to use map, we often used the
technique of “take a single expression, insert map
before each operation, and turn each value into a list, either with
make-list or by using something like iota”.
For example, suppose we make one circle as follows.
(define circle-37
(vshift-drawing (* 10 (modulo 37 10))
(hshift-drawing (* 5 37)
(scale-drawing (increment (modulo 37 7))
drawing-unit-circle))))
In the extreme, we may end up writing something like the following.
(define many-circles
(lambda (n)
(map vshift-drawing
(map *
(make-list n 10)
(map modulo
(iota n)
(make-list n 10)))
(map hshift-drawing
(map *
(make-list n 5)
(iota n))
(map scale-drawing
(map increment
(map modulo
(iota n)
(make-list n 7)))
(make-list n drawing-unit-circle))))))
a. Using the techniques from the reading on analyzing
procedures and the
corresponding lab, update many-circles to count
calls to cons, car, cdr,
and null?. You can find implementations of all of the
main procedures (e.g., map, make-list,
and iota) in the exam code file.
b. Find out how many calls there are to cons,
car, cdr, and null?. if we
are making lists of 5, 10, 20, and 40 circles.
c. Come up with an approximate formula for the number of calls based on n.
d. Rewrite the code so that there are no more than 2*n calls to
cons in making a list of n circles. Any helpers you
write should be local.
Note: You may want to preview the list of
circles with something like
(drawing->image (drawing-compose (many-circles 50)) 200 100).
This problem is optional. If you complete it correctly, you will earn a few points of extra credit.
Topics: Higher-order procedures, code reading, documentation
Write the 6P documentation for the mystery procedure defined below. Be sure to show at least four example uses of this procedure.
(define mystery (l-s l-s l-s))
Warning! This problem may make your brain hurt.
Here we will post answers to questions of general interest. Please check here before emailing your questions!
STUB comment that appears in the code
file?
Here you will find errors of spelling, grammar, and design that students have noted. Remember, each error found corresponds to a point of extra credit for everyone. We usually limit such extra credit to five points. However, if we make an astoundingly large number of errors, then we will provide more extra credit. (And no, we don't count errors in the errata section or the question and answer sections.)
vector-join. [PM, 1 point]
exam4.rkt rather than
exam4-sr.rkt or exam4-cc.rkt. (Since
you are renaming the file, it doesn't really matter.) [PM, 0 points]
tree-map procedure, some of the output appears
incorrect. [GN, 1 point, SR section only]
make-list of ...” should be
“make-list or ...”. [PM, 0 points]
silly, so it gets mad when you attempt to redefine
it in the definitions pane. (In the example, we've only redefined
it in the interactions pane, so this isn't strictly a bug. We have,
nonetheless, renamed the procedure in the exam.) [EN, 0 points]
map. The exam provides
only one. [GN, 1 point]
vector-join. [YZ, 1 point]
make-list. [JO, 1 point, SR section only]
Some of the problems on this exam are based on (and at times copied from) problems on previous exams for the course. Those exams were written by Janet Davis, Rhys Price Jones, Samuel A. Rebelsky, John David Stone, Henry Walker, and Jerod Weinman. Many were written collaboratively, or were themselves based upon prior examinations, so precise credit is difficult, if not impossible.
Some problems on this exam were inspired by conversations with our students and by correct and incorrect student solutions on a variety of problems. We thank our students for that inspiration. Usually, a combination of questions or discussions inspired a problem, so it is difficult and inappropriate to credit individual students.