2016-01-27 2 views
8

Я читаю «Практический общий Лисп» Питера Сейбела. В Chapter 9, он ходит читателя через создание основы модульного тестирования, и он включает в себя следующий макрос, чтобы определить, является ли список, состоящий только из истинных выражений:Есть ли преимущество для этого макроса?

(defmacro combine-results (&body forms) 
    (let ((result (gensym))) 
    `(let ((,result t)) 
     ,@(loop for form in forms collect `(unless ,form (setf ,result nil))) 
     ,result))) 

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

(defun combine-results (&rest expressions) 
    (let ((result t)) 
    (loop for expression in expressions do (unless expression (setf result nil))) 
    result)) 

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

+2

Реальное преимущество макроса заключается в том, что он имеет доступ к исходным формам и, следовательно, может печатать форму, которая не может быть оценена до истинного значения. Печально, что пример книги на самом деле не показывает этого. – hans23

+1

@ hans23: в коде книги формы фактически являются макросами, которые печатают результат. –

ответ

7

Ваше наблюдение в основном правильное; и на самом деле ваша функция может быть просто:

(defun combine-results (&rest expressions) 
    (every #'identity expressions)) ;; i.e. all expressions are true? 

Поскольку макрос безоговорочно оценивает все свои аргументы слева направо, а урожайность T если все они истинны, это в основном только рядными оптимизации то, что может выполняться функцией. Функции могут быть запрошены для установки в (declaim 'inline ...). Более того, мы можем написать макрос компилятора для функции в любом случае с define-compiler-macro. С этим макросом мы можем произвести расширение, и имеют это как функцию, которую мы можем apply и в противном случае косвенно.

Другие способы вычисления результата внутри функции:

(not (position nil expressions)) 
(not (member nil expressions)) 

Пример выглядит как макро практики: создание GENSYM и генерации кода с loop. Кроме того, макрос является отправной точкой для чего-то, что может появиться в модульной системе тестирования.

+1

Макрос может быть полезен (например, для оптимизации), если он закорочен на полпути путем оценки его аргументов, которые никогда не допускает функциональный подход. Но в этой форме это не так. –

+2

@JoaoTavora Если макрокоманда закорочена, это будет избыточная реализация стандартного макроса 'и'! – Kaz

+0

@Kaz, не совсем так, поскольку он вернет 't', если все аргументы не равны нулю. Но да, в принципе. Я думаю, что реальное использование в этом случае - это угол «verbosity» @ hans23: с помощью макроса вы можете печатать формы и делать все, что хотите, с помощью самих форм. –

9

В этом случае это может иметь значение, но для будущих версий было бы более полезно использовать макрос. Имеет ли смысл использовать макрос? В зависимости от случая использования:

Использование функции

(combine-results (foo) (bar) (baz)) 

Обратите внимание, что во время выполнения Lisp видит, что combine-results является функцией. Затем он оценивает аргументы. Затем он вызывает функцию combine-results с значениями результата. Это правило оценки жестко закодировано в Common Lisp.

Это означает: код функции запускается после аргументы были оценены.

Использование макро

(combine-results (foo) (bar) (baz)) 

Поскольку Лисп видит, что это макрос, он вызывает макрос в макро время расширения и генерирует код. Что сгенерированный код полностью зависит от макроса. Это позволяет нам генерировать код следующим образом:

Этот код будет выполнен.Например, вы можете настроить системные переменные, предоставить обработчики ошибок, настроить некоторые механизмы отчетов и т. Д. Каждая отдельная форма также может быть встроена в какую-либо другую форму. И после того, как функции запуска, можно сделать некоторые очистки, отчетности и т.д. prepare-an-environment и embed-it бы макросы или специальные операторы, которые убедитесь, что часть кода работает до того, вокруг и после встроенные формы мы предоставили.

Мы бы код, выполняемый до того, вокруг и после предоставленных форм. Это полезно? Может быть. Это может быть полезно для более обширной тестовой структуры.

Если это звучит знакомо, вы увидите, что можно получить аналогичную структуру кода, используя методы CLOS (первичные, до, после, вокруг). Тесты будут выполняться в основном методе, а другой код будет работать как около, до и после методов.

Обратите внимание, что макрос может также печатать (см. Комментарий Hans23), проверить и/или изменить предоставленные формы.

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