## BMI Calculator

### August 30, 2013

We distinguish between imperial and metric units by the number of arguments given to the function, using `case-lambda`

:

`(define bmi`

(case-lambda

((feet inches pounds)

(let ((height (+ (* feet 12) inches)))

(* (/ pounds (* height height)) 703.0)))

((centimeters kilograms)

(let ((meters (/ centimeters 100.0)))

(/ kilograms (* meters meters))))))

`(define (bmi-class bmi)`

(cond ((<= bmi 18.5) "underweight")

((< bmi 25) "normal")

((< bmi 30) "overweight")

((< bmi 40) "obese")

(else "morbidly obese")))

`Case-lambda`

was only added to the Scheme standard in R6RS, though it has been available for years. If your Scheme doesn’t have `case-lambda`

, here’s a definition from SRFI-16:

`(define-syntax case-lambda`

(syntax-rules ()

((case-lambda)

(lambda args

(error "CASE-LAMBDA without any clauses.")))

((case-lambda

(?a1 ?e1 ...)

?clause1 ...)

(lambda args

(let ((l (length args)))

(case-lambda "CLAUSE" args l

(?a1 ?e1 ...)

?clause1 ...))))

((case-lambda "CLAUSE" ?args ?l

((?a1 ...) ?e1 ...)

?clause1 ...)

(if (= ?l (length '(?a1 ...)))

(apply (lambda (?a1 ...) ?e1 ...) ?args)

(case-lambda "CLAUSE" ?args ?l

?clause1 ...)))

((case-lambda "CLAUSE" ?args ?l

((?a1 . ?ar) ?e1 ...)

?clause1 ...)

(case-lambda "IMPROPER" ?args ?l 1 (?a1 . ?ar) (?ar ?e1 ...)

?clause1 ...))

((case-lambda "CLAUSE" ?args ?l

(?a1 ?e1 ...)

?clause1 ...)

(let ((?a1 ?args))

?e1 ...))

((case-lambda "CLAUSE" ?args ?l)

(error "Wrong number of arguments to CASE-LAMBDA."))

((case-lambda "IMPROPER" ?args ?l ?k ?al ((?a1 . ?ar) ?e1 ...)

?clause1 ...)

(case-lambda "IMPROPER" ?args ?l (+ ?k 1) ?al (?ar ?e1 ...)

?clause1 ...))

((case-lambda "IMPROPER" ?args ?l ?k ?al (?ar ?e1 ...)

?clause1 ...)

(if (>= ?l ?k)

(apply (lambda ?al ?e1 ...) ?args)

(case-lambda "CLAUSE" ?args ?l

?clause1 ...)))))

You can run the program at http://programmingpraxis.codepad.org/frLTNeu8.

Pages: 1 2

My version in Python. I cheated a little with the categories. According to specs underweight is when bmi <= 18.5. Here it is if bmi < 18.5.

CATEGORIES = zip([18.5, 25, 30, 40, 1000], ["underweight", "normal",

"overweight", "obese", "morbidly obese"])

WEIGHT_CONV = dict(zip(["kg", "lbs", "ounce"], [1, 0.45359237, 0.0283495231]))

LENGTH_CONV = dict(zip(["m", "cm", "inch", "ft"], [1, 0.01, 0.0254, 0.3048]))

def val_metric(value, conv):

"""convert to metric units (kg, m)

allowed: 5 ft 8 inch or 100 lbs 3 ounce

"""

elements = value.split()

total = 0

while elements:

total += float(elements.pop(0)) * conv[elements.pop(0)]

return total

def bmi(weight, length):

w = val_metric(weight, WEIGHT_CONV)

l = val_metric(length, LENGTH_CONV)

bmi = w / l ** 2

for c, txt in CATEGORIES:

if bmi < c:

cat = txt

break

return bmi, cat

print "bmi=", bmi("160 lbs", "5 ft 7 inch")

Hopefully this time with better formatting:

Kawa has built-in support for Quantities (numbers with units), so here’s a solution using them instead of case-lambda:

The

`bmi-class`

function is the same as above.`(bmi 95kg 190cm) =>`

26.31578947368421

(bmi 125lb (+ 5ft 4in)) =>

21.4559633220829