From: R. Bharat Rao
Subject: Amazing the dumb mistakes we make...
Date: 
Message-ID: <Bso9Jr.LJD@ux1.cso.uiuc.edu>
;; Been coding for all your life and still you can make some really,
;; really, really *dumb* mistakes.  Just for laughs try and spot the error
;; in the code below.  I must have been on something when I wrote it..

;; A sadistic examiner for LISP programming course may say

Q 1) Does the code below work as intended.  If not, explain why.  Rewrite
     the function so that it works.

(defun build-normalizing-functions (vars)
  "Given a list of variables, build functions to normalize the data to
   a 0 mean, 1 variance"
  (let (sigma mean func)
    ;; NB: var is a structure of type var
    (dolist (var vars)
       (setq sigma (estimate-std-dev var))
       (setq mean (estimate-mean var))
       (setq func (function (lambda (val) (/ (- val mean) sigma))))
       ;; Initialize new max and min
       (setf (var-min var) (funcall func (var-min var)))
       (setf (var-max var) (funcall func (var-max var)))
       ;; set up function for normalizing the data set
       (setf (var-norm-fn var) func)
       )))

;;I rigorously tested the function for a single variable and it 
;;worked fine.  But..


;; Have fun,

;; Bharat

From: R. Bharat Rao
Subject: Re: Amazing the dumb mistakes we make...
Date: 
Message-ID: <BsoDyK.5Kz@ux1.cso.uiuc.edu>
······@cs.uiuc.edu (R. Bharat Rao) writes:

My "solution" follows...

>Q 1) Does the code below work as intended.  If not, explain why.  Rewrite
>     the function so that it works.

>(defun build-normalizing-functions (vars)
>  "Given a list of variables, build functions to normalize the data to
>   a 0 mean, 1 variance"
>  (let (sigma mean func)
>    ;; NB: var is a structure of type var
>    (dolist (var vars)
>       (setq sigma (estimate-std-dev var))
>       (setq mean (estimate-mean var))
>       (setq func (function (lambda (val) (/ (- val mean) sigma))))
>       ;; Initialize new max and min
>       (setf (var-min var) (funcall func (var-min var)))
>       (setf (var-max var) (funcall func (var-max var)))
>       ;; set up function for normalizing the data set
>       (setf (var-norm-fn var) func)
>       )))

The closure captures the binding of sigma (and mean), not the value.
Therefore, all (var-norm-fn var) will use the mean and sigma for the
LAST variable through the loop.

>;;I rigorously tested the function for a single variable and it 
>;;worked fine.  But..

Obviously, since there was only a single var.

The error was somewhat insiduous in showing up, because the
calculations of (var-min var) and (var-max var) were correct, using
the current (and correct) values of sigma in each case.  It was while
using (var-norm-fn var) to normalizing the data set, that problem
recurred.

One fix is to simply put the let inside the dolist as in
  (dolist (var vars)
    (let ((sigma (estimate-sigma var))
          (mean (estimate-mean var))
          func)
      ...))

othen a fresh binding will be established every single time, and this
will work fine.


Another possibility is to force evaluation at run-time..

   (let (sigma mean func)
      (dolist (var vars)
         .....
         ;; Warning kludge alert!!
         (setq func `(function (lambda (val) (/ (- val ,mean) ,sigma))))
         ))

I do know Norvig's book (and other sources) suggest that using
backquote in closures is bad style.

The first one is definitely more elegant, while the second one seems
to capture what I am doing better (I am interested in the value not he
binding).  Both work equally well, of course....  the multiple
bindings seems to be the choice for now, but...

ANy suggestions welcome.

>;; Have fun,

Hope you did...

Bharat
From: Barry Margolin
Subject: Re: Amazing the dumb mistakes we make...
Date: 
Message-ID: <1614dbINN7hf@early-bird.think.com>
In article <··········@ux1.cso.uiuc.edu> ······@cs.uiuc.edu writes:
>Another possibility is to force evaluation at run-time..
>
>   (let (sigma mean func)
>      (dolist (var vars)
>         .....
>         ;; Warning kludge alert!!
>         (setq func `(function (lambda (val) (/ (- val ,mean) ,sigma))))
>         ))

No offense intended, but yucko!

Remember that you'll have to use EVAL later to turn that list into a
funcallable function.  In CLtL1 you could just leave out the FUNCTION
wrapper and call the lambda expression as a function, but ANSI CL removes
the automatic coercion of lambda expressions to functions (but provides
(coerce '(lambda ...) 'function)).
-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: Christopher Owens
Subject: Re: Amazing the dumb mistakes we make...
Date: 
Message-ID: <1992Aug11.162422.3327@midway.uchicago.edu>
In article <··········@ux1.cso.uiuc.edu> ······@cs.uiuc.edu writes:
>
>;; A sadistic examiner for LISP programming course may say
>
>Q 1) Does the code below work as intended.  If not, explain why.  Rewrite
>     the function so that it works.

To which a student in one of my courses would answer, I hope:

  "I don't know. Last time anyone in class tried to use SETQ (or SETF
   of a symbol) you got all foamy around the mouth and started
   mumbling about "damnable side-effects," "alias bugs," and
   "referential transparency." We didn't really understand at the
   time; we all figured you were complaining about the side-effects of
   your antipsychotic medication, or something like that, and that
   "aliases" and "bugs" had something to do with that mysterious
   Government-sponsored research.  At first, we avoided using SETQ
   just because we were scared.  Since then, we have become
   enlightened, and we avoid using SETQ for sounder reasons."

   I'd probably rewrite the program as:

   (defun build-normalizing-functions (vars)
     "Takes a list of VARs; calculates a normalization function for
     each var, and updates the VAR-MIN, VAR-MAX, and VAR-NORM-FN 
     slots."
     (dolist (var vars)
       (let ((sigma (estimate-std-dev var))
             (mean (estimate-mean var)))
         (flet ((norm-fn (val)
                  (/ (- val mean) sigma)))
           (setf (var-min var) (norm-fn (var-min var)))
           (setf (var-max var) (norm-fn (var-max var)))
           (setf (var-norm-fn var) #'norm-fn)))))