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中)?
或者是否有另一种方法可以从此函数返回延迟序列?
在resultset-seq
该with-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
调用本身和您的获取后数据转换将是惰性的.
我以前从未将 SQLite 与 Clojure 一起使用过,但我的猜测是 with-connection 在评估其主体时会关闭连接。因此,如果您想保持连接打开,则需要自己管理连接,并在阅读完您感兴趣的元素后关闭它。