Re: New member

Mabry Tyson <[email protected]> Fri, 15 Apr 2005 20:13:51 -0700
Newsgroups gmane.lisp.common-lisp
Message-ID <[email protected]>
FYI, the [email protected] list has been quite quiet over the past 
several (or many) years.   It was used much more in the days leading up 
to the adoption of ANSI Common Lisp standard.

Since you're new to Lisp, I'll mention a few things regarding the code 
you wrote.

Obviously in any language one can write a program whose running time is 
inefficient.  Your program is one of that class.  But that's not bad in 
and of itself.    I've always felt we want to be efficient in our use of 
people (both code writers and the users of the program), not computers.  
A single-use program should be judged by how quickly it can be used, 
from the start of writing to the end of its use.  In that sense, your 
program is efficient.   But I wouldn't want to compute the primality of 
any large Mersenne primes with it!

Given that, I'll comment about the code with regards to the code being 
used more than once.   There are those that have written text books that 
have given more thought to this than me.  I'm just a long-time user.

An issue with programming is the clarity of the program.  Having 
meaningful names helps, but one needs comments!    Chunking the code 
into meaningful functions is always important.  For instance you could 
have considered creating a function  (defun DIVISIBLE-BY-P (product 
factor) (zerop (mod product factor))).   Having clean code (ie, 
avoiding  "(not ())" ) is a good idea while avoiding more complicated or 
convoluted code   (for instance, "(and foo ...)" is equivalent to "(if 
(not (null foo)) foo ...)"  but might confuse someone into thinking the 
"(and ... )" expression return T or NIL when in fact it returns 
something else.  But we all do those short cuts.

[A convention from early days of lisp is using "-P" (for predicate) as a 
suffix to a function name indicating that the function was a boolean 
Predicate function.  For instance, I would have named your function 
PRIME-P rather than PRIME.  I would use PRIME for some sort of action 
such as PRIME-PUMP.  That also brings up the issue of  <verb>-<noun> 
name formats versus <noun>-<method> name formats.  One might name a 
function as WRITE-CHECK, but if you're writing a number of such 
functions for checks, money orders, etc, you might use  CHECK-WRITE, 
CHECK-DEPOSIT, CREDIT-CARD-CHARGE, ....  Whatever you do, be 
consistent.  Did you notice that CHECK-DEPOSIT is ambiguous? ]

Anything one can do to let the next person (or even the same person!) 
who looks at the code to better understand it is a good thing.  
Consistent naming of functions by type; consistent ordering/naming of 
arguments or variables; use of comments to explain the code; use of 
declarations to indicate and test the arguments; use of error trapping; 
testing of the code; etc. etc.


ALWAYS test the input from the user -- in this case, the arg of PRIME.  
What should happen if the user asked for  (prime NIL)?  He will get some 
error that he won't understand without looking at the code.  What if he 
asked for (prime 3.0)?   Worse yet, what if he asked for (prime 3.1) or 
(prime -5)???   What about (prime 0) or (prime 1)?

With bignums, Lisp can handle arbitrarily big numbers, but that doesn't 
mean that the code can.  Depending upon the optimizations of the 
compiler, there will be some (actually an infinite number) large integer 
argument to PRIME that will cause the system to fail (when it recurses 
too deeply for the computer and Lisp you're running it on).   You may 
not have an easy way to know the actual value of that bound.  You could 
test for an arbittrary bound, or you could use CL's error trapping 
system to detect an error.

Declarations of the types of arguments and other variables can help the 
compiler produce faster code (perhaps at the expense of debugging, but 
you've already been warned about testing the input and trapping errors) 
as well as indicate to the next person what you intended the code to 
do.   Adding tests (eg, ASSERT) or other debugging statements, 
especially during development will help the next person understand where 
the code went wrong.

But, building "perfect" code takes effort and time that might be better 
spent doing other things.  For the code you wrote, it might be better 
to, say, investigate other ways to be more efficient (if your testing 
integers less than 10^9) or entirely different mechanisms (if testing 
integers greater than 10^100), or maybe swapping space for speed (if 
you're only testing integers less than 1000 but doing this often, it 
might be better to use table lookup).

A lot of us write Lisp code for efforts that are more than just 
one-time-use programs but less than productized used-by-the-average-joe 
programs.  Lisp is our language of choice for many reasons, some better 
than others.  As a researcher, I am always exploring ideas.  Lisp gives 
me the incremental capability to design the code as I better understand 
the issues and to investigate what is happening in the middle of 
execution.  It gives me the debugging that I need for less-than-perfect 
code.  Lisp gives me the power to deal withh the unknown.  Lisp is 
efficient enough for every problem I've had to tackle.