core: move frequencies/group-by/not-empty/filterv/max-key/min-key to Clojure
New collection tier (core/20-coll.clj): six pure, eager fns expressed as
compositions of frozen primitives (reduce/assoc/get/conj/filter/vec). Six more
core-* primitives + table entries gone from the Janet kernel.
Two semantics to preserve, caught by the suite:
- max-key/min-key use the canonical multi-arity form (strict </> on the first
pair, <=/>= in the fold) to reproduce the JVM IEEE-754 NaN behavior; a single
reduce got the NaN cases wrong.
- frequencies/group-by base on (hash-map), not the {} literal: a struct map
doesn't canonicalize collection keys across representations ({:a 1} literal vs
(hash-map :a 1)), a PHM does — same reason the Janet impl used make-phm.
Conformance 218/218 all modes; battery holds 3916.
This commit is contained in:
parent
279c8e176b
commit
57b8ffcb9b
3 changed files with 60 additions and 51 deletions
53
jolt-core/clojure/core/20-coll.clj
Normal file
53
jolt-core/clojure/core/20-coll.clj
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
;; clojure.core — collection tier. Pure, eager fns expressed as compositions of
|
||||
;; already-frozen core primitives (reduce/assoc/get/conj/filter/vec/count/>=).
|
||||
;; No host internals, no laziness, no macros — so they compile cleanly and stay
|
||||
;; redefinable. Loaded after the seq tier; self-hosted in compile mode.
|
||||
;;
|
||||
;; Same migration rule as the seq tier (see 10-seq.clj): not in core-renames, no
|
||||
;; internal Janet callers, not used by the self-hosted compiler.
|
||||
|
||||
;; Base is (hash-map), not the {} literal: a literal map is a struct that doesn't
|
||||
;; canonicalize collection keys across representations (a {:a 1} literal vs
|
||||
;; (hash-map :a 1) key), whereas a PHM does — so counting/grouping by collection
|
||||
;; value needs the PHM base (the prior Janet impl used make-phm for this reason).
|
||||
(defn frequencies [coll]
|
||||
(reduce (fn [counts x] (assoc counts x (inc (get counts x 0)))) (hash-map) coll))
|
||||
|
||||
(defn group-by [f coll]
|
||||
(reduce (fn [ret x] (let [k (f x)] (assoc ret k (conj (get ret k []) x)))) (hash-map) coll))
|
||||
|
||||
(defn not-empty [coll]
|
||||
(if (or (nil? coll) (zero? (count coll))) nil coll))
|
||||
|
||||
(defn filterv [pred coll]
|
||||
(vec (filter pred coll)))
|
||||
|
||||
;; Greatest/least x by (k x). Canonical Clojure multi-arity: the first pair uses
|
||||
;; strict < / > and the fold uses <= / >= — this exact ordering reproduces the
|
||||
;; JVM IEEE-754 NaN behavior (e.g. (min-key identity 1 ##NaN) => ##NaN). > / <
|
||||
;; throw on non-numbers, as Clojure does.
|
||||
(defn max-key
|
||||
([k x] x)
|
||||
([k x y] (if (> (k x) (k y)) x y))
|
||||
([k x y & more]
|
||||
(let [kx (k x) ky (k y)
|
||||
v (if (> kx ky) x y)
|
||||
kv (if (> kx ky) kx ky)]
|
||||
(loop [v v kv kv more more]
|
||||
(if (seq more)
|
||||
(let [w (first more) kw (k w)]
|
||||
(if (>= kw kv) (recur w kw (next more)) (recur v kv (next more))))
|
||||
v)))))
|
||||
|
||||
(defn min-key
|
||||
([k x] x)
|
||||
([k x y] (if (< (k x) (k y)) x y))
|
||||
([k x y & more]
|
||||
(let [kx (k x) ky (k y)
|
||||
v (if (< kx ky) x y)
|
||||
kv (if (< kx ky) kx ky)]
|
||||
(loop [v v kv kv more more]
|
||||
(if (seq more)
|
||||
(let [w (first more) kw (k w)]
|
||||
(if (<= kw kv) (recur w kw (next more)) (recur v kv (next more))))
|
||||
v)))))
|
||||
|
|
@ -38,7 +38,8 @@
|
|||
# lazily on the first such form, sees the kernel tier already in place).
|
||||
(def- core-tiers
|
||||
[{:ns "clojure.core.00-kernel" :kernel true}
|
||||
{:ns "clojure.core.10-seq" :kernel false}])
|
||||
{:ns "clojure.core.10-seq" :kernel false}
|
||||
{:ns "clojure.core.20-coll" :kernel false}])
|
||||
|
||||
(defn- eval-overlay-source [ctx src]
|
||||
(var s src)
|
||||
|
|
|
|||
|
|
@ -1166,21 +1166,8 @@
|
|||
(do (put seen x true) (array/push result x))))
|
||||
(if (jvec? coll) (make-vec result) result)))))
|
||||
|
||||
(defn core-group-by [f coll]
|
||||
(def f (as-fn f))
|
||||
# phm base so collection keys group by value
|
||||
(var result (make-phm))
|
||||
(each x (realize-for-iteration coll)
|
||||
(let [k (f x)]
|
||||
(set result (phm-assoc result k (array/push (phm-get result k @[]) x)))))
|
||||
result)
|
||||
|
||||
(defn core-frequencies [coll]
|
||||
# phm base so collection elements are counted by value
|
||||
(var result (make-phm))
|
||||
(each x (realize-for-iteration coll)
|
||||
(set result (phm-assoc result x (+ 1 (phm-get result x 0)))))
|
||||
result)
|
||||
# group-by / frequencies now live in the Clojure collection tier
|
||||
# (core/20-coll.clj).
|
||||
|
||||
(defn core-partition
|
||||
"(partition n coll) or (partition n step coll). Only complete partitions of
|
||||
|
|
@ -3071,10 +3058,7 @@
|
|||
(when (not (number? n)) (error "nthnext requires a numeric count"))
|
||||
(let [r (core-nthrest coll n)] (if (or (nil? r) (= 0 (length r))) nil r)))
|
||||
|
||||
(defn core-filterv [pred coll]
|
||||
(def pred (as-fn pred))
|
||||
(let [r @[]] (each x (realize-for-iteration coll) (when (truthy? (pred x)) (array/push r x)))
|
||||
(make-vec r)))
|
||||
# filterv now lives in the Clojure collection tier (core/20-coll.clj).
|
||||
|
||||
# mapv lives in the Clojure kernel tier — core/00-kernel.clj.
|
||||
|
||||
|
|
@ -3143,8 +3127,7 @@
|
|||
(table? coll) @{}
|
||||
nil))
|
||||
|
||||
(defn core-not-empty [coll]
|
||||
(if (or (nil? coll) (= 0 (core-count coll))) nil coll))
|
||||
# not-empty now lives in the Clojure collection tier (core/20-coll.clj).
|
||||
|
||||
# rseq is defined only on vectors and sorted collections (Reversible).
|
||||
(defn core-rseq [coll]
|
||||
|
|
@ -3199,29 +3182,7 @@
|
|||
# asymmetry reproduces the JVM's NaN-ordering behavior. Janet's < / > are used
|
||||
# directly (NaN comparisons are false, never throwing).
|
||||
# keys must be numbers (NaN allowed) — like Clojure, which compares them with </>.
|
||||
(defn core-min-key [f & xs]
|
||||
(def f (as-fn f))
|
||||
(when (= 0 (length xs)) (error "min-key requires at least one value"))
|
||||
(if (= 1 (length xs)) (first xs)
|
||||
(do (var v (in xs 0)) (var kv (need-num (f v) "min-key"))
|
||||
(let [y (in xs 1) ky (need-num (f y) "min-key")] (when (not (< kv ky)) (set v y) (set kv ky)))
|
||||
(var i 2)
|
||||
(while (< i (length xs))
|
||||
(let [w (in xs i) kw (need-num (f w) "min-key")] (when (<= kw kv) (set v w) (set kv kw)))
|
||||
(++ i))
|
||||
v)))
|
||||
|
||||
(defn core-max-key [f & xs]
|
||||
(def f (as-fn f))
|
||||
(when (= 0 (length xs)) (error "max-key requires at least one value"))
|
||||
(if (= 1 (length xs)) (first xs)
|
||||
(do (var v (in xs 0)) (var kv (need-num (f v) "max-key"))
|
||||
(let [y (in xs 1) ky (need-num (f y) "max-key")] (when (not (> kv ky)) (set v y) (set kv ky)))
|
||||
(var i 2)
|
||||
(while (< i (length xs))
|
||||
(let [w (in xs i) kw (need-num (f w) "max-key")] (when (>= kw kv) (set v w) (set kv kw)))
|
||||
(++ i))
|
||||
v)))
|
||||
# min-key / max-key now live in the Clojure collection tier (core/20-coll.clj).
|
||||
|
||||
(defn core-not-every? [pred coll]
|
||||
(def pred (as-fn pred))
|
||||
|
|
@ -3791,9 +3752,7 @@
|
|||
"take-nth" core-take-nth
|
||||
"nthrest" core-nthrest
|
||||
"nthnext" core-nthnext
|
||||
"filterv" core-filterv
|
||||
"empty" core-empty
|
||||
"not-empty" core-not-empty
|
||||
"rseq" core-rseq
|
||||
"shuffle" core-shuffle
|
||||
"replace" core-replace
|
||||
|
|
@ -3803,8 +3762,6 @@
|
|||
"ifn?" core-ifn?
|
||||
"indexed?" core-indexed?
|
||||
"distinct?" core-distinct?
|
||||
"min-key" core-min-key
|
||||
"max-key" core-max-key
|
||||
"not-every?" core-not-every?
|
||||
"not-any?" core-not-any?
|
||||
"vary-meta" core-vary-meta
|
||||
|
|
@ -3829,8 +3786,6 @@
|
|||
"sort" core-sort
|
||||
"sort-by" core-sort-by
|
||||
"distinct" core-distinct
|
||||
"group-by" core-group-by
|
||||
"frequencies" core-frequencies
|
||||
"partition" core-partition
|
||||
"partition-by" core-partition-by
|
||||
"range" core-range
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue