【问题标题】:how to overwrite (defun eval (expr)) function in LISP如何在 LISP 中覆盖 (defun eval (expr)) 函数
【发布时间】:2013-04-24 17:06:01
【问题描述】:

我是 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  

我编写了一个可以对简单算术表达式进行 eval 的代码,它可以工作!!但我不能让它在嵌套算术运算上工作。我想我的递归部分有问题,我做错了什么,但它到底是什么 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))

【问题讨论】:

  • 在 SO 上发布源代码时请不要使用制表符。 :) 始终尝试正确缩进代码,如果可以,请尝试使行适合代码窗格的宽度,以便没有水平滚动条(这不是必需的,但这样更容易阅读代码) .
  • 标签太多。这是埃利斯普吗? XLisp?还是 Common Lisp?

标签: lisp elisp common-lisp clisp xlispstat


【解决方案1】:

首先,能达到目的的基本调用是……打鼓……-

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

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

这一行替换了代码中的所有四种情况,它们都是彼此的精确副本,直到要执行的操作。

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

我们只需调用我们的新函数calc,来代替“计算”:

(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

【讨论】:

  • @Will Ness 非常感谢你!它可以工作并且代码更少。你是摇滚。感谢您的帮助
  • @AriannaNewman 不客气,欢迎来到 Lisp 的奇迹! (还有Scheme,和Haskell,......如果你能把它放在你的日程安排中——Prolog)。 :)
  • @AriannaNewman 也可以重写你的driver 函数。 Common Lisp 没有尾递归保证 IIRC,你写的看起来更像是一个 Scheme 代码。只需use do,这样您也可以设计一种干净地退出循环的方法。
  • 我真的非常感谢您的所有 cmets 和您的帮助。我今天刚加入这个网站,对它不是很熟悉:)
【解决方案2】:

一些提示:

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

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

  • 避免使用CARCDRCADR、...等函数 - 使用FIRSTRESTSECOND、...

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

【讨论】:

    猜你喜欢
    • 2019-10-18
    • 1970-01-01
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多