Java迭代代码上的Clojure lazy-seq



我正试图从我继承的一些迭代Java库代码中创建一个Clojure seq。基本上,Java代码所做的是使用解析器从文件中读取记录,将这些记录发送到处理器,并返回结果的ArrayList。在Java中,这是通过调用parser.readData(),然后调用parser.getRecord()来获得一条记录,然后将该记录传递到processer.processRecord()中来完成的。对parser.read Data()的每次调用都返回一条记录;如果没有更多记录,则返回null。Java中非常常见的模式。

因此,我在Clojure中创建了下一个记录函数,它将从解析器中获取下一条记录。

(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解析器处理器)),这给了我一个懒惰的seq。正如预期的那样,得到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-resules(datamap-seq解析器处理器))进行定义,这给了我一个懒惰的seq,并且没有实现任何元素。然而,只要我做了其他类似的事情(第一个结果),它就会强制实现整个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))))))

我真的不明白为什么删除会引起问题,但当我把它从这个功能中删除时,一切都正常(我现在正在其他地方删除空列表)。

循环表达式中的

循环/recurt循环,直到递归结束。在它周围添加一个懒惰的seq并不能阻止这一点。

您第一次尝试使用lazy-seq/cons应该已经可以正常工作了,没有堆栈溢出。我现在无法发现它的问题是什么,尽管它可能在代码的java部分。

我将在这里发布对Joost答案的补充。此代码:

(defn integers[start](懒惰的seq(缺点开始(integers(inc-start))))

如果我做这样的事情,将不会抛出StackOverflowExceptein:

(take 5 (drop 1000000 (integers)))

编辑:

当然,更好的方法是(iterate inc 0)。:)

第2版:

我将试着解释一下懒惰seq是如何工作的。lazy-seq是一个返回类似seq对象的宏。再加上在请求之前没有意识到第二个参数的cons,你就会变得懒惰。

现在来看一下LazySeq类是如何实现的。LazySeq.sval触发下一个值的计算,返回"冻结"的懒惰序列的另一个实例。方法LazySeq.seq甚至更好地展示了概念背后的机制。请注意,为了完全实现序列,它使用while循环。这本身意味着堆栈跟踪的使用仅限于返回LazySeq的另一个实例的短函数调用。

我希望这有任何意义。我描述了我可以从源代码中推断出的内容。如果我犯了什么错误,请告诉我。

最新更新