This problem asks you to write several Racket functions: together, they will implement a "unit-calculator" or Unicalc application. A complete application will be able to convert any unit quantity to any compatible quantity by using an association list as a database of known conversions.
Design, formatting and testing will account for roughly a quarter of the credit for each function or program you complete.
The graders will keep an eye out for
your overall design with these guidelines in mind.
Again, we will provide several tests in
the problem descriptions. These example are not complete, however -- designing
and using your own tests is crucial
to testing all of the cases your code needs to handle.
Be sure that you name your functions as specified in each problem -- this helps enormously with the grading! Helper functions may have any names appropriate for their operation.
Start by downloading the support files
for this problem:
a02.rkt– Skeleton file,
with some tests.·
unicalc-db.rkt The database file.
This is the first part of a 3-part assignment. Unicalc is a calculator that includes physical and other units, rather than just numbers. Units are important to computation because they eliminate an element sometimes left to human interpretation. In the area of engineering, failure to interpret numbers without their units specified has been known to lead to failure of space missions for example. In the wikipedia article about NASA’s Mars Climate Orbiter, it is stated:
“The Mars Climate Orbiter was intended to enter orbit at an altitude of 140–150
km (460,000-500,000 ft.) above Mars. However, a navigation error caused the
spacecraft to reach as low as 57 km (190,000 ft.). The spacecraft was destroyed
by atmospheric stresses and friction at this low altitude. The navigation error
arose because a NASA subcontractor (Lockheed Martin) used Imperial units
(pound-seconds) instead of the metric system.”
A little closer to home, you will have to do many unit conversions and operations in your other classes here at HMC. It is our hope that this calculator will prove useful in those classes.
At the heart of the Unicalc back-end is our choice of data representation.
We will be working with numerical quantities, including units. Examples
of these quantities in conventional notation, would be as follows, where ^
denotes raising to a power.
420 seconds
4.2 meters / second^2
42.42 mile / hour
We need some way to represent this data in Racket. In particular, we need
to create a data abstraction so that whatever representation we choose,
it would be easy to change in the future if we so desired.
This week, we will represent our data as certain lists structures, which we
call Quantities. The Quantity data abstraction consists of a
numeric multiplier, a flat list of symbols (not strings) as the numerator, and
a flat list of symbols as a denominator.
;; Data abstraction for a Quantity
; Function: make-quantity
; Arguments:
; multi: a number
; numer: a flat list of symbols
representing the units in the numerator
; denom: a flat list of symbols
representing the units in the denominator
; Result:
; the representation of a Quantity
(define (make-quantity multi numer
denom)
(list multi numer denom))
; Accessor
functions for a Quantity
(define get-multi first)
(define get-numer second)
(define get-denom third)
Using this representation, the above examples would look like:
‘(420 (second) ())
‘(4.20 (meters) (second second))
‘(42.42 (mile) (hour))
NOTE: Although make-quantity,
get-multi, get-numer
and get-denom
simply rename the functions first,
second and third, when you use
quantities in this assignment, you should always use the named functions and never use first, second, third directly for this
purpose, even though they do the exact same thing currently. This is so that if
you need to change the meaning of any of these functions, as you may in the
future, you do not have to worry about confusing them other uses of the basic
built-in functions.
Symbols, rather than
strings, are what are contained in the flat lists inside a quantity. You will
want to be able to compare symbols alphabetical. You can create your own
symbol<? Function, using the information in http://www.cs.hmc.edu/courses/2012/fall/cs42/symbolsAndStrings.rkt
On the main course page, and above on
this page, you will find unicalc-db.rkt. This is a Racket source file for the Unicalc database. The database is, in effect, a set of
equations defining single symbols, called units, in terms of Quantities. As a
Racket structure, it is an association
list to which variable unicalc-db
will be bound. You will want to download this file into the same directory as
your code.
The API is a set of functions that you will construct, as described below, along with other helper functions. To describe our functions, we need a couple more definitions.
Defined Units: A symbol that is defined in the database (as the first element of an element of the association list) is called a defined unit.
Basic Units:
A symbol that is not a defined unit
is called a basic unit. So basic
units have that property due to the omission of definitions for them in the
database.
For unicalc-db as currently constituted, examples of defined units are: mile, day, pound. Examples of basic units are: kg, second, meter, and most other SI units.
Defined units can be expanded into equivalent Quantities consisting of only basic units in one or more steps, based on definitions in the database. (You can assume for now that there are no circular definitions in the database.)
Basic units can be trivially converted into quantities by making them the only element in the numerator list, accompanied by a multiplier of 1 and an empty denominator list.
Normalized Quantities: A Quantity is
called normalized provided that the
following two conditions are true:
· The numerator and denominator lists consist of only basic units. (Either could be empty.)
· The numerator and denominator lists are sorted in ascending alphabetic order.
· The numerator and denominator have no units in common.
Here are the functions you need to provide in the API. We don’t require the user to provide normalized Quantities, but it helps make processing more efficient if internally use only normalized quantities. Note that divide effectively achieves conversion from one kind of normalized Quantity to another. Below, Q1 and Q2 always represent Quantities.
|
Function Call Form |
Meaning |
|
|
Returns a normalized Quantity
corresponding to 1 in the given unit (where this Unit is a single unit such
as |
|
|
Returns a normalized Quantity from
any Quantity Q1. |
|
|
Returns the normalized result of
multiplying normalized quantity Q1
by Q2. |
|
|
Returns the normalized result of dividing
normalized quantity Q1 by Q2. |
|
|
Returns the result of adding normalized
Q1 to normalized Q2, provided the quantities are interconvertible. The result should be in units of Q1. |
|
|
Returns the result
of subtracting normalized Q2 from
normalized Q1, provided the
quantities are interconvertible. The result should be in
units of Q1. |
|
|
Returns the result of raising Q1 to the integer power |
Note that the database, unicalc-db,
is global and is imported to these
functions, rather than being passed as an argument.
You will need other functions to make all of this work, but those functions will not be advertised to the user. Here are a few suggested utilities. These will also provide some practice with simpler functions.
|
Function Call Form |
Meaning |
|
|
Make a normalized Quantity from a
multiplier, a list of units for the numerator, and a list of units for the
denominator. I will provide this definition as an example. |
|
|
Make a normalized Quantity from a singe
unit. |
|
|
Make a normalized Quantity from a singe
number. |
|
|
Merge two lists of symbols that are
already sorted into a single sorted list. |
|
|
Given two sorted
lists of symbols, for each instance of a symbol in L that occurs in M, remove
an instance from L. |
|
|
Returns the normalized equivalent of
the product of a list of units. |
|
|
Not a function, but a constant that
represent the number 1 as a normalized Quantity. |
Although we have provided test cases, you should also include at least one
more test case for each of the functions specified above. Include these
test cases in your a02.rkt source file. We will also test
your code on additional cases not provided.
To reduce amount of typing and space that is occupied by test cases, I provide two convenience functions. These are to simplify the user interface only. The cannot be used to solve the main problems, as this would create circularity.
|
Function Call Form |
Meaning |
|
|
Make a Quantity from an S-expression,
such as ‘(42.24 (meter)(second second)), by calling
make-quantity. |
|
|
Make a normalized Quantity from an S-expression, such as ‘(42.24
(meter)(second second)), by calling
make-normalized-quantity. |
Rather than throwing an error in the case of addition and subtraction of non-interconvertible quantities, we could return a Super-Quantity representing the sum or difference. This would require a special data representation distinguishing a Super-Quantity from an ordinary Quantity. The content of a Super-Quantity would be a list of two or more Quantities. Then we could represent ideas such as:
add(3 apples, 2 bananas) gives “3 apples and 2 bananas”
While returning such a Super-Quantity for adding and subtracting pure Quantities is not difficult, we’d have to have closure, meaning that add and subtract could take Super-Quantities as arguments. Moreover, we’d have to extend multiply, divide, and power to accept Super-Quantities as well. This will require some design.
It is required that Super-Quantities always have an extra level of normalization that groups interconvertible summands. For example, adding “3 apples and 2 bananas” to “4 oranges and 5 bananas” should give “3 apples, 7 bananas, and 4 oranges”.
If you decide to try this problem, include test cases to show that your solution does have closure.