0% found this document useful (0 votes)
39 views

Assignment: 5: Due: Language Level: Files To Submit: Practice Exercises

This document provides instructions for Assignment 5 for CS 135 Fall 2020. It is due on October 27th at noon and covers material up to the end of Module 10. Students are to submit 3 Racket files (not-lists.rkt, groceries.rkt, sillystring.rkt) implementing functions based on provided data definitions and examples. The assignment has 3 questions - the first involves defining functions to operate on a non-list data structure, the second defines a grocery store database with queries, and the third involves string manipulation.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
39 views

Assignment: 5: Due: Language Level: Files To Submit: Practice Exercises

This document provides instructions for Assignment 5 for CS 135 Fall 2020. It is due on October 27th at noon and covers material up to the end of Module 10. Students are to submit 3 Racket files (not-lists.rkt, groceries.rkt, sillystring.rkt) implementing functions based on provided data definitions and examples. The assignment has 3 questions - the first involves defining functions to operate on a non-list data structure, the second defines a grocery store database with queries, and the third involves string manipulation.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

CS 135 Fall 2020

Becker, Clarke, Hackman, Holtby, Lank, Morland, Nijjar, Watson

Assignment: 5
Due: Tuesday, October 27nd at noon (Waterloo time)
Language level: Beginning Student with List Abbreviations
Files to submit: not-lists.rkt, groceries.rkt, sillystring.rkt
Practice exercises: HtDP 6.4.2, 6.5.2, 7.3.1, 17.1.2, 17.2.2, 17.6.2, and 17.8.4
• Make sure you read the OFFICIAL A05 post on Piazza for the answers to frequently
asked questions.
• Unless stated otherwise, all policies from Assignment 04 carry forward.
• This assignment covers material up to the end of Module 10.
• The only built-in functions and special forms you may use are listed below. If a built-in
function or special form is not in the following list, you may not use it:

define-struct error round


* + - ... / < <= = > >= abs add1 and append boolean? ceiling char=? char?
check-error check-expect check-within cond cons cons? define eighth
else empty? equal? even? exp expt fifth first floor fourth integer?
length list list->string list? max member? min negative? not
number->string number? odd? or positive? quotient remainder rest
second seventh sixth sqr sqrt string->list string-append string-length
string-upcase string<=? string<? string=? string>=? string>? string?
sub1 substring symbol=? symbol? third zero?

• Remember that basic tests are meant as sanity checks only; by design, passing them should
not be taken as any indication that your code is correct, only that it has the right form.
• Unless the question specifically says otherwise, you are always permitted to write helper
functions to perform any task. You may use any constants or functions from any part of a
question in any other part.

1. Lists? We don’t need no stinking lists! Recall that we defined a (listof X) as follows:
;; A (listof X) is one of:
;; * empty
;; * (cons X (listof X))

We then used the built-in functions first and rest to extract parts of the list, and the built-in
function cons to make a list one longer.
Here we will create a structure that is able to store arbitrarily long data, just like a list.
Place your solution in the file not-lists.rkt
For this question, use the following data definition:

CS 135 — Fall 2020 Assignment 5 1


(define-struct ls (first rest))
;; A (lsof X) is one of:
;; * 'nothing
;; * (make-ls X (lsof X))

(a) [5% Correctness]


Write a function ls-length that consumes a (lsof Any) and produces the number
of values in it.
Exercise

For example,
(check-expect (ls-length
(make-ls "!" (make-ls 'huh (make-ls 42 'nothing)))) 3)

(b) [5% Correctness]


Write a function ls-max that consumes a (lsof Num) that contains at least one
Exercise

value, and produces the largest value.


