feat(stdlib): sorting toolkit — sort_by/sort_desc_by/sort_with + stable merge sort (#11)
Some checks are pending
docs / build-and-deploy (push) Waiting to run
Some checks are pending
docs / build-and-deploy (push) Waiting to run
Grow List sorting from a numeric-only insertion sort into a small, game-friendly toolkit that sorts records and query results by a key or a full comparator, stably and in O(n log n). - List.sort_by(s, keyfn) ascending by a key (draw order, price) - List.sort_desc_by(s, keyfn) descending (leaderboards) - List.sort_with(s, cmpfn) full cmp(a,b)->int comparator (multi-field) Comparators/keys are passed as named top-level functions rather than lambdas, so the toolkit ships without waiting on closures (#1). Engine: a stable bottom-up merge sort. emit_takeright is the single place stability is decided ("take the right run's head only on a strict win" -> equal keys keep prior order). List.sort becomes a hybrid: insertion sort for n<32, merge sort above; both stable, so output is unchanged. Key functions must return an integer-ish type; record slices hold pointer elements, so the key/comparator receives the record pointer. Tests: selfhost/tests/sort.ludic (scalar large-n, sort_by, sort_desc_by, stability, sort_with). Docs: list-sort_by/desc_by/with + updated list-sort. All suites green (28 self-host / 46 test / 29 test-tools); reseeded, C-free bootstrap fixpoint holds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b5455cd550
commit
4aa2012231
8 changed files with 9622 additions and 7747 deletions
74
selfhost/tests/sort.ludic
Normal file
74
selfhost/tests/sort.ludic
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
program T {
|
||||
property P { key: int = 0, tag: int = 0 }
|
||||
function pkey(p: P) -> int { return p.key }
|
||||
function cmp_key(a: P, b: P) -> int { return a.key - b.key }
|
||||
entry {
|
||||
# 1) scalar large-n path (merge sort): 40 descending -> ascending
|
||||
let xs = new []int
|
||||
for i in 0 .. 40 { push(xs, 39 - i) }
|
||||
List.sort(xs)
|
||||
print(xs[0]) # 0
|
||||
print(xs[39]) # 39
|
||||
print(xs[20]) # 20
|
||||
|
||||
# 2) sort_by ascending on records by a field
|
||||
let ps = new []P
|
||||
let a = new P
|
||||
a.key = 3
|
||||
push(ps, a)
|
||||
let b = new P
|
||||
b.key = 1
|
||||
push(ps, b)
|
||||
let c = new P
|
||||
c.key = 2
|
||||
push(ps, c)
|
||||
List.sort_by(ps, pkey)
|
||||
print(ps[0].key) # 1
|
||||
print(ps[1].key) # 2
|
||||
print(ps[2].key) # 3
|
||||
|
||||
# 3) sort_desc_by (leaderboard)
|
||||
List.sort_desc_by(ps, pkey)
|
||||
print(ps[0].key) # 3
|
||||
print(ps[2].key) # 1
|
||||
|
||||
# 4) stability: equal keys keep their prior order
|
||||
let qs = new []P
|
||||
let q0 = new P
|
||||
q0.key = 1
|
||||
q0.tag = 10
|
||||
push(qs, q0)
|
||||
let q1 = new P
|
||||
q1.key = 1
|
||||
q1.tag = 20
|
||||
push(qs, q1)
|
||||
let q2 = new P
|
||||
q2.key = 0
|
||||
q2.tag = 30
|
||||
push(qs, q2)
|
||||
let q3 = new P
|
||||
q3.key = 1
|
||||
q3.tag = 40
|
||||
push(qs, q3)
|
||||
List.sort_by(qs, pkey)
|
||||
print(qs[0].tag) # 30 (key 0)
|
||||
print(qs[1].tag) # 10 (key 1, original order)
|
||||
print(qs[2].tag) # 20
|
||||
print(qs[3].tag) # 40
|
||||
|
||||
# 5) sort_with (full comparator, ascending)
|
||||
let rs = new []P
|
||||
let r0 = new P
|
||||
r0.key = 5
|
||||
push(rs, r0)
|
||||
let r1 = new P
|
||||
r1.key = 2
|
||||
push(rs, r1)
|
||||
let r2 = new P
|
||||
r2.key = 8
|
||||
push(rs, r2)
|
||||
List.sort_with(rs, cmp_key)
|
||||
print(rs[0].key) # 2
|
||||
print(rs[2].key) # 8
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue