loops - Clojure:使用 `for` 绑定(bind)构建集合

标签 loops for-loop clojure macros reduce

我对 clojure 还是很陌生,但是我发现自己经常在其中使用的一个模式是这样的:我有一些集合,我想构建一个新集合,通常是一个哈希映射,其中包含一些过滤器或条件。总有几种方法可以做到这一点:使用 loop或使用 reduce结合 map/filter例如,但我想实现更像 for 的东西宏,它具有很好的语法来控制在循环中评估的内容。我想生成一个语法如下的宏:

(defmacro build
  "(build sym init-val [bindings...] expr) evaluates the given expression expr
   over the given bindings (treated identically to the bindings in a for macro); 
   the first time expr is evaluated the given symbol sym is bound to the init-val
   and every subsequent time to the previous expr. The return value is the result
   of the final expr. In essence, the build macro is to the reduce function
   as the for macro is to the map function.

   Example:
     (build m {} [x (range 4), y (range 4) :when (not= x y)]
       (assoc m x (conj (get m x #{}) y)))
      ;; ==> {0 #{1 3 2}, 1 #{0 3 2}, 2 #{0 1 3}, 3 #{0 1 2}}"
  [sym init-val [& bindings] expr]
  `(...))

看着for clojure.core 中的代码,很明显我不想自己重新实现它的语法(甚至忽略复制代码的普通危险),但是在上面的宏中提出类似 for 的行为比我最初是预料到的。我最终想出了以下方法,但我觉得(a)这可能不是非常高效,并且(b)应该有一个更好的,仍然是 clojure-y 的方法来做到这一点:
(defmacro build
  [sym init-val bindings expr]
  `(loop [result# ~init-val, s# (seq (for ~bindings (fn [~sym] ~expr)))]
     (if s#
       (recur ((first s#) result#) (next s#))
       result#))
   ;; or `(reduce #(%2 %1) ~init-val (for ~bindings (fn [~sym] ~expr)))

我的具体问题:
  • 是否有一个内置的 clojure 方法或库可以解决这个问题,也许更优雅?
  • 假设我可能会非常频繁地将这个宏用于相对较大的集合,那么更熟悉 clojure 性能的人能否告诉我这个实现是否有问题以及我是否应该/多少担心性能?
  • 有什么好的理由我应该在上面的宏的reduce版本上使用循环,反之亦然?
  • 谁能看到宏的更好实现?
  • 最佳答案

    您的 reduce版本也是我基于问题陈述的第一种方法。我认为它既好又简单,我希望它能够很好地工作,特别是因为 for将产生一个分 block 的序列 reduce将能够非常快速地迭代。
    for无论如何都会生成函数来生成输出,我不希望 build 引入的额外层扩展尤其成问题。根据 volatile! 对该版本进行基准测试可能仍然值得。还有:

    (defmacro build [sym init-val bindings expr]
      `(let [box# (volatile! ~init-val)] ; AtomicReference would also work
         (doseq ~bindings
           (vreset! box# (let [~sym @box#] ~expr)))
         @box#))
    

    Criterium非常适合进行基准测试,并且可以消除任何与性能相关的猜测。

    关于loops - Clojure:使用 `for` 绑定(bind)构建集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33694209/

    相关文章:

    clojure,使用函数列表

    php循环解决方案

    python - 按每个整数的第一个数字对整数列表进行排序

    javascript - 使用 jQuery/Javascript 的 For 循环

    java - 将循环结果添加到数组中?

    clojure - 如何从嵌套列表中删除元素?

    java - 如何有效地对用户输入进行分类

    python - 找到达到多个级别的方法

    c - c中绝对最快的for循环是什么?

    Java 将自定义对象传递给 Clojure