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)))))
|
||||
Loading…
Add table
Add a link
Reference in a new issue