More kv support, version bump and avg fix

This commit is contained in:
Christophe Grand 2016-06-03 10:49:27 +02:00
parent 4464afffec
commit a10df4554e
3 changed files with 88 additions and 30 deletions

View file

@ -95,9 +95,9 @@ Padding can be achieved using the `pad` function:
;; avg of last 4 items
=> (sequence
(x/window 4 x/avg #(x/avg % (- %2)))
(x/window 4 x/avg #(x/avg %1 %2 -1))
nums)
(11 19/2 17 77/4 12 37/4 79/10 77/12)
(11 19/2 17 77/4 18 37/2 79/4 77/4)
;; min of last 3 items
=> (sequence
@ -163,6 +163,8 @@ Several xforms transducers and transducing contexts leverage `reduce-kv` and `kv
<tr><td>3-arg `into`<br>(transducing context)<td>when `from` is a map<td>when `to` is a map
<tr><td>`by-key`<br>(as a transducer)<td>when is `kfn` and `vfn` are unspecified or `nil`<td>when `pair` is `vector` or unspecified
<tr><td>`by-key`<br>(as a transducing context on values)<td>no<td>no
<tr><td>`juxt`<td>when at least one of the children `rfns` is a kvrf<td>no
<tr><td>`juxt-map`<td>when at least one of the children `rfns` is a kvrf<td>no
</tbody>
<table>

View file

@ -1,4 +1,4 @@
(defproject net.cgrand/xforms "0.2.0"
(defproject net.cgrand/xforms "0.3.0"
:description "Extra transducers for Clojure"
#_#_:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"

View file

@ -154,12 +154,14 @@
(defn- key' [kv] (nth kv 0))
(defn- val' [kv] (nth kv 1))
(defn- noprf "The noop reducing function" ([acc] acc) ([acc _] acc))
(defn- noprf "The noop reducing function" ([acc] acc) ([acc _] acc) ([acc _ _] acc))
(defn- multiplexable
"Creates a multiplexable reducing function (doesn't init or complete the uderlying rf)."
[rf]
(fn ([]) ([acc] acc) ([acc x] (rf acc x)))) ; no init no complete rf
(if-some [rf (some-kvrf rf)]
(kvrf ([]) ([acc] acc) ([acc x] (rf acc x)) ([acc k v] (rf acc k v)))
(fn ([]) ([acc] acc) ([acc x] (rf acc x))))) ; no init no complete rf
(defn by-key
"Returns a transducer which partitions items according to kfn.
@ -252,6 +254,8 @@
Partitions can be padded using #'pad."
; being strict towards partition size implies buffering and avoiding unecessary buffering is part of this
; library goal. So partition won't support it. However a buffer transducer may be an option.
([n]
(partition n n (into [])))
([n xform]
(partition n n xform))
([n step xform]
@ -259,17 +263,11 @@
(defn avg
"Reducing fn to compute the arithmetic mean."
([]
(let [count (volatile! 0)
sum (volatile! 0)]
(fn secret-container
([] (when (pos? @count) (/ @sum @count)))
([n]
(vswap! count inc)
(vswap! sum + n)
secret-container))))
([acc] (acc))
([acc x] (acc x)))
([] (transient [0 0]))
([[n sum]] (/ sum n))
([acc x] (avg acc x 1))
([[n sum :as acc] x w]
(-> acc (assoc! 0 (+ n w)) (assoc! 1 (+ sum (* w x))))))
(defn window
"Returns a transducer which computes an accumulator over the last n items
@ -282,7 +280,10 @@
mutable and 1-arity f to project its state to a value.
If you don't want to see the accumulator until the window is full then you need to
use (drop (dec n)) to remove them."
use (drop (dec n)) to remove them.
If you don't have an inverse function, consider using partition and reduce:
(x/partition 4 (x/reduce rf))"
[n f invf]
(fn [rf]
(let [ring (object-array n)
@ -370,6 +371,21 @@
"Returns a reducing fn which compute all rfns at once and whose final return
value is a vector of the final return values of each rfns."
[& rfns]
(if (some some-kvrf rfns)
(let [rfns (mapv ensure-kvrf rfns)]
(kvrf
([] (mapv #(vector % (volatile! (%))) rfns))
([acc] (mapv (fn [[rf vacc]] (rf (unreduced @vacc))) acc))
([acc x]
(let [some-unreduced (clj/reduce (fn [some-unreduced [rf vacc]]
(when-not (reduced? @vacc) (vswap! vacc rf x) true))
false acc)]
(if some-unreduced acc (reduced acc))))
([acc k v]
(let [some-unreduced (clj/reduce (fn [some-unreduced [rf vacc]]
(when-not (reduced? @vacc) (vswap! vacc rf k v) true))
false acc)]
(if some-unreduced acc (reduced acc))))))
(let [rfns (vec rfns)]
(fn
([] (mapv #(vector % (volatile! (%))) rfns))
@ -378,16 +394,56 @@
(let [some-unreduced (clj/reduce (fn [some-unreduced [rf vacc]]
(when-not (reduced? @vacc) (vswap! vacc rf x) true))
false acc)]
(if some-unreduced acc (reduced acc)))))))
(if some-unreduced acc (reduced acc))))))))
(defn multiplex
[xforms-map]
(fn [rf]
(let [mrf (multiplexable (ensure-kvrf rf))
rfs-map (volatile! (into {} (for [[k xform] %
:let [xform (comp xform (for [x %] [k x]))]]
[k (xform mrf)])
xforms-map))]
(kvrf
([] (rf))
([acc] (rf acc))
([acc x]
(let [acc (reduce-kv
(fn [acc tag rf]
(let [acc (rf acc x)]
(if (reduced? acc)
(do (vswap! rfs-map dissoc tag) (rf @acc))
acc)))
acc @rfs-map)]
(if (zero? (clj/count @rfs-map))
(reduced acc)
acc)))
([acc k v]
(let [acc (reduce-kv
(fn [acc tag rf]
(let [acc (rf acc k v)]
(if (reduced? acc)
(do (vswap! rfs-map dissoc tag) (rf @acc))
acc)))
acc @rfs-map)]
(if (zero? (clj/count @rfs-map))
(reduced acc)
acc)))))))
(defn juxt-map
[& key-rfns]
(let [f (apply juxt (take-nth 2 (next key-rfns)))
keys (vec (take-nth 2 key-rfns))]
(if-some [f (some-kvrf f)]
(kvrf
([] (f))
([acc] (zipmap keys (f acc)))
([acc x] (f acc x))
([acc k v] (f acc k v)))
(fn
([] (f))
([acc] (zipmap keys (f acc)))
([acc x] (f acc x)))))
([acc x] (f acc x))))))
(defn first
"Reducing function that returns the first value or nil if none."