我正在尝试从我继承的一些迭代 Java 库代码中创建一个 Clojure seq。基本上,Java 代码所做的是使用解析器从文件中读取记录,将这些记录发送到处理器并返回结果的 ArrayList。在 Java 中,这是通过调用 parser.readData(),然后调用 parser.getRecord() 来获取记录,然后将该记录传递给 processor.processRecord() 来完成的。对 parser.readData() 的每次调用都返回一条记录,如果没有更多记录,则返回 null。Java中很常见的模式。
所以我在 Clojure 中创建了这个 next-record 函数,它将从解析器中获取下一条记录。
(defn next-record
"Get the next record from the parser and process it."
[parser processor]
(let [datamap (.readData parser)
row (.getRecord parser datamap)]
(if (nil? row)
nil
(.processRecord processor row 100))))
然后的想法是调用此函数并将记录累积到 Clojure seq(最好是惰性 seq)中。所以这是我的第一次尝试,只要没有太多记录,它就可以很好地工作:
(defn datamap-seq
"Returns a lazy seq of the records using the given parser and processor"
[parser processor]
(lazy-seq
(when-let [records (next-record parser processor)]
(cons records (datamap-seq parser processor)))))
我可以创建一个解析器和处理器,并执行类似 (take 5 (datamap-seq parser processor)) 之类的操作,这给了我一个惰性序列。正如预期的那样,获得该 seq 的(第一个)只实现一个元素,执行 count 实现所有元素,等等。这正是我对惰性 seq 所期望的行为。
当然,当有很多记录时,我最终会遇到 StackOverflowException。所以我的下一个尝试是使用循环递归来做同样的事情。
(defn datamap-seq
"Returns a lazy seq of the records using the given parser and processor"
[parser processor]
(lazy-seq
(loop [records (seq '())]
(if-let [record (next-record parser processor)]
(recur (cons record records))
records))))
现在以相同的方式使用它并使用 (def results (datamap-seq parser processor)) 定义它给了我一个惰性序列并且没有实现任何元素。但是,一旦我做任何其他事情(例如(第一个结果)),它就会强制实现整个 seq。
谁能帮助我理解我在第二个函数中出错的地方,使用循环递归导致它实现整个事情?
更新:
我已经从异常中仔细查看了堆栈跟踪,并且堆栈溢出异常是从 Java 类之一引发的。但是只有当我有这样的 datamap-seq 函数时才会发生这种情况(我上面发布的那个确实有效):
(defn datamap-seq
"Returns a lazy seq of the records using the given parser and processor"
[parser processor]
(lazy-seq
(when-let [records (next-record parser processor)]
(cons records (remove empty? (datamap-seq parser processor))))))
我真的不明白为什么删除会导致问题,但是当我把它从这个功能中取出时,它一切正常(我现在正在其他地方删除空列表)。