feat: persistent singly-linked lists with O(1) conj/cons prepend
Round 3 of the persistent-collections work. Lists were immutable Janet arrays, so conj/cons-prepend was an O(n) copy (O(n^2) to build a list) — a large perf gap vs Clojure's PersistentList. Add src/jolt/plist.janet: an immutable cons-cell list (first/rest/count), same algorithm as Clojure/CLJS/jank PersistentList. conj/cons onto a list now creates an O(1) node that shares the existing list as its tail (no copy), with a cached O(1) count. Repeated conj is O(n) total instead of O(n^2). Hooked plist through first/rest/next/seq/count/peek/pop/nth/empty/empty?, the predicates (list?/seq?/coll?/sequential?), realize-for-iteration, =, coll->cells (concat/lazy), both printers, destructuring, and instance? tags. (list ...) and quoted lists stay arrays; only conj/cons introduce plist nodes, so the surface and risk stay small. Verified: reduce-conj of 200k elements runs in ~0.4s (was effectively O(n^2)). conformance 206/206, features 78/78 (+7 list regressions), jank 120 (+1).
This commit is contained in:
parent
e43f39bc36
commit
a0c9696900
9 changed files with 142 additions and 22 deletions
|
|
@ -1,14 +1,16 @@
|
|||
(use ../src/jolt/types)
|
||||
(use ../src/jolt/pv)
|
||||
(use ../src/jolt/plist)
|
||||
(use ../src/jolt/reader)
|
||||
(use ../src/jolt/evaluator)
|
||||
(use ../src/jolt/core)
|
||||
|
||||
# Normalize jolt collection results to Janet tuples so Janet-level deep=/= can
|
||||
# compare against tuple literals regardless of the (pvec) representation.
|
||||
# compare against tuple literals regardless of the (pvec/plist) representation.
|
||||
(defn norm [x]
|
||||
(cond
|
||||
(pvec? x) (tuple ;(map norm (pv->array x)))
|
||||
(plist? x) (tuple ;(map norm (pl->array x)))
|
||||
(tuple? x) (tuple ;(map norm x))
|
||||
(array? x) (tuple ;(map norm x))
|
||||
x))
|
||||
|
|
|
|||
|
|
@ -118,6 +118,15 @@
|
|||
["for :let" "(quote (1 4 9))" "(for [x [1 2 3] :let [sq (* x x)]] sq)"]
|
||||
["for :while" "(quote (0 1 2))" "(for [x (range 10) :while (< x 3)] x)"]
|
||||
|
||||
### 13b. Persistent lists — O(1) conj-prepend, immutable, value semantics
|
||||
["list conj prepends" "(quote (0 1 2 3))" "(conj (list 1 2 3) 0)"]
|
||||
["list conj multi" "(quote (:c :b :a))" "(conj (quote ()) :a :b :c)"]
|
||||
["list immutable" "true" "(let [l (list 1 2 3) l2 (conj l 9)] (and (= l (quote (1 2 3))) (= l2 (quote (9 1 2 3)))))"]
|
||||
["list? after conj" "true" "(list? (conj (list 1 2) 0))"]
|
||||
["list = vector elts" "true" "(= (quote (1 2 3)) [1 2 3])"]
|
||||
["reduce conj list" "(quote (2 1 0))" "(reduce conj (list) (range 3))"]
|
||||
["cons onto list" "(quote (0 1 2 3))" "(cons 0 (list 1 2 3))"]
|
||||
|
||||
### 14. Janet interop
|
||||
["interop method" "\"v=41\"" "(. {:value 41 :describe (fn [self] (str \"v=\" (:value self)))} describe)"]
|
||||
["interop field" "41" "(.-value {:value 41})"]
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
# Baseline: the number of pass-tests Jolt currently handles. Raise this as Jolt
|
||||
# gains features so regressions (a previously-passing test breaking) are caught.
|
||||
(def baseline 119)
|
||||
(def baseline 120)
|
||||
|
||||
# Tests that loop forever under Jolt's eager evaluation (skipped to avoid hangs;
|
||||
# tracked as known gaps — variadic-recur arity selection and var-quote calls).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue