我正在尝试编写一个调试宏来打印出表达式及其值。如果我发送一个惰性序列,这会导致问题,因为如果我把它变成一个字符串(带 str),程序就会挂起。如果它位于顶层,则很容易检测到惰性序列:
(def foo (cycle [1 2]))
(= (type foo) clojure.lang.LazySeq) ;=> true
但是当然,如果它嵌套在另一个集合中,这将不起作用
(def bar (list (cycle [1 2])))
(= (type bar) clojure.lang.LazySeq) ;=> false
为了解决这个问题,我需要两件事之一:
1:一个检查集合的函数,看看它是否包含嵌套在某处的惰性序列。
2:将集合转换为字符串而不评估嵌套惰性序列的函数,如下所示:
(str2 {:inf (cycle [1 2])}) => "{:inf #clojure.lang.LazySeq@e9383}"
使用 Michał Marczyk 的回答我想出了这个宏:
(defmacro dbg-print [& rest]
"Print out values or expressions in context"
`(let [lazy-take# 5 ;when printing lazy-seq, how many elements to print
symb-str# (map str '~rest)
symb-evl# (reverse
(binding [*print-length* 10]
(loop [coll# (list ~@rest) retur# '()]
(if (not (empty? coll#))
(recur (rest coll#) (cons (pr-str (first coll#)) retur#))
retur#))))
pairs# (map #(str %1 %2 %3 %4) symb-str# (repeat ":") symb-evl# (repeat " "))
str# (reduce str pairs#)]
(println (format "%s\n" str#))))
它是这样工作的:
(dbg-print (+ 1 3) (cycle [1 2])) ;=> (+ 1 3):4 (cycle [1 2]):(1 2 1 2 1 2 1 2 1 2 ...)
并且可以处理嵌套的惰性序列:
(dbg-print (list (cycle [1 2]))) ;=> (list (cycle [1 2])):((1 2 1 2 1 2 1 2 1 2 ...))