3

I am new to LISP programming and it is the end of semester and our teacher asked us to do this project and I have been trying to make it but I am stuck so any help would be appreciated. the Project is to write an eval (expr) function in Lisp to overwrite the already existing function. here is the details:

Project Description: Items in an arithmetic expression separated by spaces;

; Input:
;    1. The form of arithmetic expression given in prefix notation like LISP 
; Assumptions:
;    1. binary operations for +, -, *, and / 
;    2. integer division, no reals
;    3. an arithmetic expression occupies only one line
;    4. nested arithmetic expressions permitted
;    5. all given inputs are syntax correct
;    6. no need for error handling  

I wrote a code that can do eval of the simple arithmetic expressions and it works!! but I COULD NOT get it to work on nested arithmetic operations. I think I have an issue with the recursion part i am doing something wrong but what is it exactly idk :(

here is my code :

; Assign a character string to a global variable input-prompt
; treat input-prompt as a constant global variable
(setf input-prompt "Please input an arithmetic expression: ")

(setf output-prompt "The value is: ")

(defun prompt-for-input (msg)
  (format t msg)
  (format t "~%"))   ; ~% new line

(defun prompt-for-output (msg)
  (format t msg))

(defun output-msg (result)
  (format t "~S" result) ; ~S takes the result into the print message
  (format t "~%"))

(defun eval (expr)
  (print "My EVAL Function is Working *_*")
  (if (numberp expr) expr)     
  (cond
    ((eq (car expr) '+)
      (if (and (numberp (cadr  expr)) 
               (numberp (caddr expr))) ;to check if we have simple expression
        (+ (cadr expr) (caddr expr)) ;in case both are numbers we add normally
        (if (not (numberp (cadr expr))) 
           ;in case the second argument is not number 
           ;we need to call eval again to check the expression 
          ((eval (cadr exp)))
          (if (not (numberp (caddr expr))) 
             ;in case the third argument is not a number 
             ;we need to call eval again to check the expression
            ((eval (caddr exp)))
            (0)))))
    ((eq (car expr) '-)
      (if (and (numberp (cadr  expr)) 
               (numberp (caddr expr))) ;to check if we have simple expression
        (- (cadr expr) (caddr expr)) ;in case both are numbers we add normally
        (if (not (numberp (cadr expr))) 
           ;in case the second argument is not number 
           ;we need to call eval again to check the expression 
          ((eval (cadr exp)))
          (if (not (numberp (caddr expr))) 
             ;in case the third argument is not a number 
             ;we need to call eval again to check the expression
            ((eval (caddr exp)))
            (0)))))
    ((eq (car expr) '*)
      (if (and (numberp (cadr  expr)) 
               (numberp (caddr expr))) ;to check if we have simple expression
        (* (cadr expr) (caddr expr)) ;in case both are numbers we add normally
        (if (not (numberp (cadr expr))) 
           ;in case the second argument is not number 
           ;we need to call eval again to check the expression 
          ((eval (cadr exp)))
          (if (not (numberp (caddr expr))) 
             ;in case the third argument is not a number 
             ;we need to call eval again to check the expression
            ((eval (caddr exp)))
            (0)))))
    ((eq (car expr) '/)
      (if (and (numberp (cadr  expr)) 
               (numberp (caddr expr))) ;to check if we have simple expression
        (/ (cadr expr) (caddr expr)) ;in case both are numbers we add normally
        (if (not (numberp (cadr expr))) 
           ;in case the second argument is not number 
           ;we need to call eval again to check the expression 
          ((eval (cadr exp)))
          (if (not (numberp (caddr expr))) 
             ;in case the third argument is not a number 
             ;we need to call eval again to check the expression
            ((eval (caddr exp)))
            (0)))))))

    ; it should have eval(expr) function which returns the value of the
    ; arithmetic expression
    ; for instance, 
    ; (+ 2 3) outputs 5
    ; (+ (* 3 2) (/ 4 2))) outputs 8
    ; (* (- 2 3) 5) outputs -5 

    ; driver accepts the input arithmetic expression
    ; evaluate the arithmetic expression
    ; output the reulst of the evaluation of the arithmetic expression
    ; execution is in the loop; to exit the loop; type cntrl-c

(defun driver ()
  (prompt-for-input input-prompt) 
     ;to print "Please input an arithmetic expression
  (let ((expression (read)))      
    (output-msg expression)
    (let ((result (eval expression)))
      (prompt-for-output output-prompt)  
      (output-msg result)))
  (driver))
Will Ness
  • 70,110
  • 9
  • 98
  • 181
Arianna Newman
  • 75
  • 1
  • 11
  • please don't use tabs when you post source code on SO. :) Always try to indent the code properly, and if you can, try to fit your lines to the code pane's width so that there's no horizontal scroll-bar (that's not a requirement, but it's much easier to read the code that way). – Will Ness Apr 24 '13 at 19:56
  • Too many tags. Is this Elisp? XLisp? Or Common Lisp? – Kaz Apr 28 '13 at 04:47

2 Answers2

3

First of all, the basic call that does the trick is ... drum roll ... -

(apply (symbol-function (car expr)) (cdr expr))

this assuming - for a moment - that all the arguments in the expression are already numbers.

This one line replaces all four cases in your code which are all exact copies of one another, up to the operation to be performed.

Now, to make sure we have numbers, we just need to call same eval on each of them. If they were numbers, they will stay as is, and if not - they'll get evaluated.

Let's just call our new function calc, for "calculate", instead:

(defun calc (expr)     ; our "eval"
  (cond
    ((numberp expr) expr)
    (T (call (car expr)
             (mapcar #'calc (cdr expr))))))

(defun call (op args)  ; our "apply"
  (apply (symbol-function op)
         args))

That's all. If you consider this cheating, you can call the operation by hand, but still you don't need to copy the same block of code four times for that. :)

If you indeed write the call yourself to call the operation by hand, do note that the default value for (*) is 1, not 0; and that there's no default value for (-) and (/) in Common Lisp (as tested in CLisp). Also, (/ 2) should return 1/2.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • @ Will Ness Thank you so much man! It works and with less code. You are rock. I appreciate your help – Arianna Newman Apr 24 '13 at 19:49
  • @AriannaNewman you're very welcome, and Welcome to the wonders of Lisp! (and Scheme, and Haskell, ... and if you can fit it in your schedule - Prolog). :) – Will Ness Apr 24 '13 at 19:51
  • @AriannaNewman also, re-write your `driver` function. Common Lisp has no tail-recursion guarantee IIRC, what you wrote looks more like a Scheme code. Just [use `do`](http://www.lispworks.com/documentation/HyperSpec/Body/m_do_do.htm), that way you can devise a way to exit from the loop cleanly, too. – Will Ness Apr 24 '13 at 19:54
  • I really really appreciate all your comments and your help. I just joined this website today and was not that familiar with it :) – Arianna Newman Apr 24 '13 at 20:05
2

a few hints:

  • Expressions like (0) are not meaningful. 0 is not a function. Similar ((foo)) also makes no sense.

  • you should properly format and indent Lisp code. The editor helps.

  • Avoid functions like CAR, CDR, CADR, ... - use FIRST, REST, SECOND, ...

  • don't call the function EVAL. That's a built-in function in Common Lisp.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346