Homework 2 is due Monday, October 9, 2017 at 11:59pm.
Similarly to last time, you will submit your work with filename hw2.rkt in a directory called hw2. The following commands, when run from within a copy of your repository, will get you started. Note the fourth instruction, touch hw2.rkt, will create a (blank) file of that name. Also, the command cd .. goes "up" one directory from the current working directory.
$ svn updateAfter having done this, work in the file hw2/hw2.rkt
$ svn mkdir hw2
$ cd hw2
$ touch hw2.rkt
$ svn add hw2.rkt
$ cd ..
$ svn commit hw2 -m "getting ready for homework 2"
Unlike the last homework, we should be concerned with bogus inputs, such as a value outside of a meaningful range. The way to do this is to have a condition that checks for invalid inputs. If an invalid input is encountered, call the error function with a string that describes the problem as its sole parameter:
(error "contains-digit?: digit must be in the range 0-9")Note that the convention is for the error message to begin with the name of the function in which the error occurred.
When you raise an error in this manner, your program will print the error message and then immediately terminate. This is generally desirable, because there is often no "safe" value to return from a function that was given invalid input, and it would be inappropriate to use a meaningless result in further computations.
But, this means that you cannot test error cases using check-expect, because the program will quit in the middle of your tests. Instead, a special form, check-error allows you to test error handling. It works similarly to check-expect: you provide a function call that, in this case, you expect to raise an error. But, instead of providing the expected result value, you provide the error string that you expect, typed exactly the same way;
(check-error (widgets-per-grommet 17 12) "widgets-per-grommet: number of widgets must be even")the test will pass if and only if a matching error was generated. There is special machinery in the implementation of check-error that allows it to continue with the next test, rather than terminating your program prematurely.
Preliminaries
(Same as last week.)
At the top of your hw2.rkt file, write this line to designate Typed Racket as the current language:
#lang typed/racket
Then, add the following lines:
(require typed/test-engine/racket-tests) (require "../include/cs151-core.rkt")
At the bottom of the file, call the test function (with no arguments) to run the check tests:
(test)This function call — (test) — should remain as the last line of your program even as you are working on the code above it.
Homework Problems
For every function you write, you must preface the function definition with a type ascription (also known as a contract) and a purpose, and you must follow it with tests with check testers (check-expect, check-within, etc.). Do write helper functions where it seems like a good idea to do so. All functions need contracts, purposes and tests, even if they are "only" helper functions. (In actuality, a function is just a function, and the "helper function" designation is an illusion.) You may also call functions from other functions whenever you like.
Problem 1
Singular Wireless charges for monthly mobile broadband usage on a smartphone as follows: The first 300 MB of data costs $20. If a customer uses between 300 MB and 3 GB (3072 MB) of data, he or she pays $30 in total. If a customer exceeds 3 GB, he or she pays an overage of $15 per GB (1024 MB) above and beyond the 3 GB level. When a customer incurs an overage, he or she is given buckets of extra data, 1 GB at a time, at the $15 rate; overages are not pro-rated by smaller increments.
Write the function mobile-broadband-cost, which consumes the (integer) number of megabytes a customer has used in a month, and returns the dollar amount to bill that customer according to the rules above.
Problem 2
In this problem, you will write functions that examine the individual digits of an Integer. For instance, the number 122333 is both a number over one hundred thousand, and a number comprised of six digits: one 1, two 2s, and three 3s when viewed as a decimal number.
We can use basic mathematical operations to access the individual digits of any such number, by exploiting how the place-value system (also known as "positional notation") works: think about how to get the digit in the ones place, and how to get the set of digits that result from "chopping off" the ones place using simple operations.
If we want to investigate a property of the digits of an arbitrary number, we would need to examine the digits in turn, but we cannot anticipate how many there are in advance. Recursion gives us the ability to scan down the digits, however many there may be.
We need to be precise about how to handle zeros in the decimal representation of a number. For instance, the number 15100 has two zeros. But the exact same number can be written as 015100 -- this has the same mathematical value, but appears to have three zeros. These so-called "leading zeros" do not change the value of the number, and we can have arbitrarily many of them, confusing the issue of counting the number of zeros in a number. For this problem, we will never attempt to count leading zeros.
On a related note, consider the number 0 itself. We write a single digit, the digit 0, when we write this number. But it serves almost as a placeholder to show that there is a number being written; arguably this digit is itself a leading zero in front of a blank number. So, we will consider the number 0 to have no digits at all, for the purposes of this problem.
Write the following functions:
- contains-digit?, which takes in an Integer, representing a number, and a second Integer, representing a single digit, and evaluates to true if and only if at least one copy of that digit is present in the number; it is an error to be given a digit outside of the range 0-9
- all-digits>=?, with the same two parameters as above, which evaluates to true if and only if every digit of the number is at least the specified value
- at-least-k-occurrences?, which takes three Integers: a number, a digit, and a value of k, and evaluates to true if and only if the digit appears at least k times in the number (not necessarily contiguously)
Problem 3
The eyeWatch is a new smartwatch that, among other things, tracks your activity throughout the day. To entice you to be more active, it displays three pie charts indicating, respectively, for how many minutes you were standing throughout the day, how many calories you burned during exercise throughout the day, and for how many minutes you had an elevated heart rate throughout the day (indicating aerobic exercise).
In your hw2.rkt file, define a struct named Activity with three fields, each an Integer, in the following order: one named mins-standing, one named cals-burned, and one named mins-elev-hr.
Then, implement the following functions to work with this data:
- fat-burned: takes an Activity and returns the number of grams of fat burned, according to the number of calories burned in the Activity, under the assumption that the source for all the expended energy was burning fat. Assume that a gram of fat contains 3500 calories. Your return type should be an Exact-Rational.
- cals-per-min: takes in an Activity and returns the number of calories burned per minute of exercise, under the assumption that all calories burned (recorded in the second field) were burned during minutes that were accounted for in the third field. The return type should be an Exact-Rational.
- add-activities: takes in two Activity structs and returns a new one whose fields are the sum of the corresponding fields in the two inputs.
- active?: takes an Activity and returns true if and only if the wearer spent at least one hour and twenty minutes standing, burned at least 250 calories, and spent at least 30 minutes at an elevated heart rate.
- as-good?: takes two Activity structs, the first from yesterday and the second from today. It returns true if and only if each measurement is at least as good today as it was yesterday.
Problem 4
Last time, we wrote a function, eval-quadratic, to calculate the y-value of a quadratic function at a given x-value. It took four parameters; the first three were the three coefficients of a quadratic equation.
This was a bit unwieldy; it would be better to bundle together these three values into a single data structure. So, let's do that with the following struct definition:
(define-struct Quadratic ([a : Real] [b : Real] [c : Real]))
Copy and paste the above definition into your hw2.rkt file, and then implement the following functions over Quadratics:
- (: value-at : Quadratic Real -> Real) Find the value of the quadratic at given x.
- (: to-string : Quadratic -> String) Return a string of the form "ax^2+bx+c". Use the built-in functions string-append and number->string. It is acceptable if you return a string with coefficients of 0 or 1, but it would be a very nice touch to omit terms with coefficients of zero entirely, and, for terms with coefficients of 1, skip the coefficient. For negative coefficients, you can represent them with a negative number, even if that would result in a negative coefficient after a plus sign in the formula, but it would be a nice touch to use a positive coefficient and replace the plus sign with a minus sign instead.
- (: scale (Quadratic Real -> Quadratic)) Scale the quadratic by the given constant.
- (: integral : Quadratic Real Real -> Real) Compute the value of the integral from the left x to the right x.
- (: discriminant : Quadratic -> Real) Compute the value of the discriminant, b2–4ac.
- (: num-real-roots : Quadratic -> Integer) Return 0, 1 or 2 for the number of real solutions to the given quadratic. Note that you can determine this by the sign of the discriminant.
- (: has-real-roots? : Quadratic -> Boolean) Return true if the quadratic has 1 or 2 real roots.
- (: intersect? : Quadratic Quadratic -> Boolean) Return true if the two parabolas intersect at at least one point.
- (: derivative : Quadratic -> Linear)
Compute the derivative of the quadratic and return a linear equation
as represented by a structure of the following type:
(define-struct Linear ([a : Real] [b : Real]))
Submit Your Work
Submit your work by committing hw2/hw2.rkt to your CS151 subversion repository by Monday, October 9 at 11:59pm. All your work must be contained in that one file.
You should commit your work early and often. Intermediate commits — that is, commits made along the way to the final commit — are strongly encouraged; only your last commit before the deadline will be evaluated by your graders.
A note on style. Please make sure your lines of code are not more than 80 characters long. You can see the current character number at the bottom center of the DrRacket window. Use line breaks to clarify your code, and press TAB frequently within DrRacket to align the code correctly.