Вопрос о SICP chpt 4.1: Как (анализировать expr) помогает ускорить eval? - PullRequest
8 голосов
/ 14 октября 2010

Я читаю следующий раздел SICP

http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-26.html#%_sec_4.1.7

Согласно тексту, следующее преобразование eval улучшит, предлагает улучшение производительности, так как выражение, которое оценивается много раз, будет проанализировано только один раз?

(define (eval exp env)
    ((analyze exp) env))

Вот функция analyze, приведенная в книге:

(define (analyze-if exp)
(let ((pproc (analyze (if-predicate exp)))
    (cproc (analyze (if-consequent exp)))
        (aproc (analyze (if-alternative exp))))
    (lambda (env)
        (if (true? (pproc env))
            (cproc env)
                (aproc env)))))

Я не понимаю, почему в книге говорится, что analyze будет работать только один раз. Разве тело eval, которое является ((analyze exp) env)), в основном не говорит, что каждый раз, когда вызывается eval, analyze будет вызываться с exp в качестве его параметра? Это будет означать, что analyze будет вызываться каждый раз, когда вызывается eval.

Что не так с моим пониманием? Буду признателен за любые отзывы, спасибо!

Ответы [ 3 ]

5 голосов
/ 15 октября 2010

Гинтаутас ответил правильно, но, возможно, пример в порядке.Предположим, вы разработали диалект Scheme, в котором используется петлевая конструкция

(do-n-times n expr)

с очевидной семантикой.Теперь, когда вы вызываете наивный eval для оценки цикла, который выполняется десять раз

(eval '(do-n-times 10 (print 'hello)))

, он анализирует тело цикла десять раз.С версией eval, которая отделяет анализ от оценки, тело цикла составляет analyze d один раз, затем оценивается десять раз.

Фаза анализа возвращает процедуру, которая может быть быстрой или нет в вашей схемепереводчик.Тем не менее, он мог бы выполнять любые виды оптимизации (анализ мертвого кода, JIT-компиляция для машинного кода и т. Д.).

5 голосов
/ 14 октября 2010

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

2 голосов
/ 06 ноября 2011

ответы Ларсмана чрезвычайно хороши.

В качестве дополнительного ответа можно также просмотреть analyze(environ) как каррированную форму eval(expr, environ), где параметр expr был передан заранее. В SICP вы можете прочитать пример кода, например:

(define (analyze-assignment exp)
  (let ((var (assignment-variable exp))
        (vproc (analyze (assignment-value exp))))
    (lambda (env)
      (set-variable-value! var (vproc env) env)
      'ok)))

Когда вы видите let (([var] [preprocessed stuff])), то предварительная обработка сохраняется в замыкании до тех пор, пока она не понадобится позже, когда передается environ.

...