2015-10-06 5 views
1

Рассматривая Practical Common Lisp, мы рассмотрим простую автоматизированную платформу для тестирования модулей. Мы пытаемся, чтобы написать макрос, который будет использоваться в качестве таковых:Переписать петлю в качестве картографа

(check (= (+ 1 2) 3) (= (- 1 4) 9)) 

Это должно расширить к чему-то, используя предварительно определенную функцию report-result. Предлагаемая реализация:

(defmacro check (&body forms) 
    `(progn 
    ,@(loop for f in forms collect `(report-result ,f ',f)))) 

Однако это расширение кажется для меня довольно процедурным. Я хотел бы заменить loop с mapcar расширить на что-то вроде этого:

(mapcar #'(lambda (form) (report-result form 'form)) (list form-1 ... form-n)) 

Однако, я явно не хватает навыков макро-письма, чтобы сделать это. Может кто-нибудь придумать один такой макрос?

В случае, если это уместно, это определение report-result:

(defun report-result (result form) 
    (format t "~:[FAIL~;pass~] ... ~a~%" result form)) 
+1

Непонятно, что вы просите. Что вам нужно делать? Вы хотите заменить «LOOP» на «MAPCAR» в макросе или должен ли расширенный код содержать «MAPCAR»? –

+0

Ну, честно говоря, мне не пришло в голову, что обе формы возможны. Ответ @ amalloy дает возможное решение для первого. Сначала я пытался написать что-то, чтобы в расширенном коде появился «mapcar». Это также возможно? – Guilherme

ответ

3

Это действительно довольно просто: вы просто поместить выражение collect в тело вашего mapcar:

(defmacro check (&body forms) 
    `(progn 
    ,@(mapcar #'(lambda (form) 
        `(report-result ,form ',form)) 
       forms))) 

Вы надеваете «Мне действительно нужно знать что-либо о том, что происходит с« макро-у », чтобы сделать нужную вам замену, которая просто заменяет loop некоторым другим эквивалентным выражением : он будет работать так же хорошо в макроконтексте, как и снаружи.

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

(defmacro check (&body forms) 
    `(let ((results (list ,@(mapcar #'(lambda (form) 
             `(list ,form ',form)) 
            forms)))) 
    (mapcar #'(lambda (result) 
       (report-result (car result) (cadr result))) 
      results))) 

который расширяется как так

> (macroexpand-1 '(check (+ 1 2) (* 2 3))) 
(let ((results (list (list (+ 1 2) '(+ 1 2)) 
        (list (* 2 3) '(* 2 3))))) 
    (mapcar #'(lambda (result) (report-result (car result) (cadr result))) 
      results)) 

Который, как вы можете увидеть довольно неудобно: макрос уже имеет формы, как (+ 1 2) имеющиеся в его распоряжении, но для того, чтобы сохраните их во время выполнения для отображения карты лямбда, вы должны дважды ввести форму ввода. И вы должны составить весь список для отображения, а не просто создать список, который «закончен» для начала. Кроме того, это создает список в качестве вывода и требует одновременного ввода всех входов и выходов в память: исходный макрос с progn производил входы и выходы по одному и отбрасывал их по завершении.

+0

Очень просто! Я пытался написать что-то, что расширилось бы до 'mapcar', и у него были проблемы с списками экранов/unescaping. Теперь у вас не было бы такого решения, не так ли? :) – Guilherme

+1

Вы не можете этого сделать (по крайней мере, не полезно). Нет смысла расширять (mapcar f (list ...)) вместо просто (список (f a) (f b)). Поскольку список должен быть известным фиксированным размером во время компиляции, нет необходимости/причины отображать его во время выполнения вместо времени компиляции. Но, я думаю, я могу включить «неосуществимую» версию, чтобы продемонстрировать, что я имею в виду: я отредактирую ответ в ближайшее время. – amalloy

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