From: Kent M Pitman
Subject: Re: small prog big probs
Date: 
Message-ID: <sfwlnjg2zad.fsf@world.std.com>
"David Nixon" <·····@nixon25.freeserve.co.uk> writes:

> 
> (defun count-odd (s)
> 
> (cond ((null s) 0)
>       ((atom s) 1)
>       (t (apply '+ (mapcar 'count-odd s)))))
>                  (count-odd '(5 56 7 8))

Well, for one thing you could use a better indenter.
Maybe the following translation of what you've written will help:

(defun count-odd (s)            ;<-- defines a function COUNT-ODD of 1 arg, S
  (cond ((null s) 0)            ;<-- if S is the symbol NIL, it counts as 0
        ((atom s) 1)            ;<-- if S is any other atom (like MY-SYMBOL,
                                ;    or "Some String" or 14 or 37), it counts
                                ;    as 1.
        (t                      ;<-- otherwise
          (apply '+             ;<-- add up the list produced by
                 (mapcar 'count ;<-- calling COUNT 
                                ;    (a function you don't define here;
                                ;     maybe you meant COUNT-ODD)
                          s)    ;    on each of the elements of the non-atom S
                 ))))

> I am trying to count the odd numbers but cannot find what I am doing wrong
> Can anyone please help.

First, as you can see in the atom case, you are counting both odds and evens
as 1.  Second, as you can see in the MAPCAR case, you are not even recursively
using this function; since your first call is to a non-atom, you are executing
the MAPCAR on the result of a function we have not seen.  But if you were
actually using the function here, you'd probably end up counting all the numbers
rather than only the odd ones, since you count both odds and evens as 1.

Hint:  If you want to count only odds, probably a call to oddp or evenp
       (or something computationally equivalent, like mod) will occur in
       your program.


> Also :-
> (defun wall (n)
>   (cond ((= n 0) nil  (oddp n))
> 
>          (t (cons 'brick (wall (- n 1)))))))
> 
>                (wall 7)
> 
> This program is supposed to construct a wall brick mortar brick mortar etc,
> but it must always start with brick. What am I doing wrong
> Any help is greatly appreciated.
> Dave

Well, first, the clause that contains 
 ((= n 0) nil (oddp n))
is ill-formed.  A cond clause contains a test and then
a series of forms, the last of which contributes a value,
as in:
 (test form1 form2 form3... formN)
Since form1...formN-1 do not contribute a value, it is
not very useful for them not to do a side-effect, since
their values are discarded.  As such, if you think the
nil is getting used anywhere, it is not.  Further, the oddp
gets returned as a value.  If it were getting used, it would
yield a T or a NIL.  Since n = 0 in the case this fires, it will
always return NIL and this may be what is confusing you into
thinking the NIL that precedes the ODDP test is doing something.
Finally, ther is no MORTAR in your example, so I'm not sure
what you mean by the idea that WALL should return something
containing both BRICK and MORTAR.  Lisp is not especially
knowledgeable about this particular kind of construction materials.
It does know a lot about cons cells and hash tables and various
other programming construction materials, but that is a different
form of construction altogether.

As a hint, a special case is usually required  to manage
the case where a fencepost is involved.  For example, to
write "A, B, C" on the console, given (A B C) one might write:

 (defun write-list (elements)
   (write-rest-of-list nil elements))

 (defun write-rest-of-list (comma-first? elements)
   (cond ((null elements) nil) ;i.e., do nothing - we're done
         (t
           (if comma-first? (write-string ", "))
           (write (first elements))
           (write-rest-of-list t (rest elements)))))

The structure of your program will e different, but the fact
that somewhere in the program you will need to address the
issue of the first brick not needing mortar above it OR
the last brick not needing mortar below it will have to
occur, just as here the fact that the first element didn't
need a comma has to be expressed.

Incidentally, I would never in a production program write this
as a recursive function, but I've tried to accomodate what
I expect is your book's or teacher's presentation style.
Try to find out about DO or LOOP sometime.

For example:

 (defun write-list (elements)
   (do ((comma? nil t)
        (elements elements (cdr elements)))
       ((null elements))
     (when comma? (write-string ", "))
     (write element)))

or

 (defun write-list (elements)
   (loop for comma? first nil then t
         for element in elements
         when comma?
           do (write-string ", ")
         do (write element)))

according to personal taste.

There is nothing wrong with recursion conceptually, and
it's good to know.  But in practice, Lisp does not do
tail call elimination and your programs will risk blowing
out for lack of stack space if you write them using a 
recursive style and then use very long lists.  In Scheme,
you do get reliable tail call elimination (at some potential
risk of loss of debugging data in case of a problem), so
this causes some confusion.