Clojure中的迭代器块?

Ale*_*x B 6 clojure lazy-evaluation lazy-sequences

clojure.contrib.sql用来从SQLite数据库中获取一些记录.

(defn read-all-foo []
  (with-connection *db*
    (with-query-results res ["select * from foo"]
       (into [] res))))
Run Code Online (Sandbox Code Playgroud)

现在,我真的不想在从函数返回之前实现整个序列(即我想让它保持懒惰),但是如果我res直接返回或者将它包装成某种惰性包装器(例如我想要确定map结果序列转换),SQL相关的绑定将被重置,连接将在我返回后关闭,因此实现序列将引发异常.

如何将整个函数包含在闭包中并返回一种迭代器块(如yield在C#或Python中)?

或者是否有另一种方法可以从此函数返回延迟序列?

Bri*_*per 7

resultset-seqwith-query-results返回可能已经很懒,你会得到什么.正如你所说,只有把手打开,懒惰才有效.没有办法解决这个问题.如果关闭数据库句柄,则无法从数据库中读取.

如果你需要做I/O并在手柄关闭后保留数据,然后打开手柄,快速啜饮它(打败懒惰),关闭手柄,然后处理结果.如果你想迭代一些数据而不将它全部保存在内存中,那么打开句柄,获取数据的懒惰seq doseq,然后关闭句柄.

因此,如果您想对每一行(对于副作用)执行某些操作并丢弃结果而不将整个结果集放入内存中,那么您可以这样做:

(defn do-something-with-all-foo [f]
  (let [sql "select * from foo"]
    (with-connection *db*
      (with-query-results res [sql]
        (doseq [row res]
          (f row))))))

user> (do-something-with-all-foo println)
{:id 1}
{:id 2}
{:id 3}
nil

;; transforming the data as you go
user> (do-something-with-all-foo #(println (assoc % :bar :baz)))
{:id 1, :bar :baz}
{:id 2, :bar :baz}
{:id 3, :bar :baz}
Run Code Online (Sandbox Code Playgroud)

如果你想让你的数据长期存在,那么你也可以在read-all-foo上面使用你的函数(从而消除懒惰).如果您想要转换数据,那么map在您获取所有数据后,将结果.此时您的数据将全部存储在内存中,但map调用本身和您的获取后数据转换将是惰性的.


Mic*_*ent 0

我以前从未将 SQLite 与 Clojure 一起使用过,但我的猜测是 with-connection 在评估其主体时会关闭连接。因此,如果您想保持连接打开,则需要自己管理连接,并在阅读完您感兴趣的元素后关闭它。