2016-01-27 16 views
8

Czytam Practical Common Lisp Peter Seibel. W Chapter 9, on idzie czytelnika poprzez stworzenie ram testów jednostkowych, a on obejmuje następujące makro w celu określenia, czy dana lista składa się tylko prawdziwych wyrażeń:Czy to makro ma zaletę?

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

nie jestem jasne, co zaletą korzystania makro to tutaj, choć - wydaje się, co następuje byłby jaśniejszy, a także bardziej efektywne dla wartości dynamiczna:

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

Czy zaletą makra tylko, że to jest bardziej wydajny w czasie wykonywania żadnych połączeń, które są rozszerzony podczas kompilacji? Czy to jest paradygmat? A może książka stara się usprawiedliwić praktykę różnych wzorów w makrach?

+2

Prawdziwa zaleta makra polega na tym, że ma on dostęp do oryginalnych formularzy, dzięki czemu można wydrukować formularz, którego nie udało się ocenić do prawdziwej wartości. To smutne, że przykład książki faktycznie tego nie pokazuje. – hans23

+1

@ hans23: w kodzie książki formularze faktycznie są makrami, które drukują wynik. –

Odpowiedz

7

Twoja obserwacja jest w zasadzie słuszna; i faktycznie czynność może być tylko:

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

Ponieważ makro bezwarunkowo ocenia wszystkich swoich argumentów od lewej do prawej, a rentowności T jeśli wszystkie z nich są prawdziwe, to jest po prostu coś właśnie inline-optymalizacji, które mogą być wykonane przez funkcję. Funkcje mogą być wymagane, aby być zaznaczone jako (declaim 'inline ...). Co więcej, możemy napisać makro kompilatora dla funkcji tak samo z define-compiler-macro. Z tym makro możemy wyprodukować rozszerzenie, i mają to jako funkcję, którą możemy apply i poza tym pośrednio.

Inne sposoby obliczania wyników wewnątrz funkcji:

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

Przykład wygląda jak makro praktyce: dokonanie gensym i generowania kodu z loop. Ponadto makro jest punktem wyjścia dla czegoś, co może pojawić się w ramach testowania jednostkowego.

+1

Makro może być użyteczne (na przykład do optymalizacji), jeśli wystąpi zwarcie w połowie oceny jego argumentów, na co podejście funkcji nigdy nie pozwala. Ale w tej formie tak nie jest. –

+2

@JoaoTavora Jeśli makro zostało zwarte, byłoby to redundantną implementacją standardowego makra 'i'! – Kaz

+0

@Kaz, niezupełnie, ponieważ zwróciłoby 't', jeśli wszystkie argumenty nie są zerowe. Ale tak, w zasadzie. Myślę, że prawdziwym zastosowaniem w tym przypadku jest kąt "szczegółowości" @ hans23: za pomocą makra możesz drukować formularze i robić, co chcesz, z samymi formularzami. –

9

W tym przypadku może nie mieć znaczenia, ale dla przyszłych wersji może być bardziej użyteczne użycie makra. Czy ma sens używanie makra? Zależy od przypadku użycia:

Korzystanie z funkcji

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

Należy pamiętać, że w czasie wykonywania Lisp widzi, że combine-results jest funkcją. Następnie ocenia argumenty. Następnie wywołuje funkcję combine-results z wartościami wyniku. Ta reguła oceny jest zakodowana na Common Lisp.

To oznacza: kod funkcji działa po argumenty zostały ocenione.

Korzystanie makro

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

Od Lisp widzi, że jest to makro, wywołuje makro na makro czasie ekspansji i generuje kod. Co wygenerował kod, w pełni zależy od makra. To, co nam to pozwala, to generowanie kodu:

(prepare-an-environment 

    (embed-it (foo)) 
    (embed-it (bar)) 
    (embed-it (baz)) 

    (do-post-processing)) 

Kod ten zostanie wykonany.Można na przykład skonfigurować zmienne systemowe, udostępnić obsługę błędów, skonfigurować niektóre mechanizmy raportowania itd. Każda indywidualna forma może być następnie osadzona w innej formie. A po uruchomieniu funkcji, można zrobić kilka posprzątać, raportowanie itd prepare-an-environment i embed-it byłoby makra lub specjalne operatorzy, które sprawiają, że niektóre pewien kod działa przed, wokół i po wbudowanych formularzy zapewniamy.

Musielibyśmy kod wykonywany przed, wokół i po dostarczonych formularzy. Czy to jest przydatne? Może być. Może być przydatny w przypadku bardziej rozbudowanego środowiska testowego.

Jeśli brzmi to znajomo, można zauważyć, że można uzyskać podobną strukturę kodu za pomocą metod CLOS (primary, before, after, around). Testy byłyby uruchamiane metodą podstawową, a inny kod byłby uruchamiany jako około, przed i po metodach.

Uwaga: makro może również drukować (patrz komentarz Hans23), sprawdzać i/lub zmieniać dostarczone formularze.

Powiązane problemy