Ecco uno che uso per il debug (in 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
Ho dovuto gestire una tabella hash rollata a mano in C ++, dove il metodo get
ha preso come riferimento un riferimento stringa non const, il che significa che non posso chiamarlo con un letterale. Per renderlo più facile da gestire, ho scritto qualcosa del tipo:
#define LET(name, value, body) \
do { \
string name(value); \
body; \
assert(name == value); \
} while (false)
Anche se è improbabile che qualcosa di simile a questo problema venga fuori in lisp, trovo particolarmente piacevole il fatto che si possano avere macro che non valutano i loro argomenti due volte, ad esempio introducendo un reale let- rilegatura. (Ammesso, qui avrei potuto aggirarlo).
Ricorro anche al trucco terribilmente brutto dell'intreccio di roba in un do ... while (false)
tale che puoi usarlo nella parte allora di un if e avere ancora il resto della parte come previsto. Non è necessario questo in lisp, che è una funzione di macro che operano su alberi di sintassi piuttosto che su stringhe (o sequenze di token, penso, nel caso di C e C ++) che poi subiscono l'analisi.
Esistono alcune macro di threading incorporate che possono essere utilizzate per riorganizzare il codice in modo che sia più chiaro ("threading" come in "seminare il codice insieme", non parallelismo). Ad esempio:
(->> (range 6) (filter even?) (map inc) (reduce *))
Prende il primo modulo, (range 6)
, e lo rende l'ultimo argomento del modulo successivo, (filter even?)
, che a sua volta diventa l'ultimo argomento del modulo successivo e così via, in modo tale che il precedente venga riscritto in
(reduce * (map inc (filter even? (range 6))))
Penso che la prima sia molto più chiara: "prendi questi dati, fai questo, poi fallo, poi fai l'altro e abbiamo finito", ma questo è soggettivo; qualcosa che è oggettivamente vero è che leggi le operazioni nella sequenza in cui vengono eseguite (ignorando la pigrizia).
C'è anche una variante che inserisce il modulo precedente come primo (piuttosto che ultimo) argomento. Un caso d'uso è aritmetico:
(-> 17 (- 2) (/ 3))
Legge come "prendere 17, sottrarre 2 e dividere per 3".
Parlando di aritmetica, puoi scrivere una macro che esegue l'analisi delle notazioni infix, in modo che tu possa dire ad es. (infix (17 - 2) / 3)
e sputerebbe (/ (- 17 2) 3)
che ha lo svantaggio di essere meno leggibile e il vantaggio di essere un'espressione di lisp valida. Questa è la parte DSL / dati in sublocazione.