Assuming you took CS5, at this point in CS60 you've worked with (at least) three programming languages: Python, HMMM, and Racket. This week - and in the coming weeks - we will add Prolog to that list (if it's not already there).
Parts 1, 2, 3, and 4 this week may be done in pairs -- or individually, if you prefer. Overall, you will submit 3 files for this week's problems:
First, you will want to be sure you can run prolog -- either from
your computer or on the CS lab machines.
Instructions from the CS lab machines -- or from a Mac once Prolog is installed:
cd Desktop
You should see hw3pr1.pl when you type ls,
which is the command to list all the files in the directory.
/usr/local/bin/swipl -f hw3pr1.pl
or any other file name you'd like. Remember that you can use the up arrow to repeat
a command you've typed earlier (so that there's no need to retype it).
parent(X, bart).
once the hw3pr1.pl file is loaded. Recall that
the semicolon means OR, and asks for more
possible solutions. Hitting return stops the search for possible
solutions to a predicate query.
brew install swi-prologProlog is in some ways like Racket. However, there are a couple of important differences to keep in mind: Racket allows you to define functions and data in a file and/or at the command line (the interpreter). However, prolog is different: you must define everything in a file. The prolog command line can only be used to make queries based on the facts and rules in the file. Therefore, the following will NOT work:
?- [hw3pr1]. <-- Loads in the file hw3pr1.pl with Simpsons stuff
% simpsons compiled 0.42 sec, 4242 bytes
Yes
?- parent(bart, bartjr). % this will not work!
This is attempting to define a new fact inside Prolog's query environment
and prolog will, to use a technical term, "barf."
[40 points (5 points each), pair or individual]
This part asks you to write predicates that define family relationships and answer questions about the Simpsons' family tree -- or, at least, the version of the family tree that appears in hw3pr1.pl.
Formatting and commenting...
Please use the standard commenting
convention of your submission site id/login, file name, and date at the top of each file.
In addition, be sure to include a comment for each function
that you implement. Examples appear through the hw3pr1.pl starter file.
Finally, please be sure to name your files and functions
as noted by the problems.... The grading procedure for this assignment is
partially automated and files and functions whose spellings differ from
those below will help the graders a lot!
You should download that hw3pr1.txt file -- you may need to change its name to hw3pr1.pl from hw3pr1.txt, depending on how you get the file. If it's already hw3pr1.pl, it's OK. (This is because some browsers believe that files ending in .pl are Perl scripts.)
In that file, there are rules for a (fictional) Simpsons' family tree. These rules define, from scratch, the predicates
Finally, at the very top of the file, there are eight placeholders where you should replace the code after :- with rules that define each of the predicates described in detail below. The predicates to write are
For each predicate you write and the three provided functions, please add at least one additional test case. These test cases should be clearly labeled, by writing them in the blank provided. Again, we remind you that writing tests is a great way to start thinking about the code you plan to write and should be done BEFORE writing code.
% The following tests can be run by typing:
% run_tests(child)
:- begin_tests(child).
test(childT1) :- child(bart, marge), !.
test(childT2) :- child(lisa, marge), !.
test(childT3) :- child(bart, homer), !.
test(childT4) :-
setof(OneKid, child(OneKid, marge), AllKids),
AllKids == [bart, lisa, maggie].
test(childT5) :-
setof(OnePar, child(marge, OnePar), AllParents),
AllParents == [jackie, john].
test(childT6) :- \+child(marge, homer).
test(childT7) :- \+child(jackie, _).
% add additional tests below:
%% here's where you'd add more tests
:- end_tests(child).
The Simpsons' Family-tree predicates to write:
grandparent( john, bart ). % yes!
grandparent( lisa, bart ). % no.
grandparent( X, bart ). % there will be four answers
In fact, you can check all four of the answers to the last query above
using the setof predicate. Try this -- it will be useful
throughout using Prolog!
setof( P, grandparent( P, bart ), Answer ).
Answer = [homericus, jackie, john, matilda]
Thus, setof is a three-input metapredicate whose first input is a variable
and whose second input is a predicate using that variable. Then, setof
tries to find all of the values for that variable that satisfy the predicate.
Those values are collected into a list and bound to the name that is the third
and final input to setof. Crazy (but helpful)! Metapredicates, like
setof, are the only time you'll see any nesting of parentheses in prolog.
If you have nested parentheses - it is probably wrong!
siblings( bart, lisa ). % yes or true
siblings( bart, terpsichore ). % no or false
setof( S, siblings(S,lisa), Answer ). % lisa has 2 siblings
Answer = [bart,maggie]
cousins( bart, lisa ). % no or false
cousins( bart, terpsichore ). % yes or true
setof( C, cousins(C,lisa), Answer ). % lisa has 2 cousins here
Answer = [millhouse, terpsichore]
hasDaughterAndSon( homer ). % yes or true
hasDaughterAndSon( esmerelda ). # no or false
setof( P, hasDaughterAndSon(P), Answer ).
Answer = [cher,glum,homer,jackie,john,marge]
setof( X, hasOlderSibling(X), Answer ).
Answer = [atropos,glum,homer,homericus,lachesis,lisa,maggie,marge]
setof( P, hasYS(P), Answer ). % everyone with a younger sister
Answer = [atropos, bart, klotho, lisa, patty, selma]
setof( [X,GP], hasOlderCousin(X,GP), Answer ).
Answer = [[gomer,helga],[gomer,olf],[homer,helga],[homer,olf],[maggie,jackie],[maggie,john],[millhouse,jackie],[millhouse,john],[terpsichore,jackie],[terpsichore,john]]
There are lots of answers in this case!Hint: The negation examples we looked at in class won't help outright on this problem, but consider using hasOlderCousin as a helper predicate. Then the negation example we looked at in class will serve as a good template! Remember that the "not" operator in prolog is \+, as in the construction \+predicate(...).
You should check this with some of your own queries;
in addition, you can check for all of the people with first grandchildren via
setof( P, hasFirstGC(P), Answer ).
Answer = [esmerelda, homer, marge, matilda, skug]
Prolog reminders
Recall that "," is the symbol for AND, ";" is the SYMBOL for OR, and
"\+" is the symbol for NOT, while \== is the symbol for "not equal."
Please look at the class notes for other Prolog syntax. Another good resource is Learn Prolog Now!,
a site that explains the language concisely and with a number of examples.
Please submit your solutions in your hw3pr1.pl file under assignment #3.
Note on Prolog's output formatting
There are several settings that control how Prolog
formats its output: the following describes a few of those settings
you may want to change.
You may have noticed that when answer variables get bound to long lists,
swipl shows the list with '...' after about 10 elements,
which can be annoying.
To see the entire list, simply type
following the output. For example, if the following rules are
defined:
w
and then you try the query
range(L,H,[]) :- L > H.
range(L,H,[L|R]) :- M is L+1, range(M,H,R).
you will see the result
?- range(1, 50, X).
By typing a w when it pauses, Prolog will write the
whole list out (all 50 elements):
X = [1, 2, 3, 4, 5, 6, 7, 8, 9|...]
X = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50]
To change the limit on the number of elements once and for all, copy the
following at the top of your source file (we have done this
for the two source files this week).
You may use any other value for max_depth, including 0, which will
place no limit on the depth of the expression. The default value of
max_depth is set at 10. The values of the other attributes are just
the default ones. More on prolog's flags is available at
this link.
:- set_prolog_flag(toplevel_print_options, [quoted(true),
portray(true), attributes(portray), max_depth(999), priority(699)]).
We have six additional Java problems this week. We also want you to experiment with creating a particular type of error in Java. We've given you a challenge to complete, which is described in the following function in the file Hw3pr2.java
public static void main(String[] args)
Modify the main function as described in the file and fill in the blanks in the six assigned problems. The following resources might be helpful to you:
You are required to use the following functions when working with weighted edges and graphs. For this problem, write some simple graphs to use for testing. We have provided the definition for BigGraph below. You should create:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Weighted Edge interface
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; make-edge: creates a weighted edge from a source and destination node and a weight
;; inputs: a source node and a desintation node
;; output: a weighted edge edge
(define (make-edge s d w)
(list s d w))
;; src: gets the source of an edge
;; inputs: a weighted edge
;; output: the source node of the edge
(define (src edge)
(first edge))
;; dest: gets the destination of an edge
;; inputs: a weighted edge
;; output: the destination node of the edge
(define (dst edge)
(second edge))
;; weight: gets the weight of an edge
;; inputs: a weighted edge
;; output: the edge's weight
(define (weight edge)
(third edge))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Graph interface
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; make-empty-graph:
;; inputs: none
;; output: an empty graph
(define (make-empty-graph)
'())
;; add-edge: adds an edge to a graph (does not check for duplicates)
;; inputs: an edge and a graph
;; output: a new graph that includes the given edge
(define (add-edge e G)
(cons e G))
;; emptyGraph?:
;; inputs: a graph
;; output: #t if the graph is empty; #f otherwise
(define (emptyGraph? G)
(null? G))
;; edge-list: gets a list of edges in the graph
;; inputs: a graph
;; output: a list of edges in the graph
(define (edge-list G)
G)
;; remove-edge: removes an edge from a graph
;; inputs: an edge and a graph
;; output: a new graph that excludes the given edge
(define (remove-edge e G)
(remove e G))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Graphs for testing
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; This graph makes it clearer how graphs are created:
(define TinyGraph
(add-edge (make-edge 'a 'b 1)
(add-edge (make-edge 'b 'a 1)
(make-empty-graph))))
; identical result to BigGraph
(define BigGraphVerbose
(add-edge (make-edge 'a 'b 1)
(add-edge (make-edge 'b 'c 1)
(add-edge (make-edge 'c 'd 1)
(add-edge (make-edge 'd 'e 1)
(add-edge (make-edge 'c 'f 1)
(add-edge (make-edge 'e 'g 1)
(add-edge (make-edge 'e 'h 1)
(add-edge (make-edge 'f 'x 1)
(add-edge (make-edge 'x 'y 1)
(add-edge (make-edge 'y 'z 1)
(add-edge (make-edge 'z 'b 1)
(make-empty-graph)))))))))))))
(define BigGraph
(foldr add-edge
(make-empty-graph)
(map make-edge
'(a b c d c e e f x y z)
'(b c d e f g h x y z b)
'(1 1 1 1 1 1 1 1 1 1 1))))
In this problem, you'll use the function (edge-list <graph>)
to get a list of edges in the graph. (note: we know that the interface uses a
list of edges to represent a graph, but we don't violate the abstraction! We can
call (rest <list-of-edges>) but never (rest <graph>) Write a function
which takes in
(define (gchildren N n G)
(check-expect (gchildren 0 'a BigGraph) '(a))
(check-expect (gchildren 1 'a BigGraph) '(b))
(check-expect (gchildren 2 'a BigGraph) '(c))
;; these tests use sortsym to avoid ambiguity in node order...
(define (sym<? s1 s2) (string<? (symbol->string s1) (symbol->string s2)))
(define (sortsym L) (sort L sym<?)) ;; will sort a list of symbols
(check-expect (sortsym (gchildren 1 'c BigGraph)) '(d f))
(check-expect (sortsym (gchildren 2 'c BigGraph)) '(e x))
(check-expect (sortsym (gchildren 3 'a BigGraph)) '(d f))
(check-expect (sortsym (gchildren 3 'c BigGraph)) '(g h y))
You might want to implement (kids n G) - a function that returns
the list of children of node n in graph G: it can
be used to do most of the work of gchildren.
Remember to write additional test cases!!
Write (min-dist a b G), which takes in two nodes a and b and a directed, positively-weighted graph G.
Your min-dist should
return the smallest distance that is required to travel from a
to b through graph G. Every
node should be considered 0 away from itself. However, if
there is no path from a to b in G,
for different nodes a and b, your function
should return the
value 42000000, which will be bigger than any feasible path
in our graphs. (The problem with using the value +inf.0 is
that it is floating-point, which changes check-expect's behavior.)
For example,
To help sanity-check your tests, here is a picture of Gt:
(define Graph2
(foldr add-edge
(make-empty-graph)
(map make-edge
'(e a e a a a b b d b c c)
'(b b a c d e c d e e d e)
'(100 25 42 7 13 15 10 5 2 100 1 7))))
(check-expect (min-dist 'a 'e Graph2) 10)
(check-expect (min-dist 'e 'b Graph2) 67)
(check-expect (min-dist 'd 'd Graph2) 0)
(check-expect (min-dist 'f 'a Graph2) 42000000)

You should also start by writing tests using your graphs from question #1.
Hint: You might start with the reach code here and consider how to change the return values appropriately from booleans to numeric quantities... !
This semester you'll build your own task-specific language that will be able to handle arbitrary multiplication-based conversions from one unit to another -- and account for error propagation, too. To do this, your application will be able to navigate within arbitrarily large graphs of unit-conversion data. We're calling this application a unit-calculator, or unicalc.
You'll prototype your
application in Racket this week. When we begin to focus on Java you'll build the
fundamental data structure for a Java implementation of unicalc.
Pair or individual problem You may work on this one either individually or in a pair. If you work in a pair, be sure to follow the CS 60 pair-programming guidelines.
43 Points
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.
Quantity Lists The fundamental structure used in unicalc is called a Quantity List. As a data structure, a Quantity List is a 4-element Racket list in which
'(9.8 (meter) (second second) 0.01)
Generally speaking, a data structure is rather low-level: it specifies the particular way that language-specific and/or machine-specific constructs are used to organize data, as the above example indicates. An information structure, on the other hand, provides problem-specific capabilities that are independent of a particular application. To reinforce this distinction, we provide a make-QL function in Racket that creates a Quantity List. In addition, we provide the accessor functions get-quant, get-num, get-den, and get-unc, which return the individual pieces of a Quantity List. In order to compare and sort lists of symbols, we provide functions sym<? and sortsym.
In Racket, the difference between data structure and information structure is almost entirely one's point of view. After all, the different ways of organizing data are relatively limited!
The unit database
In the file hw3pr4.rkt is a
unit-conversion database in the form of an association list - here
are a few of its conversions:
Not every unit is defined in terms of others in the
database. Some are intentionally undefined: these are
what we call basic units, e.g.,
second, kg, meter, and ampere.
(define unicalc-db
(list
(list 'A (make-QL 1.0 '(ampere) '() 0.0))
(list 'faraday (make-QL 96490.0 '(coulomb) '() 0.0))
(list 'farad (make-QL 1.0 '(coulomb) '(volt) 0.0))
(list 'fathom (make-QL 6.0 '(foot) '() 0.0))
(list 'hour (make-QL 60.0 '(minute) '() 0.0))
(list 'inch (make-QL 0.02539954113 '(meter) '() 0.0))
... ))
When a quantity is normalized, it is converted entirely into basic units. Also, the database does not contain conversions from everything to everything: that would make it very large and difficult to maintain! Instead, the database can be used multiple times, e.g., to convert furlongs to miles to feet to inches to meters.
A note on uncertainty-propagation: there are many online references that indicate how to propagate uncertainty through various functions. We used this reference from Harvard and this error-propagation calculator as a check for our results. Our uncertainties represent the size of one-standard-deviation within a Gaussian distribution around the quantity in our quantity list - note that we're not tracking relative uncertainties here, though the formulas below do compute them, as needed.
(define (merge L1 L2)
that takes in two sorted lists of symbols, L1 and L2 and
then returns a new sorted
list of all the symbols in both L1 and
L2.
(check-expect (merge '(m m s) '(kg m s s)) '(kg m m m s s s))
The function sym<? is provided at the top
of the starter file in order to compare
two symbols alphabetically.
For this function, you shouldn't use sort. Instead, you should compare
the first element in each list to figure out which element should go first.
Your solution should create the final list element by element until
one of the lists is empty.
(define (n-merge L n)
that takes in one sorted lists of symbols, L and
a nonnegative integer n. Then n-merge
should return a new sorted
list of all the symbols in n copies of L.
(check-expect (n-merge '(kg m s) 0) '())
(check-expect (n-merge '(kg m s) 1) '(kg m s))
(check-expect (n-merge '(kg m s) 2) '(kg kg m m s s))
Hint: use merge and recursion!
(define (cancel L1 L2)
that takes in two sorted lists of symbols, L1 and L2 and
then returns a new list of all the symbols in L1 after
canceling with L2. The output list should still
be sorted, too. Here, canceling means removing
common symbols as many times as they occur in both lists.
These tests will make that clearer:
(check-expect (cancel '(m m s) '(kg m s s)) '(m))
(check-expect (cancel '(m s) '(kg m s s)) '())
(check-expect (cancel '(m s s s) '(kg m s s)) '(s))
(check-expect (cancel '(kg m s s) '(m m s)) '(kg s))
For this function you must use the same element-by-element approach
as in the merge function. For example, you shouldn't use
member, remove, or sort.
The function sym<? is provided to compare
two symbols alphabetically.
The details will be slightly different, but your code will
have the same structure as it did in merge.
(define (simplify QL)
that takes in a quantity list QL and
returns another quantity list of the same value, but
having canceled out any shared elements between the numerator
and denominator. Here are some examples:
(check-expect (equal-QLs? (make-QL 1.0 '(m) '(s) 0.0)
(simplify '(1.0 (m m s) (m s s) 0.0)))
#t)
(check-expect (equal-QLs? (make-QL 1.0 '(m) '(kg) 0.0)
(simplify '(1.0 (kg m m s) (kg kg m s) 0.0)))
#t)
(define (multiply QL1 QL2)
that takes in two quantity lists QL1 and QL2
and returns a quantity list representing their product.
The result returned should be simplified!.
Note that if q1 and q2 are the quantities and u1 and u2 are
the uncertainties in QL1 and QL2,
then the uncertainty in the product is given by
(check-expect (equal-QLs? (make-QL 1764.0 '(m) '(s) 59.39696962)
(multiply (make-QL 42.0 '(kg m m) '(s s) 1.0)
(make-QL 42.0 '(s) '(kg m) 1.0)))
#t)
(check-expect (equal-QLs? (make-QL 1.0 '(kg meter x) '(amp s w) 0.02061553)
(multiply (make-QL 2.0 '(meter) '(amp w) 0.01)
(make-QL 0.5 '(kg x) '(s) 0.01)))
#t)
Use the helper function
merge and not sort.
(define (power QL p)
that takes in a quantity lists QL and an
integer p
and returns a quantity list representing QLp.
(check-expect (equal-QLs? (make-QL 1.0 '() '() 0.0)
(power (make-QL 200.0 '(euro) '() 1.0) 0))
#t)
(check-expect (equal-QLs? (make-QL 0.005 '() '(euro) 2.5e-005)
(power (make-QL 200.0 '(euro) '() 1.0) -1))
#t)
(check-expect (equal-QLs? (make-QL 8000000.0 '(euro euro euro) '() 120000.0)
(power (make-QL 200.0 '(euro) '() 1.0) 3))
#t)
Warning Implement power on its own via
recursion - don't use multiply. The reason is that uncertainty
propagates differently when you multiply two independent
variables than when you multiply the same variable, as power
does.
(define (divide QL1 QL2)
that takes in two quantity lists QL1 and QL2
and returns a quantity list representing the quotient of QL1/QL2.
Again, the result returned should be simplified! Note that
the uncertainty is similar to the multiplication case:
(check-expect (equal-QLs? (make-QL 1.0 '(m) '(s) 0.03367175)
(divide (make-QL 42.0 '(kg m m) '(s s) 1.0)
(make-QL 42.0 '(kg m) '(s) 1.0)))
#t)
(check-expect (equal-QLs? (make-QL 4.0 '(meter s) '(amp kg w x) 0.08246211)
(divide (make-QL 2.0 '(meter) '(amp w) 0.01)
(make-QL 0.5 '(kg x) '(s) 0.01)))
#t)
(define (norm-unit unit)
that takes in a Racket symbol unit and
returns the equivalent normalized quantity list.
This will require not only looking up unit in the
provided database, unicalc-db, as we did in class,
but then making sure the final result is normalized,
that is, consists only of basic units. The final result
should be simplified, as well. You should use 0.0
as the uncertainty of a plain unit (the unicalc
database will do this for you). Here are some examples:
(check-expect (equal-QLs? (make-QL 1.0 '(kg meter meter)
'(ampere second second second second) 0.0)
(norm-unit 'weber))
#t)
(check-expect (equal-QLs? (make-QL 1.0 '(ampere) '() 0.0)
(norm-unit 'ampere))
#t)
Hint: read over norm-QL before implementing
norm-unit. Then, implement each one so that it
calls the other: an example of
mutual recursion. If you'd like a bit more
explanation on this idea, see the q/a link at the
end of norm-QL.
(define (norm-QL QL)
that takes in a quantity list QL and
returns the equivalent normalized quantity list.
This will require lots of looking-up in the
provided database, so we strongly suggest a
mutual-recursion approach with norm-unit.
That is, "peel off" one unit, if there is one,
then use norm-unit to find the quantity list
equivalent to that unit. Then use multiply
or divide, as appropriate. Recursion will
be crucial here! The final result
should be simplified. Here are some examples:
(check-expect (equal-QLs? (make-QL 42.0 '() '(ampere second) 1.0)
(norm-QL (make-QL 42.0 '(weber) '(ampere volt) 1.0)))
#t)
(check-expect (equal-QLs? (make-QL 4.3890407 '(meter) '() 0.09143834)
(norm-QL (make-QL 14.4 '(foot) '() 0.3)))
#t)
(check-expect (equal-QLs? (make-QL 0.010159816 '(meter) '(second) 0.0005079908)
(norm-QL (make-QL 2.0 '(foot) '(minute) 0.1)))
#t)
Note: In the spring of 2011, Jane Hoffswell asked a great question
about this... if you're feeling like some additional guidance would help, the message is linked here.
(define (add QL1 QL2)
that takes in two quantity lists QL1 and
QL2, and returns their normalized
sum. It's a good idea to normalize first -- that way
you can detect a unit mismatch. If, after normalzing,
the quantity lists' units do not match, return
(list 'error 'add QL1 QL2)
If they can be added, you'll need to use the
error-propagation formula:
(check-expect (equal-QLs? (make-QL 2.13356145 '(meter) '() 0.03592037)
(add (make-QL 42.0 '(inch) '() 1.0)
(make-QL 42.0 '(inch) '() 1.0)))
#t)
(check-expect (equal? (add (make-QL 42.0 '(inch) '() 1.0)
(make-QL 42.0 '(s) '() 1.0))
(list 'error 'add
(make-QL 42.0 '(inch) '() 1.0)
(make-QL 42.0 '(s) '() 1.0)))
#t)
(define (subtract QL1 QL2)
that takes in two quantity lists QL1 and
QL2, and returns their normalized
difference: QL1 - QL2.
This will be almost identical to add,
except that if, after normalzing,
the quantity lists' units do not match, return
(list 'error 'subtract QL1 QL2)
If they can be subtracted, the
error-propagation formula is the same as for addition.
Here are two examples:
(check-expect (equal-QLs? (make-QL 0.0 '(meter) '() 0.03592037)
(subtract (make-QL 42.0 '(inch) '() 1.0)
(make-QL 42.0 '(inch) '() 1.0)))
#t)
(check-expect (equal? (subtract (make-QL 42.0 '(inch) '() 1.0)
(make-QL 42.0 '(s) '() 1.0))
(list 'error 'subtract
(make-QL 42.0 '(inch) '() 1.0)
(make-QL 42.0 '(s) '() 1.0)))
#t)
Note that subtract will be largely copied from add - this is completely OK!
Where's convert?
With this toolkit of functions in place,
unit-conversion turns out to be an application of division:
Feel free to include this and try it out!
(define (convert from to)
(norm-QL (divide from to)))
More tests... Here are
a few more tests you might try. These combine your unit-calculation
functions into composite operations:
(check-expect (equal-QLs? (make-QL 0.38735983 '(second) '() 0.030581039)
(divide (norm-QL (make-QL 3.8 '(m) '(s) 0.3))
(norm-QL (make-QL 9.81 '(m) '(s s) 0.0))))
#t)
(check-expect (equal-QLs? (norm-QL (make-QL 0.48 '(cm) '() 0.6327716))
(let* ((w (make-QL 4.52 '(cm) '() 0.02))
(x (make-QL 2.0 '(cm) '() 0.2))
(y (make-QL 3.0 '(cm) '() 0.6)))
(subtract (add (norm-QL x) (norm-QL y)) (norm-QL w))))
#t)
(check-expect (equal-QLs? (norm-QL (make-QL 18.04 '(cm cm) '() 3.7119827))
(let* ((w (make-QL 4.52 '(cm) '() 0.02))
(x (make-QL 2.0 '(cm) '() 0.2))
(y (make-QL 3.0 '(cm) '() 0.6)))
(norm-QL (add (multiply w x) (power y 2)))))
#t)
(check-expect (equal-QLs? (make-QL 5.1 '(meter) '() 0.360555127)
(subtract (norm-QL (make-QL 14.4 '(meter) '() 0.3))
(norm-QL (make-QL 9.3 '(meter) '() 0.2))))
#t)
(check-expect (equal-QLs? (make-QL 23.7 '(meter) '() 0.360555127)
(add (norm-QL (make-QL 14.4 '(meter) '() 0.3))
(norm-QL (make-QL 9.3 '(meter) '() 0.2))))
#t)
Congratulations! on creating an error-propagating unit calculator! Later on, you will extend this prototype implementation into a full-fledged language... in Java. For the moment, however, there's still a bit more Java to get used to!
Submitting Be sure to submit hw3pr3.rkt to the submission site.