Oto jeden, którego używam do debugowania (w Clojure):
user=> (defmacro print-var [varname] `(println ~(name varname) "=" ~varname))
#'user/print-var
=> (def x (reduce * [1 2 3 4 5]))
#'user/x
=> (print-var x)
x = 120
nil
Musiałem poradzić sobie z ręcznie zwijaną tabelą skrótów w C ++, w której get
metoda przyjęła jako argument ciąg non-const, co oznacza, że nie mogę tego nazwać dosłownie. Aby łatwiej sobie z tym poradzić, napisałem coś takiego:
#define LET(name, value, body) \
do { \
string name(value); \
body; \
assert(name == value); \
} while (false)
Podczas gdy coś podobnego do tego problemu raczej nie pojawi się w lisp, uważam za szczególnie miłe, że możesz mieć makra, które nie oceniają ich argumentów dwa razy, na przykład poprzez wprowadzenie prawdziwego let-bind. (Przyznaję, że mogłem to obejść).
Uciekam się też do okropnie brzydkiego sposobu pakowania rzeczy w do ... while (false)
taki sposób, że można go użyć w części „jeśli” i nadal wykonywać inne czynności zgodnie z oczekiwaniami. Nie potrzebujesz tego w lisp, który jest funkcją makr działających na drzewach składniowych, a nie na ciągach (lub sekwencjach tokenów, jak sądzę w przypadku C i C ++), które następnie są analizowane.
Istnieje kilka wbudowanych makr wątków, których można użyć w celu reorganizacji kodu, tak aby czytał on bardziej czytelnie („wątkowanie” jak w „zasiewie kodu razem”, a nie równoległości). Na przykład:
(->> (range 6) (filter even?) (map inc) (reduce *))
Przyjmuje pierwszą formę (range 6)
i czyni ją ostatnim argumentem następnej formy, (filter even?)
która z kolei jest ostatnim argumentem następnej formy i tak dalej, tak że powyższe zostaje przepisane na
(reduce * (map inc (filter even? (range 6))))
Myślę, że pierwszy z nich brzmi znacznie jaśniej: „weź te dane, zrób to, następnie zrób to, a potem zrób drugie i gotowe”, ale to subiektywne; obiektywnie prawda polega na tym, że odczytujesz operacje w kolejności, w jakiej są wykonywane (ignorując lenistwo).
Istnieje również wariant, który wstawia poprzednią formę jako pierwszy (a nie ostatni) argument. Jeden przypadek użycia jest arytmetyczny:
(-> 17 (- 2) (/ 3))
Odczytuje jako „weź 17, odejmij 2 i podziel przez 3”.
Mówiąc o arytmetyki, możesz napisać makro, które analizuje zapis notacji poprawkowej, abyś mógł powiedzieć np. (infix (17 - 2) / 3)
I wyplułoby to, (/ (- 17 2) 3)
co ma tę wadę, że jest mniej czytelne i ma tę zaletę, że jest prawidłowym wyrażeniem lisp. To jest część językowa DSL / danych.