如何在LISP中覆盖(defun eval(expr))函数

时间:2013-04-24 17:06:02

标签: lisp elisp common-lisp clisp xlispstat

我是LISP编程的新手,这是学期的结束,我们的老师要求我们做这个项目,我一直在努力做到这一点,但我被困住了所以任何帮助都会受到赞赏。该项目是在Lisp中编写一个eval (expr)函数来覆盖已经存在的函数。这是详细信息:

项目描述:用空格分隔的算术表达式中的项目;

; 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  

我编写了一个代码,可以对简单的算术表达式进行评估,并且可以正常工作!但我不能让它在嵌套算术运算上工作。我认为我有一个问题,递归部分我做错了什么,但它究竟是什么idk :(

这是我的代码:

; 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))

2 个答案:

答案 0 :(得分:2)

一些提示:

  • (0)之类的表达没有意义。 0不是函数。类似的((foo))也没有意义。

  • 你应该正确格式化和缩进Lisp代码。编辑帮助。

  • 避免使用CARCDRCADR,...等功能 - 使用FIRSTRESTSECOND, ...

  • 不要调用函数EVAL。这是Common Lisp中的内置函数。

答案 1 :(得分:2)

首先,这个技巧的基本召唤是......鼓声...... -

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

这假设 - 暂时 - 表达式中的所有参数都已经是数字。

这一行代替了代码中的所有四个案例,这些案例都是彼此完全相同的副本,直到要执行的操作。

现在,为了确保我们有数字,我们只需要在每个数字上调用相同的eval。如果它们是数字,它们将保持原样,如果不是 - 它们将被评估。

我们只需拨打我们的新功能calc,即可#34;计算",而不是:

(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))

这就是全部。如果你考虑这种作弊行为,你可以手动调用该操作,但是你仍然不需要复制相同的代码块四次。 :)

如果您确实亲自编写call来手动调用操作,请注意(*)的默认值为1,而不是0;并且Common Lisp中的(-)(/)没有默认值(在CLisp中测试过)。此外,(/ 2)应返回1/2