Вот что я использую для отладки (в 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
Мне приходилось иметь дело с хеш-таблицей, созданной вручную, в C ++, где get
метод принял в качестве аргумента неконстантную ссылку на строку, то есть я не могу вызвать ее с помощью литерала. Чтобы было легче разобраться, я написал что-то вроде следующего:
#define LET(name, value, body) \
do { \
string name(value); \
body; \
assert(name == value); \
} while (false)
Хотя что-то вроде этой проблемы вряд ли возникнет в lisp, мне особенно приятно, что у вас могут быть макросы, которые не оценивают свои аргументы дважды, например, путем введения реального let-связывания. (Допустим, здесь я мог бы обойти это).
Я также прибегаю к ужасно уродливому способу обернуть вещи в do ... while (false)
такое, что вы можете использовать их в тогдашней части if и по-прежнему работать как-то иначе, как и ожидалось. Вам это не нужно в lisp, который является функцией макросов, работающих на синтаксических деревьях, а не на строках (или последовательностях токенов, я думаю, в случае C и C ++), которые затем подвергаются синтаксическому анализу.
Существует несколько встроенных макросов потоков, которые можно использовать для реорганизации вашего кода таким образом, чтобы он читался более чётко («многопоточность», как в «совмещении кода», а не параллелизм). Например:
(->> (range 6) (filter even?) (map inc) (reduce *))
Он принимает первую форму (range 6)
и делает его последним аргументом следующей формы, (filter even?)
который, в свою очередь, становится последним аргументом следующей формы и т. Д., Так что приведенное выше переписывается в
(reduce * (map inc (filter even? (range 6))))
Я думаю, что первое читается гораздо яснее: «возьмите эти данные, сделайте это с ними, затем сделайте это, затем сделайте другое, и мы закончили», но это субъективно; объективно верно то, что вы читаете операции в той последовательности, в которой они выполняются (игнорируя лень).
Существует также вариант, который вставляет предыдущую форму в качестве первого (а не последнего) аргумента. Один вариант использования является арифметическим:
(-> 17 (- 2) (/ 3))
Читается как «возьмите 17, вычтите 2 и разделите на 3».
Говоря об арифметике, вы можете написать макрос, который выполняет синтаксический анализ инфиксных обозначений, так что вы могли бы сказать, например, (infix (17 - 2) / 3)
и это вылилось бы (/ (- 17 2) 3)
в недостаток, заключающийся в том, что он менее читабелен и имеет преимущество в том, что он является допустимым выражением lisp. Это часть подъязыка DSL / данных.