2013-04-24 2 views
3

Я новичок в программировании LISP, и это конец семестра, и наш учитель попросил нас сделать этот проект, и я пытался это сделать, но я застрял поэтому любая помощь будет оценена по достоинству. Проект должен написать функцию eval (expr) в Lisp, чтобы перезаписать уже существующую функцию. Вот подробности:как переписать (defun eval (expr)) в LISP

Описание проекта: Элементы в арифметическом выражении, разделенные пробелами;

; 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 простых арифметических выражений, и он работает !! но я не могу заставить его работать над вложенными арифметическими операциями. Я думаю, что у меня есть проблема с рекурсивной части я делаю что-то неправильно, но что это точно ИДК :(

вот мой код:

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

, пожалуйста, не используйте вкладки, когда вы отправляете исходный код на SO. :) Всегда старайтесь правильно вставить код, и если можете, попробуйте подстроить свои строки до ширины панели кода, чтобы не было горизонтальной полосы прокрутки (это не требование, но гораздо проще читать код таким образом) , –

+0

Слишком много тегов. Это Элис? XLisp? Или Common Lisp? – Kaz

ответ

2

Прежде всего, основной вызов, который делает трюк является ... барабанная дробь ... -

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

Вот и все. Если вы рассматриваете это обман, вы можете вызвать операцию вручную, но вам не нужно копировать один и тот же блок кода четыре раза. :)

Если вы действительно хотите написать операционную систему вручную, обратите внимание, что значение по умолчанию для (*) равно 1, а не 0; и что нет значения по умолчанию для (-) и (/) в Common Lisp (как проверено в CLisp). Кроме того, (/ 2) должен вернуть 1/2.

+0

@ Will Ness Спасибо большое! Он работает и с меньшим количеством кода. Ты рок. Я ценю вашу помощь –

+0

@AriannaNewman, что вы очень приветствуете, и Добро пожаловать в чудеса Лиспа! (и Scheme, и Haskell, ... и если вы можете поместить его в свой график - Prolog). :) –

+0

@AriannaNewman также перезапишите функцию 'driver'. Common Lisp не имеет гарантии возврата хвоста IIRC, то, что вы написали, больше похоже на код схемы. Просто [используйте 'do'] (http://www.lispworks.com/documentation/HyperSpec/Body/m_do_do.htm), таким образом вы также можете разработать способ выхода из цикла. –

2

несколько советов:

  • Выражения, подобные (0), не имеют смысла. 0 не является функцией. Аналогичные ((foo)) также не имеют смысла.

  • you sho uld правильно форматирует и отступает код Lisp. Редактор помогает.

  • остерегайтесь функции, такие как CAR, CDR, CADR ... - использовать FIRST, REST, SECOND, ...

  • не называют функцию EVAL. Это встроенная функция в Common Lisp.

Смежные вопросы