Compare commits

..

No commits in common. "master" and "v0.19.3" have entirely different histories.

9 changed files with 253 additions and 406 deletions

4
.gitignore vendored
View file

@ -8,9 +8,6 @@ pom.xml.asc
*.class *.class
/lib/ /lib/
/classes/ /classes/
/bin/
/node_modules/
/.lumo_cache/
/target/ /target/
/checkouts/ /checkouts/
.lein-deps-sum .lein-deps-sum
@ -20,6 +17,5 @@ pom.xml.asc
.nrepl-port .nrepl-port
.cpcache/ .cpcache/
.clj-kondo/*/ .clj-kondo/*/
/cljs-test-runner-out/
# End of https://www.toptal.com/developers/gitignore/api/clojure # End of https://www.toptal.com/developers/gitignore/api/clojure

View file

@ -2,6 +2,8 @@
More transducers and reducing functions for Clojure(script)! More transducers and reducing functions for Clojure(script)!
[![Build Status](https://travis-ci.org/cgrand/xforms.png?branch=master)](https://travis-ci.org/cgrand/xforms)
*Transducers* can be classified in three groups: regular ones, higher-order ones *Transducers* can be classified in three groups: regular ones, higher-order ones
(which accept other transducers as arguments) and aggregators (transducers which emit only 1 item out no matter how many went in). (which accept other transducers as arguments) and aggregators (transducers which emit only 1 item out no matter how many went in).
Aggregators generally only make sense in the context of a higher-order transducer. Aggregators generally only make sense in the context of a higher-order transducer.
@ -35,7 +37,17 @@ In `net.cgrand.xforms.io`:
## Add as a dependency ## Add as a dependency
For specific coordinates see the [Releases](https://github.com/cgrand/xforms/releases) page. ### tools.deps
See the [Releases](/releases) page for coordinates.
### Leiningen
**Note that as of the writing of this, 0.19.3 (and perhaps later versions) are not yet published as jars**
```clj
[net.cgrand/xforms "0.19.2"]
```
## Usage ## Usage
@ -228,19 +240,6 @@ Evaluation count : 24 in 6 samples of 4 calls.
## Changelog ## Changelog
### 0.19.6
* Fix regression in 0.19.5 #54
### 0.19.5
* Support ClojureDart
### 0.19.4
* Fix ClojureScript compilation broken in `0.19.3` #49
* Fix `x/sort` and `x/sort-by` for ClojureScript #40
### 0.19.3 ### 0.19.3
* Add `deps.edn` to enable usage as a [git library](https://clojure.org/guides/deps_and_cli#_using_git_libraries) * Add `deps.edn` to enable usage as a [git library](https://clojure.org/guides/deps_and_cli#_using_git_libraries)

33
bb.edn
View file

@ -2,48 +2,29 @@
:paths ["src" "test"] :paths ["src" "test"]
:tasks :tasks
{:requires ([clojure.string :as str]) {:init
:init
(do (do
(defn kaocha [alias args] (defn kaocha [alias args]
(apply shell "bin/kaocha" alias args)) (apply shell "bin/kaocha" alias args)))
(defn test-cljs [alias args] test-9
(apply clojure (str/join ["-M:test:cljs-test-runner" alias]) args)))
test-clj-9
{:task (kaocha :clj-1-9 *command-line-args*)} {:task (kaocha :clj-1-9 *command-line-args*)}
test-clj-10 test-10
{:task (kaocha :clj-1-10 *command-line-args*)} {:task (kaocha :clj-1-10 *command-line-args*)}
test-clj-11 test-11
{:task (kaocha :clj-1-11 *command-line-args*)} {:task (kaocha :clj-1-11 *command-line-args*)}
test-clj test-clj
{:depends [test-clj-9 test-clj-10 test-clj-11]} {:task (doseq [alias [:clj-1-9 :clj-1-10 :clj-1-11]]
(kaocha alias *command-line-args*))}
test-cljs-9
{:task (test-cljs :clj-1-9 *command-line-args*)}
test-cljs-10
{:task (test-cljs :clj-1-10 *command-line-args*)}
test-cljs-11
{:task (test-cljs :clj-1-11 *command-line-args*)}
test-cljs
{:depends [#_test-cljs-9 test-cljs-10 test-cljs-11]}
test-bb test-bb
{:requires ([clojure.test :as t] {:requires ([clojure.test :as t]
[net.cgrand.xforms-test]) [net.cgrand.xforms-test])
:task (t/run-tests 'net.cgrand.xforms-test)} :task (t/run-tests 'net.cgrand.xforms-test)}
test-all
{:depends [test-bb test-clj test-cljs]}
perf-bb perf-bb
{:requires ([net.cgrand.xforms :as x]) {:requires ([net.cgrand.xforms :as x])
:task :task

View file

@ -1,46 +0,0 @@
(ns build
(:require [clojure.tools.build.api :as b]
[clojure.java.shell :as sh]))
(def lib 'net.cgrand/xforms)
(def version "0.19.6" #_(format "0.0.%s" (b/git-count-revs nil)))
(def class-dir "target/classes")
(def basis (b/create-basis {:project "deps.edn"}))
(def jar-file (format "target/%s-%s.jar" (name lib) version))
(def scm {:connection "scm:git:git://github.com/cgrand/xforms.git"
:developerConnection "scm:git:git://github.com/cgrand/xforms.git"
:url "https://github.com/cgrand/xforms"})
(def extra-pom-data
[[:licenses
[:license
[:name "Eclipse Public License 1.0"]
[:url "https://opensource.org/license/epl-1-0/"]
[:distribution "repo"]]
[:license
[:name "Eclipse Public License 2.0"]
[:url "https://opensource.org/license/epl-2-0/"]
[:distribution "repo"]]]])
(defn clean [_]
(b/delete {:path "target"}))
(defn jar [_]
(b/write-pom {:class-dir class-dir
:lib lib
:version version
:basis basis
:src-dirs ["src"]
:scm (assoc scm :tag (str "v" version))
:pom-data extra-pom-data})
(b/copy-dir {:src-dirs ["src" "resources"]
:target-dir class-dir})
(b/jar {:class-dir class-dir
:jar-file jar-file}))
(defn clojars [_]
(sh/sh
"mvn" "deploy:deploy-file" (str "-Dfile=" jar-file)
;target/classes/META-INF/maven/net.cgrand/xforms/pom.xml
(format "-DpomFile=%s/META-INF/maven/%s/%s/pom.xml"
class-dir (namespace lib) (name lib))
"-DrepositoryId=clojars" "-Durl=https://clojars.org/repo/"))

View file

@ -1,16 +1,10 @@
{:deps {net.cgrand/macrovich {:mvn/version "0.2.2"}} {:deps {net.cgrand/macrovich {:mvn/version "0.2.1"}}
:paths ["src"] :paths ["src"]
:aliases :aliases
{:dev {:dev
{:extra-paths ["dev"]} {:extra-paths ["dev"]}
:cljd
{:extra-deps
{tensegritics/clojuredart
{:git/url "https://github.com/tensegritics/ClojureDart.git"
:sha "ae1b485e84ccc35b122f776dfc7cc62198274701"}}}
:clj-1-9 :clj-1-9
{:extra-deps {:extra-deps
{org.clojure/clojure {:mvn/version "1.9.0"} {org.clojure/clojure {:mvn/version "1.9.0"}
@ -30,16 +24,5 @@
{:extra-paths ["test"]} {:extra-paths ["test"]}
:kaocha :kaocha
{:extra-paths ["test"] {:extra-deps {lambdaisland/kaocha {:mvn/version "1.69.1069"}}
:extra-deps {lambdaisland/kaocha {:mvn/version "1.69.1069"}} :main-opts ["-m" "kaocha.runner"]}}}
:main-opts ["-m" "kaocha.runner"]}
:cljs-test-runner
{:extra-paths ["test"]
:extra-deps {olical/cljs-test-runner {:mvn/version "3.8.0"}}
:main-opts ["-m" "cljs-test-runner.main"]}
:build
{:paths ["."]
:deps {io.github.clojure/tools.build {:git/tag "v0.9.6" :git/sha "8e78bcc"}}
:ns-default build}}}

14
project.clj Normal file
View file

@ -0,0 +1,14 @@
(defproject net.cgrand/xforms "0.19.3"
:description "Extra transducers for Clojure"
:url "https://github.com/cgrand/xforms"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:plugins [[lein-tools-deps "0.4.5"]]
:middleware [lein-tools-deps.plugin/resolve-dependencies-with-deps-edn]
:lein-tools-deps/config {:config-files [:project]}
:profiles
{:provided
{:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.293"]]}})

View file

@ -4,31 +4,18 @@
#?(:cljs (:require-macros #?(:cljs (:require-macros
[net.cgrand.macrovich :as macros] [net.cgrand.macrovich :as macros]
[net.cgrand.xforms :refer [for kvrf let-complete]]) [net.cgrand.xforms :refer [for kvrf let-complete]])
:default (:require [net.cgrand.macrovich :as macros])) :clj (:require [net.cgrand.macrovich :as macros]))
(:refer-clojure :exclude [some reduce reductions into count for partition (:refer-clojure :exclude [some reduce reductions into count for partition
str last keys vals min max drop-last take-last str last keys vals min max drop-last take-last
sort sort-by time #?@(:bb [] :cljd/clj-host [] :clj [satisfies?])]) sort sort-by time satisfies?])
(:require [#?(:cljd cljd.core :clj clojure.core :cljs cljs.core) :as core] (:require [#?(:clj clojure.core :cljs cljs.core) :as core]
[net.cgrand.xforms.rfs :as rf] [net.cgrand.xforms.rfs :as rf]
#?@(:cljd [["dart:collection" :as dart:coll]] :clj [[clojure.core.protocols]] :cljs [])) #?(:clj [clojure.core.protocols]))
#?(:cljd/clj-host
; customize the clj/jvm ns used for macroexpansion
(:host-ns (:require [clojure.core :as core]
[net.cgrand.macrovich :as macros])))
#?(:cljs (:import [goog.structs Queue]))) #?(:cljs (:import [goog.structs Queue])))
(defn- ^:macro-support pair? [x] (and (vector? x) (= 2 (core/count x))))
(def ^:macro-support destructuring-pair?
(let [kw-or-& #(or (keyword? %) (= '& %))]
(fn [x]
(and (pair? x)
(not (kw-or-& (first x)))))))
(macros/deftime (macros/deftime
(defn- ^:macro-support no-user-meta? [x] (defn- no-user-meta? [x]
(= {} (dissoc (or (meta x) {}) :file :line :column :end-line :end-column))) (= {} (dissoc (or (meta x) {}) :file :line :column :end-line :end-column)))
(defmacro unreduced-> (defmacro unreduced->
@ -41,6 +28,12 @@
x# x#
(unreduced-> (-> x# ~expr) ~@exprs))))) (unreduced-> (-> x# ~expr) ~@exprs)))))
(defn- pair? [x] (and (vector? x) (= 2 (core/count x))))
(let [kw-or-& #(or (keyword? %) (= '& %))]
(defn- destructuring-pair? [x]
(and (pair? x)
(not (kw-or-& (first 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."
@ -89,7 +82,7 @@
([~acc] (~rf ~acc)) ([~acc] (~rf ~acc))
([~acc ~binding] ~body))))))) ([~acc ~binding] ~body)))))))
(defn- ^:macro-support arity [[arglist & body :as fn-body]] (defn- arity [[arglist & body :as fn-body]]
(let [[fixargs varargs] (split-with (complement #{'&}) arglist)] (let [[fixargs varargs] (split-with (complement #{'&}) arglist)]
(if (seq varargs) (zipmap (range (core/count fixargs) 4) (repeat fn-body))) (if (seq varargs) (zipmap (range (core/count fixargs) 4) (repeat fn-body)))
{(core/count fixargs) fn-body})) {(core/count fixargs) fn-body}))
@ -105,27 +98,20 @@
(if (destructuring-pair? arg) (if (destructuring-pair? arg)
(let [[karg varg] arg] (let [[karg varg] arg]
`([~acc ~karg ~varg] ~@body)) `([~acc ~karg ~varg] ~@body))
(let [k (gensym "k__") `([~acc k# v#] (let [~arg (macros/case :clj (clojure.lang.MapEntry. k# v#) :cljs [k# v#])] ~@body)))))
v (gensym "v__")
arg-value (macros/case
:clj `(clojure.lang.MapEntry. ~k ~v)
:cljs [k v]
:cljd `(MapEntry ~k ~v))]
`([~acc ~k ~v] (let [~arg ~arg-value] ~@body))))))
(not (arities 2)) (conj (let [[[acc karg varg] & body] (arities 3)] (not (arities 2)) (conj (let [[[acc karg varg] & body] (arities 3)]
`([~acc [~karg ~varg]] ~@body))))] `([~acc [~karg ~varg]] ~@body))))]
`(reify `(reify
#?@(:bb [] ;; babashka currently only supports reify with one Java interface at a time #?@(:bb [] ;; babashka currently only supports reify with one Java interface at a time
:default [~@(macros/case :cljd '[cljd.core/Fn] :clj '[clojure.lang.Fn])]) :default [~@(macros/case :clj '[clojure.lang.Fn])])
KvRfable KvRfable
(~'some-kvrf [this#] this#) (some-kvrf [this#] this#)
~(macros/case :cljs `core/IFn :clj 'clojure.lang.IFn :cljd 'cljd.core/IFn) ~(macros/case :cljs `core/IFn :clj 'clojure.lang.IFn)
~@(core/for [[args & body] fn-bodies] ~@(core/for [[args & body] fn-bodies]
(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 :cljd '-invoke :cljs `core/-invoke :clj 'invoke) `(~(macros/case :cljs `core/-invoke :clj 'invoke) [~name ~@nohint-args] ~@(if (seq rebind) [`(let [~@rebind] ~@body)] body)))))))
[~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]
@ -144,8 +130,12 @@
;; Workaround clojure.core/satisfies? being slow in Clojure ;; Workaround clojure.core/satisfies? being slow in Clojure
;; see https://ask.clojure.org/index.php/3304/make-satisfies-as-fast-as-a-protocol-method-call ;; see https://ask.clojure.org/index.php/3304/make-satisfies-as-fast-as-a-protocol-method-call
#?(:bb nil #?(:cljs
:cljd nil (def satisfies? core/satisfies?)
:bb
(def satisfies? core/satisfies?)
:clj :clj
(defn fast-satisfies?-fn (defn fast-satisfies?-fn
"Ported from https://github.com/clj-commons/manifold/blob/37658e91f836047a630586a909a2e22debfbbfc6/src/manifold/utils.clj#L77-L89" "Ported from https://github.com/clj-commons/manifold/blob/37658e91f836047a630586a909a2e22debfbbfc6/src/manifold/utils.clj#L77-L89"
@ -162,13 +152,10 @@
val) val)
val)))))) val))))))
#?(:cljs #?(:cljs
(defn kvreducible? [coll] (defn kvreducible? [coll]
(satisfies? IKVReduce coll)) (satisfies? core/IKVReduce coll))
:cljd
(defn kvreducible? [coll]
(satisfies? cljd.core/IKVReduce coll))
:clj :clj
(let [satisfies-ikvreduce? #?(:bb #(satisfies? clojure.core.protocols/IKVReduce %) (let [satisfies-ikvreduce? #?(:bb #(satisfies? clojure.core.protocols/IKVReduce %)
@ -181,7 +168,7 @@
(extend-protocol KvRfable (extend-protocol KvRfable
#?(:cljd fallback :clj Object :cljs default) (some-kvrf [_] nil) #?(:clj Object :cljs default) (some-kvrf [_] nil)
#?@(:clj [nil (some-kvrf [_] nil)])) #?@(:clj [nil (some-kvrf [_] nil)]))
(defn ensure-kvrf [rf] (defn ensure-kvrf [rf]
@ -215,8 +202,7 @@
(defn- into-rf [to] (defn- into-rf [to]
(cond (cond
#?(:cljd (satisfies? cljd.core/IEditableCollection to) #?(:clj (instance? clojure.lang.IEditableCollection to)
:clj (instance? clojure.lang.IEditableCollection to)
:cljs (satisfies? IEditableCollection to)) :cljs (satisfies? IEditableCollection to))
(if (map? to) (if (map? to)
(kvrf (kvrf
@ -254,8 +240,7 @@
(defn- without-rf [from] (defn- without-rf [from]
(cond (cond
#?(:cljd (satisfies? cljd.core/IEditableCollection from) #?(:clj (instance? clojure.lang.IEditableCollection from)
:clj (instance? clojure.lang.IEditableCollection from)
:cljs (satisfies? IEditableCollection from)) :cljs (satisfies? IEditableCollection from))
(if (map? from) (if (map? from)
(fn (fn
@ -430,11 +415,7 @@
(comp (apply by-key by-key-args) (into coll))) (comp (apply by-key by-key-args) (into coll)))
(macros/replace (macros/replace
[#?(:cljd {(java.util.ArrayDeque. n) (dart:coll/Queue) [#?(:cljs {(java.util.ArrayDeque. n) (Queue.)
.add .add
.poll .removeFirst
.size .-length})
#?(:cljs {(java.util.ArrayDeque. n) (Queue.)
.add .enqueue .add .enqueue
.poll .dequeue .poll .dequeue
.size .getCount}) .size .getCount})
@ -448,7 +429,7 @@
(if (fn? step-or-xform) (if (fn? step-or-xform)
(partition n n step-or-xform) (partition n n step-or-xform)
(partition n step-or-xform (into [])))) (partition n step-or-xform (into []))))
([#?(:cljd ^int n :default ^long n) step pad-or-xform] ([^long n step pad-or-xform]
(if (fn? pad-or-xform) (if (fn? pad-or-xform)
(let [xform pad-or-xform] (let [xform pad-or-xform]
(fn [rf] (fn [rf]
@ -470,7 +451,7 @@
acc) acc)
acc))))))) acc)))))))
(partition n step pad-or-xform (into [])))) (partition n step pad-or-xform (into []))))
([#?(:cljd ^int n :default ^long n) step pad xform] ([^long n step pad xform]
(fn [rf] (fn [rf]
(let [mxrf (multiplexable rf) (let [mxrf (multiplexable rf)
dq (java.util.ArrayDeque. n) dq (java.util.ArrayDeque. n)
@ -501,7 +482,7 @@
(fn [rf] (fn [rf]
(let ))) (let )))
(defn take-last [#?(:cljd ^int n :default ^long n)] (defn take-last [^long n]
(fn [rf] (fn [rf]
(let [dq (java.util.ArrayDeque. n)] (let [dq (java.util.ArrayDeque. n)]
(fn (fn
@ -514,7 +495,7 @@
(defn drop-last (defn drop-last
([] (drop-last 1)) ([] (drop-last 1))
([#?(:cljd ^int n :default ^long n)] ([^long n]
(fn [rf] (fn [rf]
(let [dq (java.util.ArrayDeque. n) (let [dq (java.util.ArrayDeque. n)
xform (map #(if (identical? dq %) nil %)) xform (map #(if (identical? dq %) nil %))
@ -530,41 +511,21 @@
) )
#?(:cljs
(defn ^:private fn->comparator
"Given a fn that might be boolean valued or a comparator,
return a fn that is a comparator.
Copied from cljs.core: https://github.com/clojure/clojurescript/blob/95c5cf384a128503b072b7b1916af1a1d5c8871c/src/main/cljs/cljs/core.cljs#L2459-L2471"
[f]
(if (= f compare)
compare
(fn [x y]
(let [r (f x y)]
(if (number? r)
r
(if r
-1
(if (f y x) 1 0))))))))
(defn sort (defn sort
([] (sort compare)) ([] (sort compare))
([cmp] ([cmp]
(fn [rf] (fn [rf]
(let [buf #?(:cljd #dart [] :clj (java.util.ArrayList.) :cljs #js [])] (let [buf #?(:clj (java.util.ArrayList.) :cljs #js [])]
(fn (fn
([] (rf)) ([] (rf))
([acc] (rf (core/reduce rf acc (doto buf #?(:cljd (.sort (dart-comparator cmp)) ([acc] (rf (core/reduce rf acc (doto buf #?(:clj (java.util.Collections/sort cmp) :cljs (.sort cmp))))))
:clj (java.util.Collections/sort cmp) ([acc x] (#?(:clj .add :cljs .push) buf x) acc))))))
:cljs (.sort (fn->comparator cmp)))))))
([acc x] (#?(:cljd .add :clj .add :cljs .push) buf x) acc))))))
(defn sort-by (defn sort-by
([kfn] (sort-by kfn compare)) ([kfn] (sort-by kfn compare))
([kfn cmp] ([kfn cmp]
(sort (fn [a b] (sort (fn [a b]
#?(:cljd (cmp (kfn a) (kfn b)) #?(:clj (.compare ^java.util.Comparator cmp (kfn a) (kfn b))
:clj (.compare ^java.util.Comparator cmp (kfn a) (kfn b))
:cljs (cmp (kfn a) (kfn b))))))) :cljs (cmp (kfn a) (kfn b)))))))
(defn reductions (defn reductions
@ -630,8 +591,7 @@
(vreset! vi (let [i (inc i)] (if (= n i) 0 i))) (vreset! vi (let [i (inc i)] (if (= n i) 0 i)))
(rf acc (f (vreset! vwacc (f (invf wacc x') x)))))))))))) (rf acc (f (vreset! vwacc (f (invf wacc x') x))))))))))))
#?(:cljd nil #?(:clj
:clj
(defn iterator (defn iterator
"Iterator transducing context, returns an iterator on the transformed data. "Iterator transducing context, returns an iterator on the transformed data.
Equivalent to (.iterator (eduction xform (iterator-seq src-iterator))) except there's is no buffering on values (as in iterator-seq), Equivalent to (.iterator (eduction xform (iterator-seq src-iterator))) except there's is no buffering on values (as in iterator-seq),
@ -662,8 +622,7 @@
(if (identical? NULL x) nil x)) (if (identical? NULL x) nil x))
(throw (java.util.NoSuchElementException.)))))))) (throw (java.util.NoSuchElementException.))))))))
#?(:cljd nil #?(:clj
:clj
(defn window-by-time (defn window-by-time
"ALPHA "ALPHA
Returns a transducer which computes a windowed accumulator over chronologically sorted items. Returns a transducer which computes a windowed accumulator over chronologically sorted items.
@ -729,11 +688,11 @@
"Count the number of items. Either used directly as a transducer or invoked with two args "Count the number of items. Either used directly as a transducer or invoked with two args
as a transducing context." as a transducing context."
([rf] ([rf]
(let [n #?(:cljd (volatile! 0) :clj (java.util.concurrent.atomic.AtomicLong.) :cljs (volatile! 0))] (let [n #?(:clj (java.util.concurrent.atomic.AtomicLong.) :cljs (atom 0))]
(fn (fn
([] (rf)) ([] (rf))
([acc] (rf (unreduced (rf acc #?(:cljd @n :clj (.get n) :cljs @n))))) ([acc] (rf (unreduced (rf acc #?(:clj (.get n) :cljs @n)))))
([acc _] #?(:cljd (vswap! n inc) :clj (.incrementAndGet n) :cljs (vswap! n inc)) acc)))) ([acc _] #?(:clj (.incrementAndGet n) :cljs (swap! n inc)) acc))))
([xform coll] ([xform coll]
(transduce (comp xform count) rf/last coll))) (transduce (comp xform count) rf/last coll)))
@ -827,8 +786,6 @@
(.addAndGet at (- (System/nanoTime) t)) (swap! at + (- (system-time) t)) (.addAndGet at (- (System/nanoTime) t)) (swap! at + (- (system-time) t))
.size .getCount})] .size .getCount})]
#?(:cljd nil
:default
(defn time (defn time
"Measures the time spent in this transformation and prints the measured time. "Measures the time spent in this transformation and prints the measured time.
tag-or-f may be either a function of 1 argument (measured time in ms) in which case tag-or-f may be either a function of 1 argument (measured time in ms) in which case
@ -865,7 +822,7 @@
(let [t (System/nanoTime) (let [t (System/nanoTime)
r (rf acc x)] r (rf acc x)]
(.addAndGet at (- (System/nanoTime) t)) (.addAndGet at (- (System/nanoTime) t))
r)))))))))) r)))))))))
#_(defn rollup #_(defn rollup
"Roll-up input data along the provided dimensions (which are functions of one input item), "Roll-up input data along the provided dimensions (which are functions of one input item),

View file

@ -6,16 +6,13 @@
[net.cgrand.xforms.rfs :refer [or-instance?]]) [net.cgrand.xforms.rfs :refer [or-instance?]])
:clj (:require [net.cgrand.macrovich :as macros])) :clj (:require [net.cgrand.macrovich :as macros]))
(:require [#?(:clj clojure.core :cljs cljs.core) :as core]) (:require [#?(:clj clojure.core :cljs cljs.core) :as core])
#?(:cljd (:require ["dart:math" :as Math]))
#?(:cljs (:import [goog.string StringBuffer]))) #?(:cljs (:import [goog.string StringBuffer])))
(macros/deftime (macros/deftime
(defmacro ^:private or-instance? [class x y] (defmacro ^:private or-instance? [class x y]
(let [xsym (gensym 'x_)] (let [xsym (gensym 'x_)]
`(let [~xsym ~x] `(let [~xsym ~x]
(if #?(:cljd (dart/is? ~xsym ~class) (if (instance? ~class ~xsym) ~(with-meta xsym {:tag class}) ~y)))))
:default (instance? ~class ~xsym))
~(with-meta xsym {:tag class}) ~y)))))
(declare str!) (declare str!)
@ -31,59 +28,40 @@
:else 0)))) :else 0))))
(defn minimum (defn minimum
([#?(:cljd comparator :clj ^java.util.Comparator comparator :cljs comparator)] ([#?(:clj ^java.util.Comparator comparator :cljs comparator)]
(let [#?@(:cljd [comparator (dart-comparator comparator)] :default [])]
(fn (fn
([] nil) ([] nil)
([x] x) ([x] x)
([a b] (cond ([a b] (cond
(nil? a) b (nil? a) b
(nil? b) a (nil? b) a
(pos? #?(:cljd (comparator a b) (pos? (#?(:clj .compare :cljs cmp) comparator a b)) b
:clj (.compare comparator a b) :else a))))
:cljs (cmp comparator a b))) b ([#?(:clj ^java.util.Comparator comparator :cljs comparator) absolute-maximum]
:else a)))))
([#?(:cljd comparator :clj ^java.util.Comparator comparator :cljs comparator) absolute-maximum]
(let [#?@(:cljd [comparator (dart-comparator comparator)] :default [])]
(fn (fn
([] ::+infinity) ([] ::+infinity)
([x] (if (#?(:clj identical? :cljs keyword-identical?) ::+infinity x) ([x] (if (#?(:clj identical? :cljs keyword-identical?) ::+infinity x)
absolute-maximum absolute-maximum
x)) x))
([a b] ([a b] (if (or (#?(:clj identical? :cljs keyword-identical?) ::+infinity a) (pos? (#?(:clj .compare :cljs cmp) comparator a b))) b a)))))
(if (or
(#?(:clj identical? :cljs keyword-identical?) ::+infinity a)
(pos? #?(:cljd (comparator a b)
:clj (.compare comparator a b)
:cljs (cmp comparator a b))))
b a))))))
(defn maximum (defn maximum
([#?(:cljd comparator :clj ^java.util.Comparator comparator :cljs comparator)] ([#?(:clj ^java.util.Comparator comparator :cljs comparator)]
(let [#?@(:cljd [comparator (dart-comparator comparator)] :default [])]
(fn (fn
([] nil) ([] nil)
([x] x) ([x] x)
([a b] (cond ([a b] (cond
(nil? a) b (nil? a) b
(nil? b) a (nil? b) a
(neg? #?(:cljd (comparator a b) (neg? (#?(:clj .compare :cljs cmp) comparator a b)) b
:clj (.compare comparator a b) :else a))))
:cljs (cmp comparator a b))) b ([#?(:clj ^java.util.Comparator comparator :cljs comparator) absolute-minimum]
:else a)))))
([#?(:cljd comparator :clj ^java.util.Comparator comparator :cljs comparator) absolute-minimum]
(let [#?@(:cljd [comparator (dart-comparator comparator)] :default [])]
(fn (fn
([] ::-infinity) ([] ::-infinity)
([x] (if (#?(:clj identical? :cljs keyword-identical?) ::-infinity x) ([x] (if (#?(:clj identical? :cljs keyword-identical?) ::-infinity x)
absolute-minimum absolute-minimum
x)) x))
([a b] ([a b] (if (or (#?(:clj identical? :cljs keyword-identical?) ::-infinity a) (neg? (#?(:clj .compare :cljs cmp) comparator a b))) b a)))))
(if (or (#?(:clj identical? :cljs keyword-identical?) ::-infinity a)
(neg? #?(:cljd (comparator a b)
:clj (.compare comparator a b)
:cljs (cmp comparator a b))))
b a))))))
(def min (minimum compare)) (def min (minimum compare))
@ -92,24 +70,23 @@
(defn avg (defn avg
"Reducing fn to compute the arithmetic mean." "Reducing fn to compute the arithmetic mean."
([] nil) ([] nil)
([#?(:cljd ^{:tag #/(List? double)} acc :clj ^doubles acc :cljs ^doubles acc)] ([^doubles acc] (when acc (/ (aget acc 1) (aget acc 0))))
(when acc (/ (aget acc 1) (aget acc 0))))
([acc x] (avg acc x 1)) ([acc x] (avg acc x 1))
([#?(:cljd ^{:tag #/(List? double)} acc :clj ^doubles acc :cljs ^doubles acc) x w] ; weighted mean ([^doubles acc x w] ; weighted mean
(let [acc (or acc #?(:cljd (double-array 2) :clj (double-array 2) :cljs #js [0.0 0.0]))] (let [acc (or acc #?(:clj (double-array 3) :cljs #js [0.0 0.0]))]
(doto acc (doto acc
(aset 0 (+ (aget acc 0) w)) (aset 0 (+ (aget acc 0) w))
(aset 1 (+ (aget acc 1) (* w x))))))) (aset 1 (+ (aget acc 1) (* w x)))))))
(defn sd (defn sd
"Reducing fn to compute the standard deviation. Returns 0 if no or only one item." "Reducing fn to compute the standard deviation. Returns 0 if no or only one item."
([] #?(:cljd (double-array 3) :clj (double-array 3) :cljs #js [0.0 0.0 0.0])) ([] #?(:clj (double-array 3) :cljs #js [0.0 0.0 0.0]))
([#?(:cljd ^{:tag #/(List double)} a :default ^doubles a)] ([^doubles a]
(let [s (aget a 0) n (aget a 2)] (let [s (aget a 0) n (aget a 2)]
(if (< 1 n) (if (< 1 n)
(Math/sqrt (/ s (dec n))) (Math/sqrt (/ s (dec n)))
0.0))) 0.0)))
([#?(:cljd ^{:tag #/(List double)} a :default ^doubles a) x] ([^doubles a x]
(let [s (aget a 0) m (aget a 1) n (aget a 2) (let [s (aget a 0) m (aget a 1) n (aget a 2)
d (- x m) d (- x m)
n (inc n) n (inc n)
@ -133,14 +110,9 @@
(defn str! (defn str!
"Like xforms/str but returns a StringBuilder." "Like xforms/str but returns a StringBuilder."
([] (#?(:cljd StringBuffer :clj StringBuilder. :cljs StringBuffer.))) ([] (#?(:clj StringBuilder. :cljs StringBuffer.)))
([sb] (or-instance? #?(:cljd StringBuffer :clj StringBuilder :cljs StringBuffer) sb ([sb] (or-instance? #?(:clj StringBuilder :cljs StringBuffer) sb (#?(:clj StringBuilder. :cljs StringBuffer.) (core/str sb)))) ; the instance? checks are for compatibility with str in case of seeded reduce/transduce.
(#?(:cljd StringBuffer :clj StringBuilder. :cljs StringBuffer.) (core/str sb)))) ([sb x] (.append (or-instance? #?(:clj StringBuilder :cljs StringBuffer) sb (#?(:clj StringBuilder. :cljs StringBuffer.) (core/str sb))) x)))
; the instance? checks are for compatibility with str in case of seeded reduce/transduce.
([sb x] (doto (or-instance?
#?(:cljd StringBuffer :clj StringBuilder :cljs StringBuffer) sb
(#?(:cljd StringBuffer :clj StringBuilder. :cljs StringBuffer.) (core/str sb)))
(#?(:cljd .write :default .append) x))))
(def str (def str
"Reducing function to build strings in linear time. Acts as replacement for clojure.core/str in a reduce/transduce call." "Reducing function to build strings in linear time. Acts as replacement for clojure.core/str in a reduce/transduce call."

View file

@ -97,7 +97,6 @@
#?(:bb nil ;; babashka doesn't currently support calling iterator on range type #?(:bb nil ;; babashka doesn't currently support calling iterator on range type
:clj :clj
(do
(deftest iterator (deftest iterator
(is (true? (.hasNext (x/iterator x/count (.iterator ^java.lang.Iterable (range 5)))))) (is (true? (.hasNext (x/iterator x/count (.iterator ^java.lang.Iterable (range 5))))))
(is (is (= [5] (iterator-seq (x/iterator x/count (.iterator ^java.lang.Iterable (range 5))))))) (is (is (= [5] (iterator-seq (x/iterator x/count (.iterator ^java.lang.Iterable (range 5)))))))
@ -132,7 +131,7 @@
[{:ts 3.25} {:ts 3.5} {:ts 3.75} {:ts 4.0}] ; t = 4.0 [{:ts 3.25} {:ts 3.5} {:ts 3.75} {:ts 4.0}] ; t = 4.0
[{:ts 3.5} {:ts 3.75} {:ts 4.0} {:ts 4.25}] ; t = 4.25 [{:ts 3.5} {:ts 3.75} {:ts 4.0} {:ts 4.25}] ; t = 4.25
[{:ts 3.75} {:ts 4.0} {:ts 4.25} {:ts 4.5}] ; t = 4.5 [{:ts 3.75} {:ts 4.0} {:ts 4.25} {:ts 4.5}] ; t = 4.5
[{:ts 4.0} {:ts 4.25} {:ts 4.5} {:ts 4.75}]]))))) ; t = 4.75 [{:ts 4.0} {:ts 4.25} {:ts 4.5} {:ts 4.75}]])))) ; t = 4.75
(deftest do-not-kvreduce-vectors (deftest do-not-kvreduce-vectors
(is (= {0 nil 1 nil} (x/into {} (x/for [[k v] %] [k v]) [[0] [1]]))) (is (= {0 nil 1 nil} (x/into {} (x/for [[k v] %] [k v]) [[0] [1]])))
@ -140,11 +139,9 @@
(deftest sorting (deftest sorting
(is (= (range 100) (x/into [] (x/sort) (shuffle (range 100))))) (is (= (range 100) (x/into [] (x/sort) (shuffle (range 100)))))
(is (= (range 100) (x/into [] (x/sort <) (shuffle (range 100)))))
(is (= (reverse (range 100)) (x/into [] (x/sort >) (shuffle (range 100))))) (is (= (reverse (range 100)) (x/into [] (x/sort >) (shuffle (range 100)))))
(is (= (sort-by str (range 100)) (x/into [] (x/sort-by str) (shuffle (range 100))))) (is (= (sort-by str (range 100)) (x/into [] (x/sort-by str) (shuffle (range 100)))))
(is (= (sort-by str (comp - compare) (range 100)) (x/into [] (x/sort-by str (comp - compare)) (shuffle (range 100))))) (is (= (sort-by str (comp - compare) (range 100)) (x/into [] (x/sort-by str (comp - compare)) (shuffle (range 100))))))
(is (= (sort-by identity > (shuffle (range 100))) (x/into [] (x/sort-by identity >) (shuffle (range 100))))))
(deftest destructuring-pair? (deftest destructuring-pair?
(let [destructuring-pair? #'x/destructuring-pair?] (let [destructuring-pair? #'x/destructuring-pair?]
@ -158,9 +155,3 @@
'(a b) false '(a b) false
'{foo bar} false '{foo bar} false
'{foo :bar} false))) '{foo :bar} false)))
(defmacro wraps-for-with-no-destructuring []
(x/into [] (x/for [x (range 5)] x)))
(deftest for-in-macro
(is (= [0 1 2 3 4] (wraps-for-with-no-destructuring))))