Я использую все 3 техники в зависимости от ситуации.Цель состоит в том, чтобы выбрать технику, которая делает код наиболее понятным (не наименьшим количеством символов!).
Техника № 2 особенно удобна при отладке , так какВы можете легко распечатать промежуточные значения.Тем не менее, я обычно даю каждому этапу отдельное имя, чтобы прояснить ситуацию:
(let [x-1 foo
x-2 (my-fn1 x-1 bar)
x-3 (my-fn2 x-2 baz)
x-4 (my-fn3 x-3 qux)]
(println :x-1 x-1)
(println :x-2 x-2)
(println :x-3 x-3)
(println :x-4 x-4)
x-4) ; don't forget to return the final result!
Обновление
Что касается отладки, я бы так и сделал.Во-первых, необработанные 3 версии:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn fa [x y] (+ x y))
(defn fb [x y] (* x y))
(defn fc [x y] {:x x :y y})
(def tgt 2)
(defn stack-nest
[foo]
(fc
(fb
(fa foo 3)
3)
99))
(defn stack-thread
[foo]
(-> foo
(fa 3)
(fb 3)
(fc 99)))
(defn stack-let
[foo]
(let [a foo
b (fa a 3)
c (fb b 3)
d (fc c 99)]
d)) ; don't forget to return the final result!
Вам не нужно изобретать собственную функцию dbg
, так как в библиотеке Tupelo уже есть несколько хороших опций .Здесь мы печатаем результаты, используя макрос spyx
(явный шпион):
(dotest
(spyx (stack-nest tgt))
(spyx (stack-thread tgt))
(spyx (stack-let tgt)))
(stack-nest tgt) => {:x 15, :y 99}
(stack-thread tgt) => {:x 15, :y 99}
(stack-let tgt) => {:x 15, :y 99}
Затем добавляем отладочную информацию, используя spy
и метку:
(defn stack-nest
[foo]
(spy :fc (fc
(spy :fb (fb
(spy :fa (fa foo 3))
3))
99)))
:fa => 5
:fb => 15
:fc => {:x 15, :y 99}
(stack-nest tgt) => {:x 15, :y 99}
Это работает, но это довольно некрасиво.Как насчет формы потоков?Здесь мы можем вставить шпиона, и это немного лучше:
(defn stack-thread
[foo]
(-> foo
(spy :foo)
(fa 3)
(spy :fa)
(fb 3)
(spy :fb)
(fc 99)
(spy :fc)
))
:foo => 2
:fa => 5
:fb => 15
:fc => {:x 15, :y 99}
(stack-thread tgt) => {:x 15, :y 99}
Мы получаем то, что хотим, но у него есть некоторое дублирование.Кроме того, нам нужно поместить каждое выражение (spy ...)
в отдельную строку, чтобы макрос многопоточности ->
отправлял значение как в вычисление, например (fa 3)
, так и на этап печати, например, (spy :fa)
.
Мы можем немного упростить его с помощью макроса it->
следующим образом:
(defn stack-thread-it
[foo]
(it-> foo
(fa it 3)
(fb it 3)
(fc 99 it)))
Мы используем символ it
в качестве заполнителя.Обратите внимание, что мы можем поместить резьбовое значение в любую позицию аргумента, как показано с помощью перевернутых аргументов fc
.Для нашей отладки используйте spyx
, чтобы выражения были помечены самостоятельно, и мы получили:
(defn stack-thread-it
[foo]
(it-> (spyx foo)
(spyx (fa it 3))
(spyx (fb it 3))
(spyx (fc 99 it))))
foo => 2
(fa it 3) => 5
(fb it 3) => 15
(fc 99 it) => {:x 99, :y 15}
(stack-thread-it tgt) => {:x 99, :y 15}
Когда промежуточные переменные находятся в выражении let
, я отлаживаю следующим образом:
(defn stack-let
[foo]
(let [a foo
>> (spyx a)
b (fa a 3)
>> (spyx b)
c (fb b 3)
>> (spyx c) ]
(spyx (fc c 99))))
a => 2
b => 5
c => 15
(fc c 99) => {:x 15, :y 99}
(stack-let tgt) => {:x 15, :y 99}
Обратите внимание, что последняя функция fc
вызывается непосредственно как возвращаемое значение (за пределами let
), но мы все равно можем напечатать его значение, используя spyx
.
Обратите внимание, что мне нравитсяиспользовать символ >>
(не используется в Clojure) вместо подчеркивания _
в качестве фиктивного получателя значения выражения, поскольку подчеркивание иногда трудно увидеть.Символ >>
не только выделяется в коде, но и выглядит как подсказка командной строки, напоминая об обязательном побочном эффекте действия печати.