如果您有一个输出一系列值的副作用函数,请将其建模为一个序列。如果序列终止,则该函数可能返回无效值。
功能
(defn ensequence [f! valid?]
(take-while valid? (repeatedly f!)))
... 返回由副作用函数f! 生成的值序列,每当valid? 测试失败时终止。
例如,
(ensequence #(rand-int 10) #(not= 5 %))
...返回(range 10) 中的随机值序列,在第一个5 之前停止:
(6 9)
...例如(您的里程会有所不同)。
为了说明ensequence 在您的情况下如何工作,我们使用了一个逆函数,将序列转换为返回其连续元素的函数,然后nil:
(defn oracle! [coll]
(let [s (atom coll)]
(fn [] (let [x (first @s)] (swap! s rest) x))))
例如,
(repeatedly 10 (oracle! (range 5)))
;(0 1 2 3 4 nil nil nil nil nil)
为了您的数据
(def data [{1 "mary", 2 "dean"} {23 "ava", 4 "scout"} {3 "bina", 16 "bob"}])
功能
(oracle! data)
...依次返回它的元素,后面跟着nils:
(repeatedly 10 (oracle! data))
;({1 "mary", 2 "dean"} {23 "ava", 4 "scout"} {3 "bina", 16 "bob"}
nil nil nil nil nil nil nil)
我们可以在上面使用ensequence,不管它是如何生成的,来恢复原始序列:
(ensequence (oracle! data) identity)
;({1 "mary", 2 "dean"} {23 "ava", 4 "scout"} {3 "bina", 16 "bob"})
由于nil 是假的并且在这里永远无效,identity 是一个很好的有效性测试。
现在我们有了序列,我们可以用它做任何我们喜欢的事情。在你的情况下,我们只是
- 将地图连接成一个大的地图条目序列;
- 找到最大
key的条目;和
- 接受它的
val。
因此:
(val (apply max-key key (reduce concat data)))
;"ava"
我们应该使用(ensequence (oracle! data) identity) 而不是等价的data,但这没有任何区别。