Converting English Measurements to Metric and vice versa

I finished answering Exercise 3.3.1 of How to Design Programs where:

Exercise 3.3.1. The United States uses the English system of (length) measurements. The rest of the world uses the metric system. So, people who travel abroad and companies that trade with foreign partners often need to convert English measurements to metric ones and vice versa.

The code is also located in here.

;; Contract: print : expr expected_expr ->
;;expr "should be" expected_expr

;; Purpose: to test expressions to find out whether they're
;; correct or not

;; Examples: (print (inches->cm 22/7) 7.98287514)
;;           => "3.142857142857143 should be 7.98287514"

(define (print actual expected)
(string-append
(number->string (exact->inexact actual))
" should be "
(number->string (exact->inexact expected))))

;; Contract: inches->cm : number -> number

;; Purpose: to convert inches to centimeters

;; Examples: (inches->cm 22/7) should be 7.9828514

(define (inches->cm in)
(* in 2.54))

;; Tests:
(print (inches->cm 22/7) (* 2.54 (/ 22 7)))
(print (inches->cm 3.5) (* 2.54 3.50))
(print (inches->cm 100) (* 2.54 100))

;; Contract: feet->inches : number -> number

;; Purpose: to convert feet to inches

;; Examples: (feet->inches 32) should be 384

;; Definition:
(define (feet->inches ft)
(* ft 12))

;; Tests:
(print (feet->inches 32) (* 32 12))
(print (feet->inches 12.5) (* 12.5 12))
(print (feet->inches -99) (* -99 12))

;; Contract: yards->feet : number -> number

;; Purpose: to convert yards to feet

;; Examples: (yards->feet 3) should be 9

;; Definition:
(define (yards->feet yd)
(* yd 3))

;; Tests:
(print (yards->feet 3) (* 3 3))
(print (yards->feet 1.5) (* 1.5 3))
(print (yards->feet -3.5) (* -3.5 3))

;; Contract: rods->yards : number -> number

;; Purpose: to convert rods to yards

;; Examples: (rods->yards 1) should be 5.5

;; Definition:
(define (rods->yards rd)
(* rd 5.5))

;; Tests:
(print (rods->yards 1) (* 1 5.5))
(print (rods->yards 2) (* 2 5.5))
(print (rods->yards 3) (* 3 5.5))

;; Contract: furlongs->rods

;; Purpose: to convert furlongs to rods

;; Examples: (furlongs->rods 5) should be 200

;; Definition:
(define (furlongs->rods fl)
(* fl 40))

;; Tests:
(print (furlongs->rods 5) (* 5 40))
(print (furlongs->rods 3) (* 3 40))
(print (furlongs->rods 1) (* 1 40))

;; Contract: miles->furlongs

;; Purpose: to convert miles to furlongs

;; Examples: (miles->furlongs 5) should be 40

;; Definition:
(define (miles->furlongs mi)
(* mi 8))

;; Tests:
(print (miles->furlongs 5) (* 5 8))
(print (miles->furlongs 6.6) (* 6.6 8))
(print (miles->furlongs (/ 22 7)) (* (/ 22 7) 8))

;; Contract: feet->cm : number -> number

;; Purpose: to convert feet to centimeters

;; Examples: (feet->cm 1) should be 0.032808

;; Definition:
(define (feet->cm ft)
(/ ft 30.48))

;; Tests:
(print (feet->cm 1) (* 1 30.48))
(print (feet->cm 3) (* 3 30.48))
(print (feet->cm 5) (* 5 30.48))

;; Contract: yards->cm : number -> number

;; Purpose: to convert yards to cm

;; Examples: (yards->cm 1) should be 91.44

;; Definition:
(define (yards->cm yd)
(* yd 91.44))

;; Tests:
(print (yards->cm 1) (* 1 91.44))
(print (yards->cm 5.5) (* 5.5 91.44))
(print (yards->cm 9.324) (* 9.324 91.44))

;; Contract: rods->inches : number -> number

;; Purpose: to convert rods to inches

;; Examples: (rods->inches 1) should be 198

;; Definition:
(define (rods->inches rd)
(* rd 198))

;; Tests:
(print (rods->inches 1) (* 1 198))
(print (rods->inches 3.3) (* 3.3 198))
(print (rods->inches 9.9) (* 9.9 198))

;; Contract: miles->feet : number -> number

;; Purpose: to convert miles to feet

;; Examples: (miles->feet 1) should be 5280

;; Definition:
(define (miles->feet mi)
(* mi 5280))

;; Tests:
(print (miles->feet 1) (* 1 5280))
(print (miles->feet #i3.14159) (* #i3.14159 5280))
(print (miles->feet 3) (* 3 5280))

#| Start Of Results

"7.982857142857143 should be 7.982857142857143"
"8.89 should be 8.89"
"254.0 should be 254.0"
"384.0 should be 384.0"
"150.0 should be 150.0"
"-1188.0 should be -1188.0"
"9.0 should be 9.0"
"4.5 should be 4.5"
"-10.5 should be -10.5"
"5.5 should be 5.5"
"11.0 should be 11.0"
"16.5 should be 16.5"
"200.0 should be 200.0"
"120.0 should be 120.0"
"40.0 should be 40.0"
"40.0 should be 40.0"
"52.8 should be 52.8"
"25.142857142857142 should be 25.142857142857142"
"0.03280839895013123 should be 30.48"
"0.0984251968503937 should be 91.44"
"0.16404199475065617 should be 152.4"
"91.44 should be 91.44"
"502.92 should be 502.92"
"852.58656 should be 852.58656"
"198.0 should be 198.0"
"653.4 should be 653.4"
"1960.2 should be 1960.2"
"5280.0 should be 5280.0"
"16587.5952 should be 16587.5952"
"15840.0 should be 15840.0"

End Of Results|#

Each function is documented in this structure:

Contract: function_name : input -> output

Purpose: [describes the purpose of the functiona]

Examples: [insert example here]

Definition:
[define function here]

Tests:
[print (expr) (expected expr)]
[print (expr) (expected expr)]
[print (expr) (expected expr)]

and tested three times. I am wondering if there's a way to make a better documentation than what my program contained. I don't regret the fact I written 220 lines of code filled with documentations, repetition helps me get used to programming languages.

Notice that there's a print function, it's what I use to test each function three times. It was implemented by Óscar-lópez of Stack Overflow in my question.

I want to know a better way to document these function (if any) to make programs shorter.

• Instead of using the print function, utilize check-expects (built into racket) or some other unit testing framework Aug 2 '14 at 3:21

I see a few points that seem open to improvement.

First of all, in some cases you document the "contract" based on the type of the input and output ("number -> number") but in others the unit of measure ("Miles -> furlongs").

The units of measure are already documented directly in the function's name, so the type is the only one that really makes much sense to document in comments. For that, you'd probably be better off with a single header documenting the contract for all the functions.

Second, essentially all of these are really just multiplying/dividing the input by a factor, and returning the result. As such, it would probably make more sense to pass the units to convert as parameters. Then you'd have a single function that looked up the correct factor from a table and returned the result based on that.

Third, I'd rather see a function that actually compared results and told me whether the test passed or failed. As it stands now, I'm left to scan through the results myself to see whether the functions worked correctly or not.

Finally, the units you've chosen to support seem rather strange, at least to me. "rods" are used almost exclusively in surveying, and furlongs are used almost exclusively in stories about medieval times. At the same time, although the initial discussion is about converting between Imperial and metric measures, the only metric unit you seem to support is the centimeter, not such common measures as meters or kilometers.

P.S. The basic premise in the exercise is also somewhat incorrect: not all the rest of the world uses the metric system. For example, Imperial measures are still used quite a bit in the UK, and in some other former UK colonies as well.

> I want to know a better way to document these function (if any) to make programs shorter.
• I would use less whitespace. It not only makes it shorter, but also easier to read
• I would also remove the word Definition as it's redundant
• I would move the tests to an extra file or at least the end of the file

So you would get this:

;; Contract:    feet->inches : number -> number
;; Purpose:     to convert feet to inches
;; Examples:    (feet->inches 32) should be 384
(define (feet->inches ft)
(* ft 12))

;; Contract:    yards->feet : number -> number
;; Purpose:     to convert yards to feet
;; Examples:    (yards->feet 3) should be 9
(define (yards->feet yd)
(* yd 3))

[...]

;; Tests
(print (feet->inches 32) (* 32 12))
(print (feet->inches 12.5) (* 12.5 12))
(print (feet->inches -99) (* -99 12))

(print (yards->feet 3) (* 3 3))
(print (yards->feet 1.5) (* 1.5 3))
(print (yards->feet -3.5) (* -3.5 3))

I think that this would be a pretty good solution. It looks nice and uniform, but not too verbose.

Here are some other format ideas, reducing the amount of lines for the comment to two or one. They are not necessarily better, but they are shorter:

;; Purpose:     to convert feet to inches
;; Examples:    (feet->inches 32) should be 384
(define (feet->inches ft) ;; Contract: number -> number
(* ft 12))

;; Contract:    feet->inches : number -> number
;; Purpose:     to convert feet to inches (e.g. 32 feet = 384 inches)
(define (feet->inches ft)
(* ft 12))

;; Purpose:     to convert feet to inches (e.g. 32 feet = 384 inches)
(define (feet->inches ft) ;; Contract: number -> number
(* ft 12))

I think that you could also get rid of the example. The functions are simple enough that they are not really needed, and you already have the test prints at the end. So another possibility might be:

;; number -> number: converts yards to feet
(define (yards->feet yd)
(* yd 3))

Other than this, I second what Jerry Coffin said.