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.
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:
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")Kawa has built-in support for Quantities (numbers with units), so here’s a solution using them instead of case-lambda:
;;; Kawa knows about the units g, m, and cm, but not kg, ft, or lb, so add those. (define-unit ft 12in) (define-unit kg 1000g) (define-unit lb 0.453592kg) (define (bmi (weight ::quantity) (height ::quantity)) ::double (let* ((weight ::quantity (+ 0.0kg weight)) ; force to kg (height ::quantity (+ 0.0m height)) ; force to m (bmi-with-units ::quantity (/ weight (* height height)))) (*:number bmi-with-units))) ; drop the unitsThe
bmi-classfunction is the same as above.(bmi 95kg 190cm) =>26.31578947368421
(bmi 125lb (+ 5ft 4in)) =>
21.4559633220829