Clojure: creating collections using `for` bindings

I'm still pretty new to clojure, but the template that I often use in it looks something like this: I have several collections, and I want to create a new collection, usually a hash map, from them with some filters or conditions. There are always several ways to do this: using loopor using reducein combination with map/ filter, for example, but I would like to implement something more than a macro forthat has excellent syntax for controlling what is evaluated in the loop. I would like to create a macro with syntax that would look like this:

(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, , ( ), , , . , , () , , () , 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, , , , , ?
  • , ?
  • - ?
+4
2

reduce , . , , , , , for , 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 , .

+1

doc, clojure. plumbing.core for-map, for-map-update:

(defn update!
  "Like update but for transients."
  ([m k f] (assoc! m k (f (get m k))))
  ([m k f x1] (assoc! m k (f (get m k) x1)))
  ([m k f x1 x2] (assoc! m k (f (get m k) x1 x2)))
  ([m k f x1 x2 & xs] (assoc! m k (apply f (get m k) x1 x2 xs))))

(defmacro for-map-update
  "Like 'for-map' for building maps but accepts a function as the value to build map values."
  ([seq-exprs key-expr val-expr]
   `(for-map-update ~(gensym "m") ~seq-exprs ~key-expr ~val-expr))
  ([m-sym seq-exprs key-expr val-expr]
   `(let [m-atom# (atom (transient {}))]
      (doseq ~seq-exprs
        (let [~m-sym @m-atom#]
          (reset! m-atom# (update! ~m-sym ~key-expr ~val-expr))))
      (persistent! @m-atom#))))

(for-map-update
  [x (range 4)
   y (range 4)
   :when (not= x y)]
  x (fnil #(conj % y) #{} ))
;; => {0 #{1 3 2}, 1 #{0 3 2}, 2 #{0 1 3}, 3 #{0 1 2}}
0

Source: https://habr.com/ru/post/1615773/


All Articles