Clojure和HBase:通过扫描迭代懒惰

假设我想在clojure中打印hbase表扫描的输出。

(defmulti scan (fn [table & args] (map class args))) (defmethod scan [java.lang.String java.lang.String] [table start-key end-key] (let [scan (Scan. (Bytes/toBytes start-key) (Bytes/toBytes end-key))] (let [scanner (.getScanner table scan)] (doseq [result scanner] (prn (Bytes/toString (.getRow result)) (get-to-map result)))))) 

get-to-map将结果转换为地图。 它可以像这样运行:

 (hbase.table/scan table "key000001" "key999999") 

但是,如果我想让用户对扫描结果做些什么呢? 我可以允许它们将函数作为回调函数传递给每个结果。 但我的问题是:如果我希望用户能够懒散地迭代每个结果,我会返回什么

 (Bytes/toString (.getRow result)) (get-to-map result) 

不是保留以前的结果,就像在lazy-seq的简单化中所发生的那样。

如果你接受一个回调参数,你可以在doseq调用它:

 (defmulti scan [f table & args] (mapv class args)) ; mapv returns vector (defmethod scan [String String] [f table start-key end-key] ; ^- java.lang classes are imported implicitly (let [scan ... scanner ...] ; no need for two separate lets (doseq [result scanner] ; call f here, eg (f result)))) 

这里f将按结果调用一次。 它的返回值以及结果本身将立即被丢弃。 你当然可以用一些预处理版本的result调用f ,例如(f (foo result) (bar result))

您还可以将结果的序列/向量返回给客户端,并让它自己进行处理。 如果序列是惰性的,你需要确保支持它的任何资源在处理期间保持打开状态(并且可能是它们稍后关闭 – 请参阅with-open ;处理代码需要在以下内容中执行with-open并在返回时完成处理)。

例如,要将预处理结果的向量返回给客户端,您可以执行此操作

 (defmethod scan ... (let [...] (mapv (fn preprocess-result [result] (result->map result)) scanner))) 

然后客户可以随心所欲地做任何事情。 使用map来返回延迟序列。 如果客户端需要打开/关闭资源,您可以接受它作为扫描的参数,以便客户端可以说

 (with-open [r (some-resource)] ; or mapv, dorun+map, doall+for, ... (doseq [result (scan r ...)] (do-stuff-with result)))