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.

About these ads

Pages: 1 2

3 Responses to “BMI Calculator”

  1. Paul said

    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")

  2. Paul said

    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")
    
  3. Jamie Hope said

    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 units
    

    The bmi-class function is the same as above.

    (bmi 95kg 190cm) =>
    26.31578947368421
    (bmi 125lb (+ 5ft 4in)) =>
    21.4559633220829

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 629 other followers

%d bloggers like this: