![]() |
![]() |
|
Due
Reading: Scott §§ 3.3.0–3.3.3 (scope), § 6.6 (tail-recursion).
Your name and the assignment-number must be in a comment at the start of the file, and your hardcopy must be stapled. All functions/data must include the appropriate steps1 of the-design-recipe.html. In particular, test cases alone might be worth half the credit for a function. Unless otherwise indicated, two test cases will suffice for most problems, if they are testing different situations.
For this (and future) assignments, bump up your DrRacket language-level to Intermediate Student with lambda. Do not call any of the following functions:
Use template: Your solution should follow the template for processing lists. Do not make it tail-recursive.
Remember: We can't re-assign to variables, in functional programming. So we won't have a counter which we increment, like you might in your imperative-programming class. Instead, be sure to follow the design recipe, and after copying the template, think about the inventory-with-values (assuming we call our parameters “threshold” and “nums”): if we call count-bigs with the particular inputs (count-bigs 3 (cons 10 (cons 2 (cons 5 empty)))), what is…Fill in each blank with a particular number, or list-of-numbers. Then, ask yourself: Starting with those pieces of info, how do I assemble them to get my desired result of 2?
- threshold =
- nums =
- (first nums) =
- (rest nums) =
- (count-bigs threshold (rest nums)) =
You don't need to include the above in your answer — it's just to remind you of what you do, for the “inventory-with-values” step of the design-recipe, #6. If you get stuck on any of the problems below, make sure you didn't skip this step; it's the one that can really make things click!
Use template: Your solution should follow the template for processing lists. Do not make it tail-recursive.To help you out, here are some test cases; no further ones are required.
(check-expect (map-sqr empty) empty) (check-expect (map-sqr (cons 7 empty)) (cons 49 empty)) (check-expect (map-sqr (cons 9 (cons 7 empty))) (cons 81 (cons 49 empty))) |
Who doesn't love binge-watching TV shows?2 After extensive research to determine what aspects of a TV series are important for binge-watching information, I have personally determined that most shows have plot threads which may span several episodes. It's not even important what the plot threads are; a true connoisseur cares only about how many plot threads there are.
A binge-task is one of:
Be nice., or
A fool might show great wisdom, like Phoebe. (Or might not, like Joey.)); and the remaining-show — a binge-task.
List-like, but not lists: Although binge-tasks are not lists, they are certainly reminiscent of them: For example, finales and shark-jumps (like empty-lists) cannot possibly followed by any additional tasks. However, cliff-hangers and morals (like cons structs) do have information about an episode, “followed” by one more binge-task (which may be another cliff-hanger which itself contains yet another binge-task, etc.).Compare this to how we represent a list of a thousand numbers really being a cons struct with only two fields: the first number, and the remainder of the list (which is another cons which itself contains yet another cons, etc.).
So: Do not use built-in lists; everything you need comes straight from the definition above.
Hint 1: As always, if you introduce compound data you need to provide both the names and types of the fields (use a define-struct/c) , as we did in anc-tree.rkt.and the type of the field (in a sample make-structname).
Hint 2: In cases where you don't have more than one piece of information to track, you don't need to introduce any compound-data.
warning: Make sure that you can distinguish between a shark-jump, and a finale that happens to resolve two threads (they are different). Also, do not try to represent all the above using a single struct -- one which contains a number-of-threads-introduced and a number-resolved and some sort of tag, plus further fields that are only meaningful depending on the tag. That would be sloppy data-modeling; a shark-jump struct/object shouldn't contain fields that are meaningless, nor fields whose value is a function of other fields3.
(define finale0 (define shark0 (define moral1 (define cliffhanger1 (define |
(check-expect (count-morals finale0) ) (check-expect (count-morals shark0) ) (check-expect (count-morals moral1) ) (check-expect (count-morals cliffhanger1) ) (check-expect (count-morals ) ) |
Note: If somebody starts a binge-taskhalfway through, this might be a negative number, if they see a thread resolved that they never saw originally introduced4 .
(check-expect (net-unresolved finale0) ) (check-expect (net-unresolved shark0) ) (check-expect (net-unresolved moral1) ) (check-expect (net-unresolved cliffhanger1) ) (check-expect (net-unresolved ) ) |
Recall: the scope of identifiers introduced with let is just the body of the let, while the scope of identifiers introduced with let* is the body of the let and all following right-hand-sides of the let*.
Recall: a variable-use's binding-occurrence is the place where that variable is defined.
Hint: The binding-occurrence itself is not considered a use of the variable. There are a total of seven uses of a, amongst the four parts.
line 01 (define a 5) line 02 (define b 10) |
line A1 (let {[z 50] line A2 [a 51] line A3 } line A4 (+ a b z)) ; evaluates to: |
line B1 (let {[z 50] line B2 [a 51] line B3 [b (* a 3)] line B4 } line B5 (+ a b z)) ; evaluates to: |
line C1 (define (foo a) line C2 (let {[z 50] line C3 [a 51] line C4 [b (* a 3)] line C5 } line C6 (+ a b z))) line C7 (foo 1001) ; evaluates to: |
line D1 (let* {[z 50] line D2 [a 51] line D3 [b (* a 3)] line D4 } line D5 (+ a b z)) ; evaluates to: |
Note: Note that being tail-recursive is a property of a function’s source-code. The fact that a tail-recursive function can be optimized to not unnecessarily-allocate stack space is a compiler-implementation issue — albeit it’s what makes the concept of tail-recursion important.
Reading: Scott also discusses recursion and tail-recursion, in §6.6.1; (both 3rd and 4th eds).
#| /** Return the smallest number in a list. * @pre <code>!nums.isEmpty()</code> * @return the smallest number in `nums`. */ static Double myMin( List<Double> nums ) { // initialize our loop-variables: double minSoFar = nums.get( ); List<Double> numsRemaining = nums.subList( ,nums.size()); while ( ) { double a = numsRemaining.get(0); // corresponding to Scott’s variable `a` minSoFar = ( ? : ); numsRemaining = ; } return minSoFar; } |# |
my-mininstead of
myMin, etc.).
: The goal of this problem is to have your racket code correspond nearly word-for-word to the above Java loop, computing the same result in exactly the same way. So each expression/step in one version should correspond directly to an expression/step in the other, with a racket helper-function corresponding to the java while, just as in the lecture examples. E.g. since the Java version has a variable a which is local to the loop, your racket code should do the same.
Btw: The book’s starting-code calls (empty? (rest l)) — something not in the template. It’s a bit of a hack to stray from the template: Scott wants to avoid making a helper-function, but still return a sentinel-value answer for empty-lists.
However, when converting to tail-recursion, this difference ends up being moot: as you make a helper/wrapper function for the tail-recursion, that extra check disappears.
scheme vs racket: The book’s scheme code uses: car, cdr, null?, and #t. In racket, these names are (respectively): first, rest, empty?, and #true.
(check-expect (natnum->string/binary 0) "") ; Note that we remove (all) leading zeroes (!) (check-expect (natnum->string/binary 1) "1") (check-expect (natnum->string/binary 2) "10") (check-expect (natnum->string/binary 3) "11") (check-expect (natnum->string/binary 4) "100") (check-expect (natnum->string/binary 5) "101") (check-expect (natnum->string/binary 15) "1111") (check-expect (natnum->string/binary 16) "10000") (check-expect (natnum->string/binary (+ 1024 8 4)) "10000001100") (check-expect (natnum->string/binary #xA) "1010") ; hex literal (check-expect (natnum->string/binary #xFFFF) "1111111111111111") (check-expect (natnum->string/binary #xfeedBee) "1111111011101101101111101110") ; (define (natnum->string/binary n) (-> natural? string?) ; Return the binary-numeral representing n (without any leading zeroes). ; Note that the numeral for zero, without leading zeros, is the empty-string! ; (cond [(zero? n) ""] [(positive? n) (string-append (natnum->string/binary (quotient n 2)) (if (even? n) "0" "1"))])) |
Btw: This code doesn’t quite follow the design-recipe for natural-numbers, because it recurs on (quotient n 2) rather than (sub1 n). But it still works fine because it “reduces” the natnum to a smaller one. To reason about this code, you wouldn’t use straight-up mathematical induction; instead you'd call it “strong induction”.
This page licensed CC-BY 4.0 Ian Barland Page last generated | Please mail any suggestions (incl. typos, broken links) to ibarland ![]() |