erlang: lists keylist BIFs (keyfind/keymember/keydelete/keyreplace/keystore/keytake/keysort) (809/809)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m3s

Adds the tuple-keyed list family to lib/erlang/lists-ext.sx: act on
first match, key compare via == (er-equal?), non-tuples/short tuples
pass through. keysort/2 reuses the stable merge sort + full term
order. keytake/3 returns {value, Tuple, Rest} | false. All seven
registered through the er-register-builtin-bifs! wrapper so they
survive mid-run registry resets. lists_ext suite 17 -> 38.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-30 13:28:57 +00:00
parent 355a482dfe
commit 343c508939
5 changed files with 203 additions and 6 deletions

View File

@@ -74,3 +74,83 @@
(er-lx-test "usort/1 length after dedup"
(erlang-eval-ast "length(lists:usort([4,4,2,2,1,1,4]))") 3)
;; ── lists:keyfind/3 ───────────────────────────────────────────────
(er-lx-test "keyfind hit"
(erlang-eval-ast "element(2, lists:keyfind(b, 1, [{a,1},{b,2},{c,3}]))") 2)
(er-lx-test "keyfind first match only"
(erlang-eval-ast "element(2, lists:keyfind(a, 1, [{a,1},{a,9}]))") 1)
(er-lx-test "keyfind miss returns false"
(er-lx-nm "lists:keyfind(z, 1, [{a,1},{b,2}])") "false")
(er-lx-test "keyfind on second element"
(er-lx-nm "element(1, lists:keyfind(2, 2, [{a,1},{b,2}]))") "b")
(er-lx-test "keyfind skips short tuples"
(er-lx-nm "lists:keyfind(x, 2, [{x},{y,x}]) =:= {y,x}") "true")
;; ── lists:keymember/3 ─────────────────────────────────────────────
(er-lx-test "keymember true"
(er-lx-nm "lists:keymember(b, 1, [{a,1},{b,2}])") "true")
(er-lx-test "keymember false"
(er-lx-nm "lists:keymember(z, 1, [{a,1},{b,2}])") "false")
;; ── lists:keydelete/3 ─────────────────────────────────────────────
(er-lx-test "keydelete removes first match"
(er-lx-nm "lists:keydelete(b, 1, [{a,1},{b,2},{c,3}]) =:= [{a,1},{c,3}]") "true")
(er-lx-test "keydelete only first"
(er-lx-nm "lists:keydelete(a, 1, [{a,1},{a,2},{b,3}]) =:= [{a,2},{b,3}]") "true")
(er-lx-test "keydelete miss unchanged"
(er-lx-nm "lists:keydelete(z, 1, [{a,1},{b,2}]) =:= [{a,1},{b,2}]") "true")
;; ── lists:keyreplace/4 ────────────────────────────────────────────
(er-lx-test "keyreplace hit"
(er-lx-nm
"lists:keyreplace(b, 1, [{a,1},{b,2},{c,3}], {b,99}) =:= [{a,1},{b,99},{c,3}]")
"true")
(er-lx-test "keyreplace miss unchanged"
(er-lx-nm
"lists:keyreplace(z, 1, [{a,1}], {z,0}) =:= [{a,1}]") "true")
;; ── lists:keystore/4 ──────────────────────────────────────────────
(er-lx-test "keystore replaces existing"
(er-lx-nm
"lists:keystore(b, 1, [{a,1},{b,2}], {b,99}) =:= [{a,1},{b,99}]") "true")
(er-lx-test "keystore appends when absent"
(er-lx-nm
"lists:keystore(z, 1, [{a,1},{b,2}], {z,0}) =:= [{a,1},{b,2},{z,0}]") "true")
;; ── lists:keytake/3 ───────────────────────────────────────────────
(er-lx-test "keytake hit value tag"
(er-lx-nm "element(1, lists:keytake(b, 1, [{a,1},{b,2},{c,3}]))") "value")
(er-lx-test "keytake hit tuple"
(er-lx-nm
"element(2, lists:keytake(b, 1, [{a,1},{b,2},{c,3}])) =:= {b,2}") "true")
(er-lx-test "keytake hit rest"
(er-lx-nm
"element(3, lists:keytake(b, 1, [{a,1},{b,2},{c,3}])) =:= [{a,1},{c,3}]") "true")
(er-lx-test "keytake miss false"
(er-lx-nm "lists:keytake(z, 1, [{a,1}])") "false")
;; ── lists:keysort/2 ───────────────────────────────────────────────
(er-lx-test "keysort by element 1"
(er-lx-nm
"lists:keysort(1, [{c,3},{a,1},{b,2}]) =:= [{a,1},{b,2},{c,3}]") "true")
(er-lx-test "keysort by element 2"
(er-lx-nm
"lists:keysort(2, [{a,3},{b,1},{c,2}]) =:= [{b,1},{c,2},{a,3}]") "true")
(er-lx-test "keysort stable on equal keys"
(er-lx-nm
"lists:keysort(1, [{a,1},{a,2},{a,3}]) =:= [{a,1},{a,2},{a,3}]") "true")