Оценка выражений cons с использованием eval и map (интерпретатор Racket, написанный на Racket)

Мне было поручено написать интерпретатор Racket с использованием самого языка Racket для одного из моих классов в серии из 5 лабораторных работ, и первая касается написания двух основных функций: lookup и evaluate. Функция lookup получает список, который служит нашей средой, содержащий таблицу символов и один символ. Как вы наверняка заметили, для целей этого первого задания таблица очень проста. Я уже написал и протестировал эту функцию, и все в порядке.

Однако функция evaluate ставит меня в тупик. Он действительно правильно оценивает примитивные процедуры, которые я ему дал: например, '(+ 3 4 (- 5 2) 3) действительно оценивается как 13. Это также работает для выражения cons в единственном числе, например '(cons 1 null) = '(1) или '(cons 2 4) = '(2 . 5). Но он выдает ошибку всякий раз, когда я пытаюсь вычислить составной оператор из нескольких cons выражений. Один конкретный случай, который я рассматривал, это '(cons 1 (cons 2 null)). После долгой отладки я обнаружил, что проблема заключается в следующем:

  1. Map оценивает итоговое выражение '(2 null) правильно как '(2). Но потом...
  2. Карта, ожидающая завершения вышеупомянутой процедуры карты, каким-то образом заканчивается оценкой '(1 (2)). Я ожидал, что он будет равен '(1 '(2)), что сработало бы, но это не так, и я не совсем понимаю, почему. Но в итоге в результате этого...
  3. Процедура cons завершается со списком, возвращенным Map, что дает '(cons 1 (2)).
  4. Программа пытается оценить эту строку, но вместо этого кричит на меня, потому что она не распознает (2) как список, вместо этого ожидая, что это процедура.
#lang racket
(provide lookup)
(provide evaluate)

(define-namespace-anchor mySpace)
(define ns (namespace-anchor->namespace mySpace))

;The list which serves as the environment for our lookup function.
(define testEnvironment(list
                        (cons 'x 10)
                        (cons '+ +)
                        (cons '- -)
                        (cons '* *)
                        (cons 'y 20)
                        (cons 'cons cons)
                        (cons 'nil '())))
;Assumes that there is at least one element in the environment given for consideration.
(define lookup
  (lambda (symbol environment)
    (if(symbol? symbol)
       (let recursiveLookup((symbol symbol) (environment environment))
         (if(equal? symbol (car (car environment)))
            (cdr (car environment))
            (if(null? (cdr environment))
               (error "This symbol does not exist in the provided environment!")
               (recursiveLookup symbol (cdr environment)))))                 
       (error "The provided argument is not a symbol."))))

(define evaluate
  (lambda (expression environment)
    (if(not (pair? expression))
       (cond         
         ;If the expression is a number, then return that number.
         ((number? expression) expression)
         ;If the expression is null, or contains a symbol equivalent to null, return null
         ((null? expression) null)
         ((equal? null (lookup expression environment)) 'null)
         ;Otherwise, the expression must be some kind of symbol, so look up 
         ; the value of that symbol.
         (else (lookup expression environment)))
       ;If the expression is a list, then use map to evaluate the remaining elements
       ;in the expression. Then pair the first element (which is a procedure)
       ;to the resulting list. Finally, apply the procedure to the resulting function
       ;using evaluate to obtain the final value.
       (eval (cons (lookup (car expression) environment) 
                   (map (lambda (listElement) 
                          (evaluate listElement environment)) 
                        (cdr expression)))
             ns))))

Для меня очень важно лучше понять, что здесь происходит и как это исправить, потому что, если я не смогу справиться с этим, я не могу представить, что оставшаяся часть написания этого интерпретатора Racket будет легкой с шатким фундаментом. Я потратил около 15 часов, пытаясь понять это разными способами, но я уперся в стену... Боюсь, я слишком много думаю об этой проблеме, но даже в этом случае это очень расстраивает, потому что процедуры, похоже, работают безупречно. в любом случае это не включает несколько выражений cons. Это сделало меня очень грустным и невежественным чувством панды.

Я надеюсь, что я достаточно хорошо сформулировал свой вопрос для всех вас, и я выражаю свою вечную благодарность всем, кто найдет время, чтобы помочь мне здесь! Спасибо всем за то, что вы есть, потому что я слишком робок, чтобы обращаться за помощью к кому-либо в реальном мире :3


person pragmaticTheo    schedule 08.05.2018    source источник


Ответы (1)


Вы не должны использовать eval. Используйте apply:

(let ((exprs (map (lambda (listElement) (evaluate listElement environment)) expression))
  (apply (car exprs) (cdr exprs)))

eval оценивает все аргументы. Для чисел они оценивают себя, но с минусами пытаются оценить список с числом, и это недействительный код схемы. Ваши аргументы уже оценены, поэтому apply делает то же самое, что и eval, за исключением того, что использует переданные аргументы как есть. Позже вам, вероятно, понадобится сделать свою собственную версию для поддержки определяемых пользователем процедур.

Я думаю, что хорошо оценивать операнд, а не считать его символом. Например. это должно работать:

((car (cons + '())) 4 5)
; ==> 9
person Sylwester    schedule 08.05.2018