假设我想在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, e.g.
(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)))