From bcdd137d6fd1cdf4c3bd14776053910c3a4e22fc Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 7 May 2026 22:19:57 +0000 Subject: [PATCH] apl: ? roll/random + apl-rng-seed! (+4 tests) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apl-rng-state global mutable LCG. apl-rng-seed! for deterministic tests. apl-rng-next! advances state. apl-roll: monadic ?N returns scalar in 1..N (apl-io-relative). apl-monadic-fn dispatches "?" → apl-roll. apl-run "?10" → 8 (with seed 42) apl-run "?100" → in 1..100 --- lib/apl/runtime.sx | 22 ++++++++++++++++++++++ lib/apl/tests/pipeline.sx | 21 +++++++++++++++++++++ lib/apl/transpile.sx | 1 + plans/apl-on-sx.md | 3 ++- 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/apl/runtime.sx b/lib/apl/runtime.sx index ada0d430..a1957d5f 100644 --- a/lib/apl/runtime.sx +++ b/lib/apl/runtime.sx @@ -1004,6 +1004,28 @@ (some (fn (c) (= c 0)) codes) (some (fn (c) (= c (nth e 1))) codes))))) +(define apl-rng-state 12345) + +(define apl-rng-seed! (fn (s) (set! apl-rng-state s))) + +(define + apl-rng-next! + (fn + () + (begin + (set! + apl-rng-state + (mod (+ (* apl-rng-state 1103515245) 12345) 2147483648)) + apl-rng-state))) + +(define + apl-roll + (fn + (arr) + (let + ((n (if (scalar? arr) (first (get arr :ravel)) (first (get arr :ravel))))) + (apl-scalar (+ apl-io (mod (apl-rng-next!) n)))))) + (define apl-cartesian (fn diff --git a/lib/apl/tests/pipeline.sx b/lib/apl/tests/pipeline.sx index 06b4a388..32bb9679 100644 --- a/lib/apl/tests/pipeline.sx +++ b/lib/apl/tests/pipeline.sx @@ -359,3 +359,24 @@ "inline-assign in dfn: f ← {x + x ← ⍵} ⋄ f 8 → 16" (mkrv (apl-run "f ← {x + x ← ⍵} ⋄ f 8")) (list 16)) + +(begin (apl-rng-seed! 42) nil) + +(apl-test + "?10 with seed 42 → 8 (deterministic)" + (mkrv (apl-run "?10")) + (list 8)) + +(apl-test "?10 next call → 5" (mkrv (apl-run "?10")) (list 5)) + +(apl-test + "?100 stays in range" + (let ((v (first (mkrv (apl-run "?100"))))) (and (>= v 1) (<= v 100))) + true) + +(begin (apl-rng-seed! 42) nil) + +(apl-test + "?10 with re-seed 42 → 8 (reproducible)" + (mkrv (apl-run "?10")) + (list 8)) diff --git a/lib/apl/transpile.sx b/lib/apl/transpile.sx index 000164d2..1e69420d 100644 --- a/lib/apl/transpile.sx +++ b/lib/apl/transpile.sx @@ -39,6 +39,7 @@ ((= g "⊖") apl-reverse-first) ((= g "⍋") apl-grade-up) ((= g "⍒") apl-grade-down) + ((= g "?") apl-roll) ((= g "⎕FMT") apl-quad-fmt) ((= g "⎕←") apl-quad-print) (else (error "no monadic fn for glyph"))))) diff --git a/plans/apl-on-sx.md b/plans/apl-on-sx.md index b4155fe4..af805fa9 100644 --- a/plans/apl-on-sx.md +++ b/plans/apl-on-sx.md @@ -202,7 +202,7 @@ Today they are documentation; we paraphrase the algorithms in :dyad/:monad capture env update when their RHS is :assign-expr, threading the new binding into the LHS evaluation. Caveat: ⍵ rebinding is glyph-token, not :name-token — covered for regular names like `a ← ⍳N`.)_ -- [ ] **`?` (random / roll)** — monadic `?N` returns a random integer +- [x] **`?` (random / roll)** — monadic `?N` returns a random integer in 1..N. Used by quicksort.apl for pivot selection. Add `apl-roll` (deterministic seed for tests) + glyph wiring. - [ ] **`apl-run-file path → array`** — read the file from disk, strip @@ -232,6 +232,7 @@ data; format for string templating. _Newest first._ +- 2026-05-07: Phase 9 step 3 — `?N` random / roll. Top-level mutable apl-rng-state with LCG; apl-rng-seed! for deterministic tests; apl-roll wraps as scalar in 1..N. apl-monadic-fn maps "?" → apl-roll. +4 tests (deterministic with seed 42, range checks) - 2026-05-07: Phase 9 step 2 — inline assignment `(2=+⌿0=a∘.|a)/a←⍳30` runs end-to-end. Parser :name clause detects `name ← rhs`, consumes rest as RHS, emits :assign-expr segment. Eval-ast :dyad/:monad capture env update when their right operand is :assign-expr. +5 tests (one-liner primes via inline assign, x+x←7=14, dfn-internal inline assign, etc.) - 2026-05-07: Phase 9 step 1 — compress-as-fn / and ⌿; collect-segments-loop emits (:fn-glyph "/") when slash stands alone; apl-dyadic-fn dispatches / → apl-compress, ⌿ → apl-compress-first (new helper); classic primes idiom now runs end-to-end: `P ← ⍳ 30 ⋄ (2 = +⌿ 0 = P ∘.| P) / P` → primes; queens(8) test removed again (q(8) climbed to 215s on this server load); +5 tests; 501/501 - 2026-05-07: Phase 9 added — make .apl source files run as-written (compress as dyadic /, inline assignment, ? random, apl-run-file, glyph audit, source-as-tests)