Clojure中doall与reduce搭配是否合理?求重构建议
Clojure中doall与reduce搭配生成Hiccup的疑问与重构建议
问题背景
我正在使用Clojure、ClojureScript、lein、shadow-cljs、re-frame、reagent、Emacs及CIDER开发Clojure/ClojureScript动态Web应用,目前是Clojure新手。代码库中存在大量通过doall包裹reduce调用来生成hiccup(HTML渲染器)的写法,示例代码如下:
(doall (reduce (fn ...) ...) [] ; hiccup-output project-variable)
根据Clojure官方文档,doall用于处理惰性序列:
当惰性序列由带有副作用的函数生成时,除生成序列首个元素所需的副作用外,其他副作用需等到序列被消费时才会触发。doall可用于强制触发所有副作用,它会遍历序列的所有后续元素,保留表头并返回,从而使整个序列同时驻留在内存中。
我对此写法存在以下疑问:
doall难道不是应该用于惰性序列吗?- 我认为
reduce并不返回惰性序列,这个观点是否错误? - 若此处不应将
doall与reduce搭配使用,有何重构建议?
问题解答与重构建议
1. doall确实仅针对惰性序列生效
doall的核心设计目标就是处理惰性序列:强制遍历整个序列,触发所有副作用并将序列全部加载到内存中。对于非惰性序列,doall没有任何实际作用——因为非惰性序列本身已经是完全实例化的,遍历不会带来任何额外变化。
2. reduce确实不返回惰性序列
你的观点完全正确。Clojure中的reduce是严格求值的:它会一次性遍历完输入集合的所有元素,直接返回最终的累加结果(比如示例里的Hiccup向量),全程不会生成惰性序列。所以这里用doall包裹reduce完全是多余的,没有任何实际效果。
3. 重构建议
既然doall在这里毫无意义,直接去掉即可,保留reduce调用本身:
(reduce (fn ...) ; 你的累加逻辑 [] ; 初始Hiccup向量 project-variable)
如果代码里的reduce是用来遍历集合生成Hiccup元素,也可以考虑用更符合Clojure习惯的写法替代:
- 若只是简单映射集合元素为Hiccup组件,
map结合into会更直观:(into [] (map (fn [item] [:div.item item])) project-variable) - 要是有复杂的状态依赖累加逻辑(比如需要根据前一个元素的状态生成当前元素),保留
reduce就好,无需额外包裹任何函数。
内容的提问来源于stack exchange,提问作者Pedro Delfino
相关产品推荐
相关产品推荐

