首页 > 解决方案 > 在列表推导中使用 prn 或 println 进行调试

问题描述

(defn f1 []
  (for [a [1 2]]
    a))
;user=>(f1)
;(1 2)

(defn f2 []
  (for [a [1 2]]
    (prn a)))
;user=>(f2)
;1
;2
;(nil nil)

(defn f3 []
  (for [a [1 2]]
    (prn a))
  'something-else)
;user=>(f3)
;something-else

为什么 f3 在打印“其他”之前不打印 1 和 2?

即我预期并假设(错误地)它将打印以下内容:

; 1
; 2
; something-else

当使用其中包含大量代码的 for 时遇到了这个问题,这对我尝试使用 prn 语句在调试时跟踪变量的值造成了严重破坏。即 prn 和 println 不打印出来。我认为只有当 for 块不是其封闭形式的最终形式时,但我仍然不确定发生了什么。

prn关键是诸如or之类的副作用println不应该需要在返回值位置才能触发。所以我不理解列表推导的更深层次的东西。

我写这篇文章时的一个概念 -f3由于懒惰,也许在列表中的理解根本就没有被评估过?...哦[审查]。

是的,确实是这样:

(defn f4 []
  (doall
    (for [a [1 2]]
      (prn a)))
  'something-else)
user=> (f4)
;1
;2
;something-else

所以,即使大部分都解决了,我仍然会发布这个问题来巩固学习——有人愿意发布一些他们自己的问题的例子吗?懒惰。

标签: clojure

解决方案


正如您在此处记录的那样,宏for是惰性的,直到必须执行时才会执行。您可以通过将其包装在(vec ...)表单或doall.

特别是出于调试目的,我喜欢使用spyxforv来自Tupelo 库来避免所有这些陷阱:

(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(defn f5 []
  (forv [a [1 2]]
    (spyx a))
  (spy :returning :something-else))

(dotest
  (spyx (f5)))

结果:

-------------------------------
   Clojure 1.10.1    Java 14
-------------------------------

Testing tst.demo.core
a => 1
a => 2
:returning => :something-else
(f5) => :something-else

Ran 2 tests containing 1 assertions.
0 failures, 0 errors.

所以你可以看到打印得很好:

  • 循环a每一步的标记值。forv
  • 带有自定义标签的函数的返回值
  • 函数调用的显式值(f5)
  • 没有什么地方是懒惰的
  • spy, spyx, et al 总是返回打印的值以供进一步使用
  • 一个方便的单元测试通过is=确认输出值。

有一个不错的模板项目,您可以克隆它以帮助您快速入门。

您可能还对with-result或 just感兴趣doseq。不要忘记Clojure CheatSheet


推荐阅读