From: Kent M Pitman
Subject: Re: Making a Calculator program in LISP
Date: 
Message-ID: <utzvgrjed.fsf@nhplace.com>
···········@gmail.com writes:

> Ok I am a newbie at LISP 

Welcome to the community.

> and I am doing a project for class and im
> trying to make a simple (very simple) calculator that just does + - /
> * functions.

It's a reasonable early program, though you're a bit off course in a number
of ways, in many ways making it harder on yourself than you need to.
That's common, so don't get discouraged.  And a lot of the things I've said
below say you did a lot of things wrong.  That's not to make you feel bad.
Lots of people have trouble with early programs.  But you need to see clearly
when you're doing things right and when not, and so I'll just try to be clear
in my text.  Hopefully you won't take that as harsh, since I certainly don't
mean it that way.  Just try to work with it as "useful data" and to learn 
from it as best you can.  Eventually you'll do fine.

> THis is what i have:
> 
> (print "ENTER 1 for ADDITION:")
> (print "ENTER 2 for SUBTRACTION:")
> (print "ENTER 3 for DIVISION:")
> (print "ENTER 4 for MULTIPLICATION")
> (print "ENTER 5 to EXIT")
> (read ANS)
> (DEFUN MENU (ANS)
>   (COND (1 (ADDITION (NUM1 NUM2)
>    (2 (SUBTRACTION (NUM1 NUM2)
>   (3 (DIVISION (NUM1 NUM2)
>     (4 (MULTIPLICATION (NUM1 NUM2)
>     (5 (print "DONE")))
> (DEFUN ADDITION (NUM1 NUM2) (+ NUM1 NUM2))
> (DEFUN SUBTRACTION (NUM1 NUM2) (- NUM1 NUM2))
> (DEFUN DIVISION (NUM1 NUM2) (/ NUM1 NUM2))
> (DEFUN MULTIPLICATION (NUM1 NUM2) (* NUM1 NUM2))

It's hard to understand why you'd be assigned this without being given
some guidance as to how to USE the operators at your command.  I wish
I could know what you've been given as initial discussion on how to start
or what operators you're to use or how you're to structure your program,
but I'll just make some guesses and offer what tips I can here...

First, your code above is inappropriately written like a script.  What
I mean by that is it that it appears to do the job of RUNNING the
calculator, not just defining it.  Lisp may seem like a scripting language
to some people, but it's not intended that you write your programs like
shell scripts.  I don't know if you intended to do that or realized you
were doing it, but I'll try to help you see what you've done so you can
fix it.

The fact that you call READ as a toplevel operation is "wrong" because
you don't want to read the one and only number as you load your
script.  Moreover, you discard the result of the READ, which is almost
never correct; that is, READ returns a value, but you have done
nothing with that value.  Additionally, you define the functiosn
you'll call after the call to READ.

The first thing to understand about Lisp, at least while starting out
(since all rules are made to be broken, but you should not break them until
you know what you're doing), is that you should always write your
definitions in a separate file than the file that calls them.  So you want
your file NOT to do:

(DEFUN HELPER-FUNCTION1 ...)
(DEFUN HELPER-FUNCTION2 ...)
(DEFUN RUN-CALCULATOR ...)
(RUN-CALCULATOR) ; <-- this call doesn't belong in this file

because you can't then load your calculator without calling it, and you
will find yourself reloading your calculator to call it.  Instead, you
want to put the (run-calculator) call in a separate file.

By the way, it's common to use read-line as a way of getting line activation
but it seems wholly unnecessary in this problem and complicates things.
Just do something simple that reads a token and decides how to change its
internal state based on what it reads.

(DEFUN RUN-CALCULATOR ()
  (LET ((CALCULATOR-STATE (MAKE-CALCULATOR-STATE)))
    (LOOP 
      (LET ((TOKEN-SEEN (READ)))
        (SETQ CALCULATOR-STATE 
              (MODIFY-CALCULATOR-STATE TOKEN-SEEN CALCULATOR-STATE))))))

If you need to print instructions at the start, put those statements in
the program, too, not at top-level in script style.  e.g.,

(DEFUN RUN-CALCULATOR ()
  (PRINT-INSTRUCTIONS)
  (LET ((CALCULATOR-STATE (MAKE-CALCULATOR-STATE)))
    (LOOP 
      (LET ((TOKEN-SEEN (READ)))
        (SETQ CALCULATOR-STATE 
              (MODIFY-CALCULATOR-STATE TOKEN-SEEN CALCULATOR-STATE))))))

(DEFUN PRINT-INSTRUCTIONS ()
  (PRINT ...))

(You might later prefer FORMAT, btw, instead of PRINT.  PRINT is pretty clumsy.
But for now, just use PRINT as you were doing since it's conceptually simpler.)

Having structured the program as I've shown, you can then focus on creating
some kind of state object with some function MAKE-CALCULATOR-STATE that takes
no arguments and returns some kind of state object.  Then make a function of
two arguments called MODIFY-CALCULATOR-STATE that takes a token and a state
and produces a new state.  That should give your project a bit of definition
as to what your goals are.  Perhaps at that point it will seem more familiar
if you've programmed in other languages.

The function READ will have done the work of constructing tokens for you,
like making numbers out of digits or symbols out of non-digits.  So if you
were to type in 3 + 3 = you'd see four tokens (the number 3, the symbol +,
the number 3, and the symbol =).  Or you could make an HP-style calculator
that took 3 3 + instead.  The state object could be a list representing a
stack, or a structure of some kind with named slots, or a property list, or
some other thing of your choosing.

Incidentally, you've made some syntax errors in your code, too.
I don't recommend structuring your code as you have, but I'll comment on
what you've written anyway since it has a chance to help you and I probably
won't have time later in the week when I'm more busy to view revised code:

>   (COND (1 (ADDITION (NUM1 NUM2)

Regarding this:

   COND actually takes clauses that are lists of test expressions to
   evaluate and operations to do.  The test expression you've given here is
   the number 1, which will self-evaluate, yielding the number 1, which is
   always a true value (because it is not NIL).  That clause with the 1
   will therefore always be executed, and no other clause considered.
   I'm pretty sure you don't want that.

   Perhaps you're thinking you have a select or case kind of
   construct.  That would look like:

   (CASE ANS
     (1 ...)
     (2 ...))

   but note it's a different operator than COND and it also has a first
   argument that tells you what you're casing on.  I recommend not using
   this construct for now, but you may prefer to.

   Given how you wrote your code, you might have meant some test like
   (= ANS 1) as in:

     (COND ((= ANS 1) ...something to do...)
           ((= ANS 2) ...something else to do...)
           (T ... what to do otherwise ...)
           ) ;this matches the COND

   Though normally in Lisp we don't put the last paren on a line of its
   own, we write it more compactly.

     (COND ((= ANS 1) ...something to do...)
           ((= ANS 2) ...something else to do...)
           (T ... what to do otherwise ...))  ;<-- the extra paren hides here

   Note, by the way, that in your original code you also didn't write the
   right number of parens for the clause with the 1 in it.  Notice how my 
   revised clause has for each clause a balance of parens for each clause:

     +-------- whole clause ----------+
     |                                |
     | +-test--+ +---consequent-----+ |
     | |       | |                  | |
     v v       v v                  v v
     ( (= ANS 1) (ADDITION NUM1 NUM2) )

   And note that functions are not like in other languages, so don't do

   (ADDITION (NUM1 NUM2)

   thinking that you are giving addition two arguments.  Parens should ONLY
   be inserted where they are called for, either as syntax of a special form
   because the special form requires it, as in:

   +---------definition bounds-----------+
   |                                     |
   |                +args-+              |
   |                |     |              |
   v                v     v              v
   (DEFUN SOMETHING (X Y Z)  ... body ...)

   and because of a function call:

   (F 3 4 5)

   But any arguments inside of a function call should be used only if there
   is another function call, as in:

   (F (G 3) 4 5)

   which is like F(G(3),4,5) in an algebraic language.  DON'T assume you
   can do like in algebraic languages where you do F(G(3),(4),(5))
   and think you're just clarifying grouping.  Doing 

   (F (G 3) (4) (5)) ;WRONG

   in Lisp is like telling Lisp to do F(G(3),4(), 5()) in an algebraic
   language, that is, it's telling Lisp to call a function named 4 and a
   function named 5.  So when you've written:

   (ADDITION (NUM1 NUM2) ;WRONG

   you've failed to close the addition parens, and may have meant

   (ADDITION (NUM1 NUM2)) ;STILL WRONG

   but you still have the problem that you've called some function NUM1
   on a single argument NUM2, and then asked to have the result of that
   call be passed as the single argument to ADDITION.

   It is as if in an algebraic language you had written ADDITION(NUM1(NUM2))
   which is not what you probably meant.

My final comment is that you should NOT let someone trick you into writing
something that reads a number in order to represent a button press of 
something like + or -.  There's little point to learning how to write a
program in Lisp if all you're going to do is write FORTRAN code, pretending
there are no datatypes but numbers.  The whole point of Lisp is that you can
talk about symbols easily.  So since READ will return the symbol + if you 
type a plus sign, just expect that and don't make the person type something
other than a plus.  For now, use the function EQUAL to compare type-in 
instead of =, and then you can do:

   (COND ((EQUAL TOKEN '=) ... stuff to do if = was typed ...)
         ((EQUAL TOKEN '+) ... etc.)
         ...etc.
         ((NUMBERP TOKEN) ; test if the token is a number
          ... stuff to do with the numeric token ...)
         (T ... handle token else we don't allow ... ))

You can even use long names for buttons the user might press:

  (COND ... ((EQUAL TOKEN 'SQRT) ...) ...)

and then have the user type:

  4 + 4 SQRT = 

This is much better than telling the user:

 Type 1 for +, 2 for -, ... 437 for absolute value, ... 519 for square root ...

No one will ever remember all those numbers.  Just tell them the names
to type

 Type a number or type an operator name (+, -, /, *, SQRT, ABS, etc.)

and then implement whatever names you like.

Also, if there's a function you want to use in Lisp, just use it.
Don't make a function that just indirects to the same thing.
Don't do this:

 (COND ((EQUAL TOKEN 'MOD) (MODULUS X Y)) ...)

and then later needlessly do:

 (DEFUN MODULUS (X Y) (MOD X Y)) ;needless

Instead just directly do:

 (COND ((EQUAL TOKEN 'MOD) (MOD X Y)) ...)

if that's what you want to call.  Calling a function that will just call
another without changing the args is usually just extra stuff for nothing.
Your functions ADDITION, SUBTRACTION, etc. aren't really needed.
There is a simpler thing you could do.

Anyway, I've tried not to write your program for you, and in fact 
my comments probably won't resolve all of your issues but I hope
it will get you disabused of at least a few of your confusions.

Good luck!