Make kvrf smarter: can infer arity 2 from and 3 from 2
This commit is contained in:
parent
899154c0df
commit
b96d9d7994
3 changed files with 27 additions and 21 deletions
|
|
@ -27,7 +27,7 @@ In `net.cgrand.xforms.io`:
|
||||||
|
|
||||||
*Transducing contexts*:
|
*Transducing contexts*:
|
||||||
|
|
||||||
* in `net.cgrand.xforms`: `transjuxt` (for performing several transductions in a single pass), `into`, `count` and `some`.
|
* in `net.cgrand.xforms`: `transjuxt` (for performing several transductions in a single pass), `iterator` (clojure only), `into`, `count` and `some`.
|
||||||
* in `net.cgrand.xforms.io`: `line-out` (3+ args) and `edn-out` (3+ args).
|
* in `net.cgrand.xforms.io`: `line-out` (3+ args) and `edn-out` (3+ args).
|
||||||
|
|
||||||
*Reducible views* (in `net.cgrand.xforms.io`): `lines-in` and `edn-in`.
|
*Reducible views* (in `net.cgrand.xforms.io`): `lines-in` and `edn-in`.
|
||||||
|
|
@ -37,7 +37,7 @@ In `net.cgrand.xforms.io`:
|
||||||
Add this dependency to your project:
|
Add this dependency to your project:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
[net.cgrand /xforms "0.11.0"]
|
[net.cgrand /xforms "0.12.0"]
|
||||||
```
|
```
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
(defproject net.cgrand/xforms "0.11.0"
|
(defproject net.cgrand/xforms "0.12.0"
|
||||||
:description "Extra transducers for Clojure"
|
:description "Extra transducers for Clojure"
|
||||||
:url "https://github.com/cgrand/xforms"
|
:url "https://github.com/cgrand/xforms"
|
||||||
:license {:name "Eclipse Public License"
|
:license {:name "Eclipse Public License"
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@
|
||||||
x#
|
x#
|
||||||
(unreduced-> (-> x# ~expr) ~@exprs)))))
|
(unreduced-> (-> x# ~expr) ~@exprs)))))
|
||||||
|
|
||||||
|
(defn- pair? [x] (and (vector? x) (= 2 (core/count x))))
|
||||||
|
(defn- destructuring-pair? [x]
|
||||||
|
(and (pair? x) (not (or (keyword? x) (= '& x)))))
|
||||||
|
|
||||||
(defmacro for
|
(defmacro for
|
||||||
"Like clojure.core/for with the first expression being replaced by % (or _). Returns a transducer.
|
"Like clojure.core/for with the first expression being replaced by % (or _). Returns a transducer.
|
||||||
When the first expression is not % (or _) returns an eduction."
|
When the first expression is not % (or _) returns an eduction."
|
||||||
|
|
@ -33,9 +37,6 @@
|
||||||
`(eduction (for [~binding ~'% ~@seq-exprs] ~body-expr) ~%or_)
|
`(eduction (for [~binding ~'% ~@seq-exprs] ~body-expr) ~%or_)
|
||||||
(let [rf (gensym 'rf)
|
(let [rf (gensym 'rf)
|
||||||
acc (gensym 'acc)
|
acc (gensym 'acc)
|
||||||
pair? #(and (vector? %) (= 2 (core/count %)))
|
|
||||||
destructuring-pair? (every-pred pair?
|
|
||||||
#(not-any? (some-fn keyword? #{'&}) %))
|
|
||||||
rpairs (core/partition 2 (rseq (vec seq-exprs)))
|
rpairs (core/partition 2 (rseq (vec seq-exprs)))
|
||||||
build (fn [init]
|
build (fn [init]
|
||||||
(core/reduce (fn [body [expr binding]]
|
(core/reduce (fn [body [expr binding]]
|
||||||
|
|
@ -74,16 +75,27 @@
|
||||||
(kvrf
|
(kvrf
|
||||||
([] (~rf))
|
([] (~rf))
|
||||||
([~acc] (~rf ~acc))
|
([~acc] (~rf ~acc))
|
||||||
([~acc ~binding] ~body)
|
([~acc ~binding] ~body)))))))
|
||||||
~(if (destructuring-pair? binding)
|
|
||||||
`([~acc ~@binding] ~body)
|
(defn- arity [[arglist & body :as fn-body]]
|
||||||
`([~acc k# v#]
|
(let [[fixargs varargs] (split-with (complement #{'&}) arglist)]
|
||||||
(let [~binding (net.cgrand.macrovich/case :clj (clojure.lang.MapEntry. k# v#) :cljs [k# v#])] ~body)))))))))
|
(if (seq varargs) (zipmap (range (core/count fixargs) 4) (repeat fn-body)))
|
||||||
|
{(core/count fixargs) fn-body}))
|
||||||
|
|
||||||
(defmacro kvrf [name? & fn-bodies]
|
(defmacro kvrf [name? & fn-bodies]
|
||||||
(let [name (if (symbol? name?) name? (gensym '_))
|
(let [name (if (symbol? name?) name? (gensym '_))
|
||||||
fn-bodies (if (symbol? name?) fn-bodies (cons name? fn-bodies))
|
fn-bodies (if (symbol? name?) fn-bodies (cons name? fn-bodies))
|
||||||
fn-bodies (if (vector? (first fn-bodies)) (list fn-bodies) fn-bodies)]
|
fn-bodies (if (vector? (first fn-bodies)) (list fn-bodies) fn-bodies)
|
||||||
|
arities (core/into {} (mapcat arity) fn-bodies)
|
||||||
|
_ (when-not (core/some arities [2 3]) (throw (ex-info "Either arity 2 or 3 should be defined in kvrf." {:form &form})))
|
||||||
|
fn-bodies (cond-> fn-bodies
|
||||||
|
(not (arities 3)) (conj (let [[[acc arg] & body] (arities 2)]
|
||||||
|
(if (destructuring-pair? arg)
|
||||||
|
(let [[karg varg] arg]
|
||||||
|
`([~acc ~karg ~varg] ~@body))
|
||||||
|
`([~acc k# v#] (let [~arg (macros/case :clj (clojure.lang.MapEntry. k# v#) :cljs [k# v#])] ~@body)))))
|
||||||
|
(not (arities 2)) (conj (let [[[acc karg varg] & body] (arities 3)]
|
||||||
|
`([~acc [~karg ~varg]] ~@body))))]
|
||||||
`(reify
|
`(reify
|
||||||
~@(macros/case :clj '[clojure.lang.Fn])
|
~@(macros/case :clj '[clojure.lang.Fn])
|
||||||
KvRfable
|
KvRfable
|
||||||
|
|
@ -93,7 +105,7 @@
|
||||||
(let [nohint-args (map (fn [arg] (if (:tag (meta arg)) (gensym 'arg) arg)) args)
|
(let [nohint-args (map (fn [arg] (if (:tag (meta arg)) (gensym 'arg) arg)) args)
|
||||||
rebind (mapcat (fn [arg nohint]
|
rebind (mapcat (fn [arg nohint]
|
||||||
(when-not (= arg nohint) [arg nohint])) args nohint-args)]
|
(when-not (= arg nohint) [arg nohint])) args nohint-args)]
|
||||||
`(~(macros/case :cljs `core/-invoke :clj 'invoke) [~name ~@nohint-args] (let [~@rebind] ~@body)))))))
|
`(~(macros/case :cljs `core/-invoke :clj 'invoke) [~name ~@nohint-args] ~@(if (seq rebind) [`(let [~@rebind] ~@body)] body)))))))
|
||||||
|
|
||||||
(defmacro ^:private let-complete [[binding volatile] & body]
|
(defmacro ^:private let-complete [[binding volatile] & body]
|
||||||
`(let [v# @~volatile]
|
`(let [v# @~volatile]
|
||||||
|
|
@ -122,8 +134,7 @@
|
||||||
(kvrf
|
(kvrf
|
||||||
([] (rf))
|
([] (rf))
|
||||||
([acc] (rf acc))
|
([acc] (rf acc))
|
||||||
([acc x] (rf acc x))
|
([acc x] (rf acc x)))))
|
||||||
([acc k v] (rf acc #?(:clj (clojure.lang.MapEntry. k v) :cljs [k v]))))))
|
|
||||||
|
|
||||||
(defn reduce
|
(defn reduce
|
||||||
"A transducer that reduces a collection to a 1-item collection consisting of only the reduced result.
|
"A transducer that reduces a collection to a 1-item collection consisting of only the reduced result.
|
||||||
|
|
@ -207,14 +218,12 @@
|
||||||
(kvrf
|
(kvrf
|
||||||
([] (rf))
|
([] (rf))
|
||||||
([acc] (rf acc))
|
([acc] (rf acc))
|
||||||
([acc kv] (rf acc (val kv)))
|
|
||||||
([acc k v] (rf acc v))))
|
([acc k v] (rf acc v))))
|
||||||
|
|
||||||
(defn keys [rf]
|
(defn keys [rf]
|
||||||
(kvrf
|
(kvrf
|
||||||
([] (rf))
|
([] (rf))
|
||||||
([acc] (rf acc))
|
([acc] (rf acc))
|
||||||
([acc kv] (rf acc (key kv)))
|
|
||||||
([acc k v] (rf acc k))))
|
([acc k v] (rf acc k))))
|
||||||
|
|
||||||
;; for both map entries and vectors
|
;; for both map entries and vectors
|
||||||
|
|
@ -264,8 +273,6 @@
|
||||||
(kvrf self
|
(kvrf self
|
||||||
([] (rf))
|
([] (rf))
|
||||||
([acc] (let-complete [m m] (rf (core/reduce (fn [acc krf] (krf acc)) acc (core/vals (persistent! m))))))
|
([acc] (let-complete [m m] (rf (core/reduce (fn [acc krf] (krf acc)) acc (core/vals (persistent! m))))))
|
||||||
([acc x]
|
|
||||||
(self acc (key' x) (val' x)))
|
|
||||||
([acc k v]
|
([acc k v]
|
||||||
(let [krf (or (get @m k) (doto (xform (make-rf k)) (->> (vswap! m assoc! k))))
|
(let [krf (or (get @m k) (doto (xform (make-rf k)) (->> (vswap! m assoc! k))))
|
||||||
acc (krf acc v)]
|
acc (krf acc v)]
|
||||||
|
|
@ -295,8 +302,7 @@
|
||||||
(do
|
(do
|
||||||
(vswap! m assoc! k nop-rf)
|
(vswap! m assoc! k nop-rf)
|
||||||
(krf @acc)))
|
(krf @acc)))
|
||||||
acc)))
|
acc)))))))))))
|
||||||
([acc k v] (self acc #?(:clj (clojure.lang.MapEntry. k v) :cljs [k v])))))))))))
|
|
||||||
|
|
||||||
(defn into-by-key
|
(defn into-by-key
|
||||||
"A shorthand for the common case (comp (x/by-key ...) (x/into {}))."
|
"A shorthand for the common case (comp (x/by-key ...) (x/into {}))."
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue