From 3489c9f131ef1d19040c3367bd926d7737604dab Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 6 May 2026 21:14:49 +0000 Subject: [PATCH] =?UTF-8?q?apl:=20each=20f=C2=A8=20monadic=20+=20dyadic=20?= =?UTF-8?q?(+14=20tests,=20139/139)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/apl/runtime.sx | 35 +++++++++++++++ lib/apl/tests/operators.sx | 89 +++++++++++++++++++++++++++++++++++++- plans/apl-on-sx.md | 3 +- 3 files changed, 125 insertions(+), 2 deletions(-) diff --git a/lib/apl/runtime.sx b/lib/apl/runtime.sx index ea252013..e26dcf1f 100644 --- a/lib/apl/runtime.sx +++ b/lib/apl/runtime.sx @@ -961,3 +961,38 @@ (rest col)))) (range 0 inner-size))) (range 0 first-dim))))))))) + +(define + apl-each + (fn + (f arr) + (let + ((shape (get arr :shape)) (ravel (get arr :ravel))) + (make-array + shape + (map (fn (x) (disclose (f (apl-scalar x)))) ravel))))) + +(define + apl-each-dyadic + (fn + (f a b) + (cond + ((and (scalar? a) (scalar? b)) (apl-scalar (disclose (f a b)))) + ((scalar? a) + (make-array + (get b :shape) + (map (fn (x) (disclose (f a (apl-scalar x)))) (get b :ravel)))) + ((scalar? b) + (make-array + (get a :shape) + (map (fn (x) (disclose (f (apl-scalar x) b))) (get a :ravel)))) + (else + (if + (equal? (get a :shape) (get b :shape)) + (make-array + (get a :shape) + (map + (fn (x y) (disclose (f (apl-scalar x) (apl-scalar y)))) + (get a :ravel) + (get b :ravel))) + (error "length error: shape mismatch")))))) diff --git a/lib/apl/tests/operators.sx b/lib/apl/tests/operators.sx index 8b2be5cc..9c17911b 100644 --- a/lib/apl/tests/operators.sx +++ b/lib/apl/tests/operators.sx @@ -157,4 +157,91 @@ (apl-test "scan-first max\\ matrix col-wise running max" (rv (apl-scan-first apl-max (make-array (list 3 2) (list 3 1 4 1 5 9)))) - (list 3 1 4 1 5 9)) \ No newline at end of file + (list 3 1 4 1 5 9)) + +(apl-test + "each negate vector" + (rv (apl-each apl-neg-m (make-array (list 3) (list 1 2 3)))) + (list -1 -2 -3)) + +(apl-test + "each negate vector preserves shape" + (sh (apl-each apl-neg-m (make-array (list 3) (list 1 2 3)))) + (list 3)) + +(apl-test + "each reciprocal vector" + (rv (apl-each apl-recip (make-array (list 3) (list 1 2 4)))) + (list 1 (/ 1 2) (/ 1 4))) + +(apl-test + "each abs vector" + (rv (apl-each apl-abs (make-array (list 4) (list -1 2 -3 4)))) + (list 1 2 3 4)) + +(apl-test "each scalar" (rv (apl-each apl-neg-m (apl-scalar 5))) (list -5)) + +(apl-test + "each scalar shape" + (sh (apl-each apl-neg-m (apl-scalar 5))) + (list)) + +(apl-test + "each negate matrix shape" + (sh (apl-each apl-neg-m (make-array (list 2 3) (list 1 2 3 4 5 6)))) + (list 2 3)) + +(apl-test + "each negate matrix values" + (rv (apl-each apl-neg-m (make-array (list 2 3) (list 1 2 3 4 5 6)))) + (list -1 -2 -3 -4 -5 -6)) + +(apl-test + "each-dyadic scalar+scalar" + (rv (apl-each-dyadic apl-add (apl-scalar 3) (apl-scalar 4))) + (list 7)) + +(apl-test + "each-dyadic scalar+vector" + (rv + (apl-each-dyadic + apl-add + (apl-scalar 10) + (make-array (list 3) (list 1 2 3)))) + (list 11 12 13)) + +(apl-test + "each-dyadic vector+scalar" + (rv + (apl-each-dyadic + apl-add + (make-array (list 3) (list 1 2 3)) + (apl-scalar 10))) + (list 11 12 13)) + +(apl-test + "each-dyadic vector+vector" + (rv + (apl-each-dyadic + apl-add + (make-array (list 3) (list 1 2 3)) + (make-array (list 3) (list 10 20 30)))) + (list 11 22 33)) + +(apl-test + "each-dyadic mul matrix+matrix shape" + (sh + (apl-each-dyadic + apl-mul + (make-array (list 2 2) (list 1 2 3 4)) + (make-array (list 2 2) (list 5 6 7 8)))) + (list 2 2)) + +(apl-test + "each-dyadic mul matrix+matrix values" + (rv + (apl-each-dyadic + apl-mul + (make-array (list 2 2) (list 1 2 3 4)) + (make-array (list 2 2) (list 5 6 7 8)))) + (list 5 12 21 32)) \ No newline at end of file diff --git a/plans/apl-on-sx.md b/plans/apl-on-sx.md index 8920b8c5..1140acef 100644 --- a/plans/apl-on-sx.md +++ b/plans/apl-on-sx.md @@ -75,7 +75,7 @@ Core mapping: ### Phase 4 — operators (THE SHOWCASE) - [x] Reduce `f/` (last axis), `f⌿` (first axis) — including `∧/`, `∨/`, `+/`, `×/`, `⌈/`, `⌊/` - [x] Scan `f\`, `f⍀` -- [ ] Each `f¨` — applies `f` to each scalar/element +- [x] Each `f¨` — applies `f` to each scalar/element - [ ] Outer product `∘.f` — `1 2 3 ∘.× 1 2 3` ↦ multiplication table - [ ] Inner product `f.g` — `+.×` is matrix multiply - [ ] Commute `f⍨` — `f⍨ x` ↔ `x f x`, `x f⍨ y` ↔ `y f x` @@ -118,6 +118,7 @@ data; format for string templating. _Newest first._ +- 2026-05-06: Phase 4 step 3 — each f¨ (monadic apl-each + dyadic apl-each-dyadic); scalar broadcast both sides; 139/139 tests - 2026-05-06: Phase 4 step 2 — scan f\ (last axis) + f⍀ (first axis); apl-scan/apl-scan-first; 125/125 tests - 2026-05-06: Phase 4 step 1 — reduce f/ (last axis) + f⌿ (first axis); apl-reduce/apl-reduce-first; 110/110 tests - 2026-05-06: Phase 3 complete — membership ∊, dyadic ⍳ (index-of), without ~ (index-of returns nil for not-found); 94/94 tests