Я решаю упражнение 4.3 SICP. Речь идет о написании ориентированного на данные оценщика (процедура eval) вместо представленного в раздел 4.1.1 книги. Основная идея состоит в том, чтобы получить процедуры, используемые eval, из таблицы в соответствии с каждым типом выражения вместо использования условий. Начало процедуры eval, представленное в книге, реализовано следующим образом:

(define (eval exp env)
  (cond ((self-evaluating? exp) exp)
        ((variable? exp) (lookup-variable-value exp env))
        ((quoted? exp) (text-of-quotation exp))
        ((assignment? exp) (eval-assignment exp env))
        [...]

Версия, ориентированная на данные, может быть:

(define (eval exp env)
  (cond ((self-evaluating? exp) exp)
        ((variable? exp) (lookup-variable-value exp env))
        ((get 'op (car exp)) exp env)
        [...]

Где все дела, кроме self-evaluating? и variable?, будут обрабатываться напрямую get. Эта процедура связана с таблицей с необходимыми процедурами (обозначенными 'op) и возвращает процедуру, связанную с типом выражения (car exp).

Проблема, с которой я столкнулся, заключалась в том, что некоторые процедуры имеют только один параметр ((text-of-quotation exp)), в то время как другие используют два ((eval-assignment exp env)), поэтому, как только (get 'op (car exp)) вернет соответствующую процедуру, я не могу получить правильное количество параметров (я уже использую два параметра с ((get 'op (car exp)) exp env).

Одно решение, которое я нашел на вики-странице схемы (здесь), выполняет следующие действия для обработки получения процедура и параметры:

((get 'op (car expr)) (get 'op (car expr) expr env)) 

Однако я совершенно не понимаю, как часть (get 'op (car expr) expr env) получает правильные параметры для применения с (get 'op (car expr)).

Я был бы очень признателен, если бы кто-нибудь мог объяснить мне, как эта схема вики-решения обрабатывает параметры в этой конкретной строке кода, поскольку она очень элегантно обрабатывает проблему, с которой мне трудно работать.

2
fabrizzio_gz 16 Июл 2020 в 19:32

1 ответ

Лучший ответ

На мой взгляд, некоторые ответы на вики-странице схемы имеют потенциальные проблемы. Я думаю, что решение, которое лучше всего отвечает на ваш вопрос, - это решение от Sphinxsky:

 (define (eval- exp- env) 
     (cond ((self-evaluating? exp-) exp-) 
         ((variable? exp-) (lookup-variable-value exp- env)) 
         (else 
             (let ((op (get 'eval (car exp-)))) 
                 (if op 
                     (op exp- env) 
                     (error "Unknown expression type -- EVAL" exp-)))))) 

Сначала выполняемая нами «операция» - это оценка, поэтому 'eval используется для обозначения этого (поскольку таблица также может содержать арифметические процедуры).

(get 'eval (car exp-))

Где (car exp-) укажет тип выражения, которое необходимо вычислить (например, quote, define, begin, ...)

Код проверяет, что объект (процедура) был возвращен, и вызывает его с (op exp- env), как вы говорите, передавая два аргумента exp- и env:

             ...
             (let ((op (get 'eval (car exp-)))) 
                 (if op 
                     (op exp- env) 
                     (error "Unknown expression type -- EVAL" exp-)))))) 

Почти все специализированные оценщики принимают два аргумента, и цитаты могут быть единственным исключением. Но с этим можно справиться, если цитата «оценщик» принимает два аргумента и просто игнорирует второй, неиспользуемый аргумент. Sphinxsky сделал это здесь, установив лямбду, которая принимает два аргумента, которые затем вызывают текст цитаты только с одним из них:

     (put 'eval 'quote 
         (lambda (exp- env) 
             (text-of-quotation exp-)))    

Что касается ответа BE, я думаю, что в нем отсутствует пара круглых скобок.

(cond
  ...
  ((get 'op (car expr)) (get 'op (car expr) expr env))
  ...

Должно быть:

(cond
  ...                    v                  v          
  ((get 'op (car expr)) ((get 'op (car expr)) expr env))
  ...

Если первый вызов ((get 'op (car expr)) возвращает значение, тогда мы оцениваем ((get 'op (car expr)) expr env), это приводит к повторению поиска, но мы знаем, что он вернет значение, поэтому:

((get 'op (car expr)) expr env)

Становится:

(<the-evalutor> expr env)

Двойной поиск не идеален, но упражнение 4.5 покажет, как этого можно избежать.

Возможно, вам придется проявить немного осторожности при чтении ответов на главы 4 и 5. Для большинства предыдущих глав люди будут публиковать код, который они запустили и поэтому были в некоторой степени протестированы (синтаксис определенно правильный). Но вполне разумно, что не все будут тратить время, необходимое для написания полноценного оценщика, чтобы запускать и тестировать свой код, поэтому некоторые ответы - это код «карандашом и бумагой». Fwiw, Мое собственное решение находится на github . Я, наверное, сейчас сделал бы это по-другому, но он работает.

1
codybartfast 17 Июл 2020 в 07:47