如何避免评估集合中的lazy-seq

时间:2013-06-13 15:16:45

标签: macros clojure lazy-evaluation

我正在尝试编写一个打印出表达式及其值的调试宏。如果我发送了一个lazy-seq,这会导致问题,因为如果我把它变成一个字符串(带有str),程序就会挂起。如果它位于顶层,很容易检测到lazy-seq:

(def foo (cycle [1 2]))
(= (type foo) clojure.lang.LazySeq) ;=> true

但是,当然如果它嵌套在另一个集合中,则无效

(def bar (list (cycle [1 2])))
(= (type bar) clojure.lang.LazySeq) ;=> false

为了解决这个问题,我需要做以下两件事之一:

1:一个检查集合的函数,看它是否包含嵌套在某处的lazy-seq。

2:在不评估嵌套的lazy-seqs的情况下将集合转换为字符串的函数,如下所示:

(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 ...) 

并且可以处理嵌套的lazy-seqs:

(dbg-print (list (cycle [1 2]))) ;=> (list (cycle [1 2])):((1 2 1 2 1 2 1 2 1 2 ...)) 

2 个答案:

答案 0 :(得分:4)

您可以将内置Vars *print-length**print-level*pr / print家庭功能(包括pr-str一起使用,如果您想获得字符串表示形式返回为返回值,而不是将其打印出来):

(binding [*print-length* 3
          *print-level*  3]
  (prn ((fn explode []
          (repeatedly #(repeatedly explode))))))

打印出来

(((# # # ...) (# # # ...) (# # # ...) ...) ((# # # ...) (# # # ...) (# # # ...) ...) ((# # # ...) (# # # ...) (# # # ...) ...) ...)

其中#表示由于过去*print-level*而忽略了数据结构的那些部分,...表示数据结构的那些部分由于省略而被省略延伸过*print-length*

打印了一些实际数据的另外两个例子:

user> (binding [*print-length* 10]
        (prn (cycle [1 2 3])))
(1 2 3 1 2 3 1 2 3 1 ...)

user> (binding [*print-level* 10]
        (prn ((fn step [i]
                (lazy-seq (list i (step (inc i)))))
              0)))
(0 (1 (2 (3 (4 (5 (6 (7 (8 (9 #))))))))))

最后,返回一个字符串的示例:

user> (binding [*print-length* 2
                *print-level*  2]
        (prn-str ((fn explode []
                    (repeatedly #(repeatedly explode))))))
"((# # ...) (# # ...) ...)\n"

此功能记录在相关Vars的文档字符串中,请参阅(doc *print-length*)(doc *print-level*)

答案 1 :(得分:3)

我想到了几种方法:

  • 用预走路走结构,用占位符替换懒人序列。
  • 使用prewalk行走序列,并递归地将10行应用于每个集合。
  • 为Clojure.lang.LazySeq扩展print-method multi方法以打印它们,无论你选择了什么。