许多API(如Elasticsearch、GitHub、S3等)的某些部分
在用法中,最终以迭代方式使用。你做一次API调用
并使用结果进行另一项API调用,依此类推。这通常出现在
API具有某种分页结果的概念,你可以翻页,在HTTP API中非常
普遍。这似乎是一种非常常见的模式,如果Clojure有内置的
对该模式的支持那将非常棒。
你可能认为Clojure已经有了对该的支持,因为毕竟,Clojure有
`iterate`。实际上,`iterate`的文档字符串
明确说明您提供给它的函数必须是无副作用的。
我建议向clojure.core添加一个名为`unfold`的函数来支持这个用例。
`unfold`将返回ReduceInit的实现。`unfold`的名称与Haskell中相似
的函数名称相匹配(
https://hackage.haskell.org/package/base-4.8.2.0/docs/Data-List.html#v:unfoldr)
,同时也与一些现有的Clojure库中使用的函数的名称相匹配
(
https://github.com/amalloy/useful/blob/develop/src/flatland/useful/seq.clj#L128-L147)。
`unfold`在某种程度上类似于`take-while`和`iterate`的结合,
尽管`iterate`需要一个纯函数。另一种可能的解决方案是
不需要纯函数的`iterate`版本。
对于我为`unfold`构思的使用场景,非缓存可归约似乎是完美的。
但是,这会让那些喜欢序列的人无所适从,所以也许至少应该考虑
序列。
邮件列表上的讨论在这里
(
https://groups.google.com/forum/#!topic/clojure-dev/89RNvkLdYc4)
你可能想与之交互的某些类型的不太真实的API可能看起来是这样的
(导入'(java.util.UUID))
((def uuids (重复1000 #(UUID/randomUUID)))
((def uuid-index (
(循环[uuids uuids
index {}]
(if (序列uuids)
( rejuvenate (rest uuids) (关联索引(第一uuids) (rest uuids)))
index)))
)
))
(从uuid-index列表中获取)}}
{:page页面}
:下一个(最后一页)}}
基于上述API,如果您有一个实现`unfold`的函数,该函数有一个决定何时展开的谓词,一个生产函数,给在序列中的值生成下一个值,以及一个初始值,您可以这样做
(= uuids (into [] (mapcat :page) (unfold :next (comp api :next) (api :start))))
结果将是true。
等价的take-while + iterate将类似于以下内容
;; 停止条件并不是严格相同的
(= uuids (into [] (mapcat :page) (take-while (comp seq :page) (iterate (comp api :next) (api :start)))))