Macros 如何避免对集合中的惰性seq求值

Macros 如何避免对集合中的惰性seq求值,macros,clojure,lazy-evaluation,Macros,Clojure,Lazy Evaluation,我正在尝试编写一个调试宏来打印表达式及其值。如果我发送一个懒惰的seq,这会导致问题,因为如果我将它转换成字符串(使用str),程序将挂起。如果懒惰的seq位于顶层,则很容易检测到它: (def foo (cycle [1 2])) (= (type foo) clojure.lang.LazySeq) ;=> true 但当然,如果它嵌套在另一个集合中,这就不起作用 (def bar (list (cycle [1 2]))) (= (type bar) clojure.lang.La

我正在尝试编写一个调试宏来打印表达式及其值。如果我发送一个懒惰的seq,这会导致问题,因为如果我将它转换成字符串(使用str),程序将挂起。如果懒惰的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:检查集合是否包含嵌套在某处的惰性seq的函数

2:一个函数,用于将集合转换为字符串,而不计算嵌套的惰性seq,如下所示:

(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 ...) 
并且可以处理嵌套的惰性seq:

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

我想到了几种方法:

  • 使用prewalk遍历结构,并使用占位符替换惰性序列
  • 使用预遍历遍历序列,并递归地对每个集合应用take 10
  • 扩展Clojure.lang.LazySeq的print method multi方法,以按您选择的方式打印它们

    • 我想到了几种方法:

      • 使用prewalk遍历结构,并使用占位符替换惰性序列
      • 使用预遍历遍历序列,并递归地对每个集合应用take 10
      • 扩展Clojure.lang.LazySeq的print method multi方法,以按您选择的方式打印它们

      您可以使用内置Vars
      *打印长度*
      *打印级别*
      pr
      打印系列函数(包括
      pr str
      如果您希望将字符串表示形式作为返回值返回,而不是打印出来):

      这是打印出来的

      (((# # # ...) (# # # ...) (# # # ...) ...) ((# # # ...) (# # # ...) (# # # ...) ...) ((# # # ...) (# # # ...) (# # # ...) ...) ...)
      
      其中,
      #
      s表示数据结构的这些部分由于下降超过
      *打印级别*
      而被忽略,
      ..
      s表示数据结构的这些部分由于超出
      *打印长度*
      而被忽略

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

      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"
      

      此功能记录在有关VAR的文档字符串中,请参见
      (doc*打印长度*)
      (doc*打印级别*)

      您可以将内置VAR
      *打印长度*
      *打印级别*
      pr
      /
      打印系列功能一起使用(包括
      pr str
      ,如果您希望将字符串表示形式作为返回值返回,而不是将其打印出来):

      这是打印出来的

      (((# # # ...) (# # # ...) (# # # ...) ...) ((# # # ...) (# # # ...) (# # # ...) ...) ((# # # ...) (# # # ...) (# # # ...) ...) ...)
      
      其中,
      #
      s表示数据结构的这些部分由于下降超过
      *打印级别*
      而被忽略,
      ..
      s表示数据结构的这些部分由于超出
      *打印长度*
      而被忽略

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

      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"
      

      有关VAR的文档字符串中记录了此功能,请参见
      (文档*打印长度*)
      (文档*打印级别*)

      一个实际的反问题:为什么要调试打印一个无限大的惰性seq?如果你不知道无限大,很可能是错误。如果你知道无限大,调试更有趣的部分是从中获取一些东西(比如(dbg宏(第一个(分区依据(comp=second)inf seq))).只是说像你的lazy take#5这样的限制会让我更担心在我进行调试时在我的思维中产生错误,因为调试输出不会表示程序中处理的数据。你可以通过从中获取一些东西来计算它包含了什么,但是立即获得结构的概览很好。例如,我有一个懒惰的seq,它意外地被包装在一个列表中。如果我使用该列表的第一个,程序将崩溃。一般来说,不让宏崩溃是很好的,因为你不小心给它提供了一个懒惰的seq。一个实际的反问:为什么你要调试打印一个无限的懒惰seq?如果你不知道关于无穷大,这可能是错误所在。如果您确实了解无穷大,那么调试更有趣的部分是从中获取一些东西(例如(dbg宏(first(partition by(comp=second)inf seq))).只是说像你的lazy take#5这样的限制会让我更担心在我进行调试时在我的思维中产生错误,因为调试输出不会表示程序中处理的数据。你可以通过从中获取一些东西来计算它包含了什么,但是立即获得结构的概览很好。例如,我有一个懒惰的seq,它意外地被包装在一个列表中。如果我使用该列表的第一个,程序将崩溃。一般来说,不让宏崩溃是很好的,因为您意外地给它提供了一个懒惰的seq。