diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index 22651af..3d9760a 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -225,3 +225,14 @@ ;; namespace-munge: Clojure namespace name -> legal Java package name (- -> _). (defn namespace-munge [s] (apply str (map (fn [c] (if (= c \-) \_ c)) (seq (str s))))) + +;; reduce-kv over a map (k v) or vector (index v). Both branches go through reduce, +;; so reduced short-circuits — and the vector path indexes correctly. (The prior +;; Janet version saw a pvec as a table and folded over its internal keys; it also +;; ignored reduced.) nil folds to init, matching Clojure. +(defn reduce-kv [f init coll] + (cond + (vector? coll) (reduce (fn [acc i] (f acc i (nth coll i))) init (range (count coll))) + (map? coll) (reduce (fn [acc k] (f acc k (get coll k))) init (keys coll)) + (nil? coll) init + :else (throw (str "reduce-kv not supported on: " coll)))) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index b11fe0e..5f4d311 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -1296,13 +1296,7 @@ (defn cstep [i] (fn [] @[(in c (% i (length c))) (cstep (+ i 1))])) (make-lazy-seq (cstep 0)))))) -(defn core-reduce-kv [f init m] - (var acc init) - (cond - (phm? m) (each e (phm-entries m) (set acc (f acc (in e 0) (in e 1)))) - (or (struct? m) (table? m)) (each k (keys m) (set acc (f acc k (get m k)))) - (indexed? m) (do (var i 0) (each x m (set acc (f acc i x)) (++ i)))) - acc) +# reduce-kv now lives in the Clojure collection tier (core/20-coll.clj). # pop is defined only on stacks (vectors -> last end, lists -> front); Clojure # throws on sets/maps/seqs/strings/scalars. (peek lives in the Clojure kernel @@ -2765,7 +2759,6 @@ "keep-indexed" core-keep-indexed "map-indexed" core-map-indexed "cycle" core-cycle - "reduce-kv" core-reduce-kv "pop" core-pop "trampoline" core-trampoline "format" core-format diff --git a/test/integration/conformance-test.janet b/test/integration/conformance-test.janet index 8b2e438..e387ad2 100644 --- a/test/integration/conformance-test.janet +++ b/test/integration/conformance-test.janet @@ -108,6 +108,7 @@ ["subvec" "[2 3]" "(subvec [1 2 3 4 5] 1 3)"] ["subvec to-end" "[3 4 5]" "(subvec [1 2 3 4 5] 2)"] ["reduce-kv" "{:a 2 :b 3}" "(reduce-kv (fn [m k v] (assoc m k (inc v))) {} {:a 1 :b 2})"] + ["reduce-kv vector idx" "(quote ([0 :a] [1 :b]))" "(reduce-kv (fn [a i v] (conj a [i v])) [] [:a :b])"] ### ---- iterating maps yields entries ---- ["map over map" "true" "(= #{1 2} (set (map val {:a 1 :b 2})))"] diff --git a/test/spec/sequences-spec.janet b/test/spec/sequences-spec.janet index 58675f0..dcad62b 100644 --- a/test/spec/sequences-spec.janet +++ b/test/spec/sequences-spec.janet @@ -56,6 +56,9 @@ ["reduce single no init" "5" "(reduce + [5])"] ["reduced short-circuits" "3" "(reduce (fn [a x] (if (> a 2) (reduced a) (+ a x))) 0 [1 2 3 4 5])"] ["reduce-kv" "6" "(reduce-kv (fn [a k v] (+ a v)) 0 {:a 1 :b 2 :c 3})"] + ["reduce-kv on vector" "[[0 :a] [1 :b]]" "(reduce-kv (fn [a i v] (conj a [i v])) [] [:a :b])"] + ["reduce-kv honors reduced" "[:a]" "(reduce-kv (fn [a i v] (if (= i 1) (reduced a) (conj a v))) [] [:a :b :c])"] + ["reduce-kv on nil" "0" "(reduce-kv (fn [a k v] (+ a v)) 0 nil)"] ["reductions" "[1 3 6]" "(reductions + [1 2 3])"] ["mapcat" "[1 1 2 2]" "(mapcat (fn [x] [x x]) [1 2])"] ["keep" "[1 3]" "(keep (fn [x] (if (odd? x) x nil)) [1 2 3 4])"]