From f4a902a6df5a37ef8a41e8beba7ab01952ce8aea Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 8 May 2026 12:27:03 +0000 Subject: [PATCH] =?UTF-8?q?mk:=20nub-o=20=E2=80=94=20dedupe=20by=20keeping?= =?UTF-8?q?=20the=20last=20occurrence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Walks the list; if the head appears in the tail (membero), drop it and recurse; otherwise keep it and recurse. Result preserves only the *last* occurrence of each value. Caveat: with input like (1 1 1) the membero check succeeds with multiplicity, so multiple (1) answers may emerge — each is shape- identical, but the test deliberately checks every-result-is-(1) rather than asserting answer count. 5 new tests, 526/526 cumulative. --- lib/minikanren/relations.sx | 10 +++++++++- lib/minikanren/tests/nub-o.sx | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 lib/minikanren/tests/nub-o.sx diff --git a/lib/minikanren/relations.sx b/lib/minikanren/relations.sx index a2c8f231..eff42820 100644 --- a/lib/minikanren/relations.sx +++ b/lib/minikanren/relations.sx @@ -80,6 +80,15 @@ ((nullo l) (nullo result)) ((fresh (a d a-result rest-result) (conso a d l) (rel a a-result) (flat-mapo rel d rest-result) (appendo a-result rest-result result)))))) +(define + nub-o + (fn + (l result) + (conde + ((nullo l) (nullo result)) + ((fresh (a d r-rest) (conso a d l) (conde ((membero a d) (nub-o d result)) ((nafc (membero a d)) (conso a r-rest result) (nub-o d r-rest)))))))) + + (define membero (fn @@ -88,7 +97,6 @@ ((fresh (d) (conso x d l))) ((fresh (a d) (conso a d l) (membero x d)))))) - (define not-membero (fn diff --git a/lib/minikanren/tests/nub-o.sx b/lib/minikanren/tests/nub-o.sx new file mode 100644 index 00000000..cffa5159 --- /dev/null +++ b/lib/minikanren/tests/nub-o.sx @@ -0,0 +1,31 @@ +;; lib/minikanren/tests/nub-o.sx — relational dedupe (keep last occurrence). + +(mk-test "nub-o-empty" (run* q (nub-o (list) q)) (list (list))) + +(mk-test + "nub-o-no-duplicates" + (run* q (nub-o (list 1 2 3) q)) + (list (list 1 2 3))) + +(mk-test + "nub-o-with-duplicates" + (run* + q + (nub-o + (list 1 2 1 3 2 4) + q)) + (list (list 1 3 2 4))) + +(mk-test + "nub-o-all-same" + (let + ((res (run* q (nub-o (list 1 1 1) q)))) + (every? (fn (r) (= r (list 1))) res)) + true) + +(mk-test + "nub-o-keeps-last" + (run* q (nub-o (list 1 2 1) q)) + (list (list 2 1))) + +(mk-tests-run!)