For example,
(check-expect (ls-max (make-ls 5 (make-ls 9 (make-ls 7 'nothing)))) 9)

2. To keep track of items sold at the local family-run store, we need to be able to describe each
item. Each item has a department, name, price per package (in dollars), and mass per package
(in grams).
Place your solution to this question in the file groceries.rkt.
We use the following data definition:
(define-struct grocery (dept name cost mass))
;; A Grocery is a (make-grocery Str Str Num Num)
;; Requires: cost >= 0, mass > 0.

;; A Store is a (listof Grocery)


;; Requires: no two items have both the same dept and same name.

Here are two Stores, examples of a database of products we might deal with:
(define try-n-save
(list (make-grocery "produce" "apple" 2.49 600)
(make-grocery "seed" "rice" 0.95 1000)
(make-grocery "dairy" "milk" 3.99 4000)
(make-grocery "seed" "pinto" 2.49 500)
(make-grocery "produce" "potato" 2.99 5000)
(make-grocery "chips" "potato" 1.99 250)
(make-grocery "chips" "corn" 1.99 275)
(make-grocery "seed" "wheat" 0.49 500)
(make-grocery "produce" "banana" 0.69 450)
(make-grocery "dairy" "cheese" 6.49 900)
(make-grocery "chips" "banana" 1.99 50)
(make-grocery "produce" "peach" 3.99 400)
(make-grocery "seed" "lentil" 2.99 800)
(make-grocery "produce" "corn" 0.99 100)

CS 135 — Fall 2020 Assignment 5 2


(make-grocery "seed" "corn" 4.99 850)
(make-grocery "dairy" "kefir" 5.99 1000)))

(define kwik-e-mart
(list (make-grocery "seed" "rice" 0.38 400)
(make-grocery "can" "corn" 4.00 400)
(make-grocery "seed" "pinto" 2.49 500)
(make-grocery "produce" "apple" 2.99 400)
(make-grocery "can" "creamed eels" 2.19 350)
(make-grocery "produce" "pineapple" 3.17 250)))

We want to be able to query databases like these to find products with certain features.

(a) [1% Correctness] Example.


Define your own constant called student-shop. This should be a Store with at
Exercise

least ten items coming from at least three departments. At least two departments
should have at least two items in them.
For testing of functions in the rest of this question, you may use any of try-n-save,
kwik-e-mart, student-shop, and other values you create.

(b) [5% Correctness] Intervals. We want to be able to tell if a number is within an interval,
that is, at least some lower bound, and at most another. We define an interval as follows:
(define-struct interval (lo hi))
;; An Interval is a (make-interval (anyof 'dontcare Num)
;; (anyof 'dontcare Num))

A number x is within an interval (make-interval a b) if a ≤ x ≤ b. For example, 6.5 is


in the interval (make-interval 4.5 10.2) since 4.5 ≤ 6.5 ≤ 10.2.
We use the symbol 'dontcare to say the interval is unbounded in that direction.
For example:
• (make-interval 4.5 'dontcare) describes x ≥ 4.5. This is sometimes written
[4.5, ∞).
• (make-interval 'dontcare 4.5) describes x ≤ 4.5. This is sometimes written
(−∞, 4.5].
• (make-interval 4.5 4.5) describes 4.5 ≤ x ≤ 4.5. The only such value is 4.5.
• (make-interval 5 4) describes 5 ≤ x ≤ 4. There are no such values.

Write a function in-interval? that consumes a Num and an Interval. It produces


true if the Num is in the interval, and false otherwise.
For example,
Exercise

(check-expect (in-interval? 42
(make-interval 'dontcare 'dontcare)) true)
(check-expect (in-interval? 34
(make-interval 35 'dontcare)) false)
(check-expect (in-interval? 34
(make-interval 'dontcare 35)) true)

CS 135 — Fall 2020 Assignment 5 3


(c) [9% Correctness] Queries. We also want to be able to tell if a string is equal to another
string, but also sometimes to ignore string values. So we define:
;; A StrPatt is a (anyof Str 'dontcare)

We say that a Str and a StrPatt “match” if the StrPatt is 'dontcare, or if the Str and
the StrPatt are identical.
Now we are ready to describe our queries.
(define-struct query (dept name cost mass))
;; A GroceryQuery is a
;; (make-query StrPatt StrPatt Interval Interval)

A Grocery satisfies a GroceryQuery if all the string fields match, and each number field
is in the corresponding interval.
Write a function find-matches that consumes a (listof Grocery) and a
Exercise

GroceryQuery. It produces a list containing the items from the list that satisfy the
GroceryQuery. Leave the items in the same order they appear in the consumed list.
See the following example queries.

This query shows everything from the "seed" department:


(check-expect
(find-matches try-n-save (make-query "seed" 'dontcare
(make-interval 'dontcare 'dontcare)
(make-interval 'dontcare 'dontcare)))
(list
(make-grocery "seed" "rice" 0.95 1000)
(make-grocery "seed" "pinto" 2.49 500)
(make-grocery "seed" "wheat" 0.49 500)
(make-grocery "seed" "lentil" 2.99 800)
(make-grocery "seed" "corn" 4.99 850)))

Whereas this shows everything from any department that is listed as "corn":
(check-expect
(find-matches try-n-save (make-query 'dontcare "corn"
(make-interval 'dontcare 'dontcare)
(make-interval 'dontcare 'dontcare)))
(list (make-grocery "chips" "corn" 1.99 275)
(make-grocery "produce" "corn" 0.99 100)
(make-grocery "seed" "corn" 4.99 850)))

And this shows everything from the "seed" department that costs no more that $3.00
and comes is a package of at least 600 g.
(check-expect
(find-matches try-n-save (make-query "seed" 'dontcare
(make-interval 'dontcare 3.00)
(make-interval 600 'dontcare)))
(list
(make-grocery "seed" "rice" 0.95 1000)
(make-grocery "seed" "lentil" 2.99 800)))

(d) [10% Correctness] Sorting. Now we want to organize the product available for sale.

CS 135 — Fall 2020 Assignment 5 4


Write a function sort-dept-name that consumes a Store, and produces the same

Exercise
values, sorted alphabetically by department, then alphabetically by name.
Use one or more of the predicates string=?, string<?, string<=?, string>?,
string>=? to determine if strings are in order. Their behaviour defines the desired
order for all Str.

For example,
(check-expect (sort-dept-name try-n-save)
(list
(make-grocery "chips" "banana" 1.99 50)
(make-grocery "chips" "corn" 1.99 275)
(make-grocery "chips" "potato" 1.99 250)
(make-grocery "dairy" "cheese" 6.49 900)
(make-grocery "dairy" "kefir" 5.99 1000)
(make-grocery "dairy" "milk" 3.99 4000)
(make-grocery "produce" "apple" 2.49 600)
(make-grocery "produce" "banana" 0.69 450)
(make-grocery "produce" "corn" 0.99 100)
(make-grocery "produce" "peach" 3.99 400)
(make-grocery "produce" "potato" 2.99 5000)
(make-grocery "seed" "corn" 4.99 850)
(make-grocery "seed" "lentil" 2.99 800)
(make-grocery "seed" "pinto" 2.49 500)
(make-grocery "seed" "rice" 0.95 1000)
(make-grocery "seed" "wheat" 0.49 500)))

(e) [15% Correctness] The Price is Right. If we have more than one Store, we want to
be able to find which items are available at both stores so we can comparison shop.
Write a function overlap. It consumes two Store, and produces a Store, containing
Exercise

only those items available in both Stores. Choose the item that is cheaper, per
gram. For items with equal cost per gram, we prefer the smaller package.
The result should be sorted by department and name, as above. Hint: sort the lists
before you compare them.

For example,
(check-expect
(overlap kwik-e-mart try-n-save)
(list
(make-grocery "produce" "apple" 2.49 600) ; Buy cheaper.
(make-grocery "seed" "pinto" 2.49 500) ; Same price and size.
(make-grocery "seed" "rice" 0.38 400))) ; Same price; buy smaller.

(f) [10% Correctness] Inflation. Now we want to be able to change the prices of certain
items in our store.

CS 135 — Fall 2020 Assignment 5 5


Write a function scale-prices. This consumes a Store, a GroceryQuery, and a
non-negative Num representing a ratio. All items in the Store that satisfy the

Exercise
GroceryQuery should have their price changed by the ratio, rounded to the nearest
0.01. The order of items should remain the same.
Use the built-in round function to do the rounding. You will need to do a little
arithmetic to make this work.

For example, here we raise the price of all "can"s by 10%:


(check-expect (scale-prices
kwik-e-mart
(make-query "can"
'dontcare
(make-interval 'dontcare 'dontcare)
(make-interval 'dontcare 'dontcare)) 1.10)
(list (make-grocery "seed" "rice" 0.38 400)
(make-grocery "can" "corn" 4.40 400)
;; corn goes from 4.00 to 4.40.
(make-grocery "seed" "pinto" 2.49 500)
(make-grocery "produce" "apple" 2.99 400)
(make-grocery "can" "creamed eels" 2.41 350)
;; eels goes from 2.19 to 2.409, rounded to 2.41.
(make-grocery "produce" "pineapple" 3.17 250)))

3. Silly String. We can store information in many different ways. With lists, we store a first
item, and the rest of the items. Here we will store the contents of a Str, in three parts:

(a) the first character


(b) the middle characters
(c) the last character.

For example, in "Babbage", the parts are #\B, "abbag", and #\e.
If we store the middle characters using the same kind of structure, we have a recursive data
structure that stores the contents of a Str.
Place your solution in the file sillystring.rkt.
We will use the following data definition:
(define-struct silly-string (first middle last))
;; A SillyStringStruct is a (make-silly-string Char SillyStr Char)

;; A SillyStr is one of:


;; * empty
;; * a Char
;; * a SillyStringStruct

CS 135 — Fall 2020 Assignment 5 6


(a) [7% Correctness]
Write a function (sillify s) that consumes a Str and produces the corresponding
SillyStr. For example,
(check-expect (sillify "Babbage")
(make-silly-string
#\B
(make-silly-string
#\a
(make-silly-string
#\b
#\b
#\a)
#\g)
Exercise

#\e))
(check-expect (sillify "Lovelace")
(make-silly-string
#\L
(make-silly-string
#\o
(make-silly-string
#\v
(make-silly-string
#\e
empty
#\l)
#\a)
#\c)
#\e))

(b) [7% Correctness]


Write a function (unsillify ss), which does the opposite of sillify. That is, it
consumes a SillyStr and produces the corresponding Str.
(check-expect (unsillify
(make-silly-string
#\L
(make-silly-string
#\o
Exercise

(make-silly-string
#\v
(make-silly-string
#\e
empty
#\l)
#\a)
#\c)
#\e))
"Lovelace")

(c) [6% Correctness] Palindromes. A palindrome is a sequence of characters that is


the same forwards as backwards: for example, "racecar" is a palindrome, while
"Koenigsegg" is not.
We will not consider lower- and upper-case letters to be the same. So we will not

CS 135 — Fall 2020 Assignment 5 7


consider "Hannah" to be a palindrome, even though "hannah" is.
Write a function (palindrome? ss).
It consumes a SillyStr, then produces true if ss a palindrome, and false
otherwise. For example,
(check-expect (palindrome?
(make-silly-string
#\r
Exercise

(make-silly-string #\a #\d #\a)


#\r)) true)
(check-expect (palindrome?
(make-silly-string
#\s
(make-silly-string #\o #\n #\a)
#\r)) false)
(check-expect (palindrome? (sillify "racecar")) true)
(check-expect (palindrome? (sillify "Koenigsegg")) false)

Do not convert to a Str. You must directly use the recursive structure of the
!
SillyStr.

CS 135 — Fall 2020 Assignment 5 8


Enhancements: Reminder—enhancements are for your interest and are not to be handed in.
There is a strong connection between recursion and induction. Mathematical induction is the proof
technique often used to prove the correctness of programs that use recursion; the structure of the
induction parallels the structure of the function. As an example, consider the following function,
which computes the sum of the first n natural numbers.

(define (sum-first n)
(cond
[(zero? n) 0]
[else (+ n (sum-first (sub1 n)))]))

To prove this program correct, we need to show that, for all natural numbers n, the result of
evaluating (sum-first n) is ∑ni=0 i. We prove this by induction on n.
Base case: n = 0. When n = 0, we can use the semantics of Racket to evaluate (sum-first 0) as
follows:

(sum-first 0) ; =>
(cond [(zero? 0) 0][else ...]) ; =>
(cond [true 0][else ...]) ; =>
0

Since 0 = ∑0i=0 i, we have proved the base case.


Inductive step: Given n > 0, we assume that the program is correct for the input n − 1, that is,
(sum-first (sub1 n)) evaluates to ∑n−1
i=0 i. The evaluation of (sum-first n) proceeds as follows:

(sum-first n) ; =>
(cond [(zero? n) 0][else ...]) ;(we know n > 0) =>
(cond [false 0][else ...]) ; =>
(cond [else (+ n (sum-first (sub1 n)))]) ; =>
(+ n (sum-first (sub1 n)))

Now we use the inductive hypothesis to assert that (sum-first (sub1 n)) evaluates to s = ∑n−1
i=0 i. Then
n−1 n
(+ n s) evaluates to n + ∑i=0 i, or ∑i=0 i, as required. This completes the proof by induction.
Use a similar proof to show that, for all natural numbers n, (sum-first n) evaluates to (n2 + n)/2.
Note: Summing the first n natural numbers in imperative languages such as C++ or Java would
be done using a for or while loop. But proving such a loop correct, even such a simple loop, is
considerably more complicated, because typically some variable is accumulating the sum, and its
value keeps changing. Thus the induction needs to be done over time, or number of statements
executed, or number of iterations of the loop, and it is messier because the semantic model in these

CS 135 — Fall 2020 Assignment 5 9


languages is so far-removed from the language itself. Special temporal logics have been developed
to deal with the problem of proving larger imperative programs correct.
The general problem of being confident, whether through a mathematical proof or some other formal
process, that the specification of a program matches its implementation is of great importance in
safety-critical software, where the consequences of a mismatch might be quite severe (for instance,
when it occurs with software to control an airplane, or a nuclear power plant).

CS 135 — Fall 2020 Assignment 5 10

You might also like