summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2025-03-27 19:31:08 -0700
committerKaz Kylheku <kaz@kylheku.com>2025-03-27 19:31:08 -0700
commit48a7a911475fbeccc2e1fe078a5db1544909096b (patch)
treee78b01798fbfcdbfa2d5e34ac3242927cb451c0e
parentb169d4699786762efc57e9a4b4ef72838002066e (diff)
downloadtxr-48a7a911475fbeccc2e1fe078a5db1544909096b.tar.gz
txr-48a7a911475fbeccc2e1fe078a5db1544909096b.tar.bz2
txr-48a7a911475fbeccc2e1fe078a5db1544909096b.zip
New function: remove.
We need a remove function that doesn't have an equality suffix, analogous to member, pos, count. * eval.c (eval_init): Register remove intrinsic. * lib.[ch] (remov): New function. Named this way to avoid clashing with the ISO C remove function in <stdlib.h>. * tests/012/seq.tl: New tests. * txr.1: Documented.
-rw-r--r--eval.c1
-rw-r--r--lib.c36
-rw-r--r--lib.h1
-rw-r--r--tests/012/seq.tl24
-rw-r--r--txr.148
5 files changed, 105 insertions, 5 deletions
diff --git a/eval.c b/eval.c
index 64970dfe..11545a12 100644
--- a/eval.c
+++ b/eval.c
@@ -7480,6 +7480,7 @@ void eval_init(void)
reg_fun(intern(lit("rmember"), user_package), func_n4o(rmember, 2));
reg_fun(intern(lit("member-if"), user_package), func_n3o(member_if, 2));
reg_fun(intern(lit("rmember-if"), user_package), func_n3o(rmember_if, 2));
+ reg_fun(intern(lit("remove"), user_package), func_n5o(remov, 2));
reg_fun(intern(lit("remq"), user_package), func_n3o(remq, 2));
reg_fun(intern(lit("remql"), user_package), func_n3o(remql, 2));
reg_fun(intern(lit("remqual"), user_package), func_n3o(remqual, 2));
diff --git a/lib.c b/lib.c
index 8aeda9ab..68634b4f 100644
--- a/lib.c
+++ b/lib.c
@@ -3506,6 +3506,42 @@ static val rem_impl(val (*eqfun)(val, val), val self,
return seq_finish(&bu);
}
+val remov(val obj, val seq, val testfun_in, val keyfun_in, val mapfun_in)
+{
+ val self = lit("remove");
+ val testfun = default_null_arg(testfun_in);
+ val keyfun = default_null_arg(keyfun_in);
+ val mapfun = default_null_arg(mapfun_in);
+ seq_iter_t it;
+ seq_build_t bu;
+ val elem;
+
+ seq_iter_init(self, &it, seq);
+ seq_build_init(self, &bu, seq);
+
+ if (!testfun && !keyfun && !mapfun) {
+ while (seq_get(&it, &elem)) {
+ if (!equal(obj, elem))
+ seq_add(&bu, elem);
+ }
+ } else {
+ while (seq_get(&it, &elem)) {
+ val key = keyfun ? funcall1(keyfun, elem) : elem;
+ if ((testfun && !funcall2(testfun, obj, key)) ||
+ (!testfun && !equal(obj, key)))
+ {
+ if (mapfun == keyfun)
+ elem = key;
+ else if (mapfun)
+ elem = funcall1(mapfun, elem);
+ seq_add(&bu, elem);
+ }
+ }
+ }
+
+ return seq_finish(&bu);
+}
+
static val rem_if_impl(val pred, val seq, val keyfun_in, val mapfun_in,
val self)
{
diff --git a/lib.h b/lib.h
index 2d2b5e4e..977fbddf 100644
--- a/lib.h
+++ b/lib.h
@@ -886,6 +886,7 @@ val member(val item, val list, val testfun, val keyfun);
val rmember(val item, val list, val testfun, val keyfun);
val member_if(val pred, val list, val key);
val rmember_if(val pred, val list, val key);
+val remov(val item, val seq, val testfun_in, val keyfun_in, val mapfun_in);
val remq(val obj, val seq, val keyfun);
val remql(val obj, val seq, val keyfun);
val remqual(val obj, val seq, val keyfun);
diff --git a/tests/012/seq.tl b/tests/012/seq.tl
index b1063513..d6c9af64 100644
--- a/tests/012/seq.tl
+++ b/tests/012/seq.tl
@@ -587,6 +587,30 @@
#(1 9 25 49 81 121 169 225 289 361)))
(mtest
+ (remove 3 #(3)) #()
+ (remove 2 #(3)) #(3)
+ (remove 2 #(3 2 3)) #(3 3)
+ (remove 2 #(2 2 2)) #())
+
+(mtest
+ (remove "b" '#"a b c") #"a c"
+ (remove "B" '#"a b c") #"a b c"
+ [remove "B" '#"a b c" tf] nil
+ [remove "B" '#"a b c" : upcase-str] #"a c"
+ [remove "B" '#"a b c" tf upcase-str] nil
+ [remove "B" '#"a b c" equal upcase-str upcase-str] #"A C"
+ [remove "b" '#"a b c" equal : upcase-str] #"A C")
+
+(mtest
+ (remove #\b "abc") "ac"
+ (remove #\B "abc") "abc"
+ [remove #\B "abc" tf] ""
+ [remove #\B "abc" : chr-toupper] "ac"
+ [remove #\B "abc" tf chr-toupper] ""
+ [remove #\B "abc" equal chr-toupper chr-toupper] "AC"
+ [remove #\b "abc" equal : chr-toupper] "AC")
+
+(mtest
(flatten '()) ()
(flatten '(nil)) ()
(flatten '(a)) (a)
diff --git a/txr.1 b/txr.1
index 1e2bed5d..42774995 100644
--- a/txr.1
+++ b/txr.1
@@ -36576,6 +36576,7 @@ is compared to
.coNP Functions @, remove-if @, keep-if @, separate @ remove-if* and @ keep-if*
.synb
+.mets (remove < key < sequence >> [testfun >> [ keyfun <> [ mapfun ]]])
.mets (remove-if < predfun < sequence >> [ keyfun <> [ mapfun ]])
.mets (keep-if < predfun < sequence >> [ keyfun <> [ mapfun ]])
.mets (separate < predfun < sequence >> [ keyfun <> [ mapfun ]])
@@ -36583,15 +36584,26 @@ is compared to
.mets (keep-if* < predfun < sequence >> [ keyfun <> [ mapfun ]])
.syne
.desc
+Functions in this group perform filtering of sequences,
+either retaining or rejecting items which satisfy a condition,
+or separating the two into two sequences.
+
The
.code remove-if
-function produces a sequence whose contents are those of
+function produces a sequence whose elements are taken from
.meta sequence
-but with those elements removed which satisfy
-.metn predfun .
-Those elements which are not removed appear in the same order.
+and which are are projected through
+.metn mapfun ,
+if that argument is specified, or else taken as-is.
+Elements which satisfy
+.meta predfun
+do not appear in the output sequence.
+
+Those elements which are retained appear in the same order.
The result sequence may share substructure with the input sequence,
-and may even be the same sequence object if no items are removed.
+and may even be the same sequence object if no items are removed,
+and no mapping is specified by
+.metn mapfun .
The optional
.meta keyfun
@@ -36625,6 +36637,32 @@ is called, or whether the result of
is used.
The
+.code remove
+function is similar to
+.codn remove-if .
+The condition identifying elements to be removed is specified not as a
+.meta predfun
+predicate, but as a
+.meta key
+element: elements are removed if they are the same as
+.metn key .
+Each element is projected to a key value through
+.metn keyfun ,
+if that is specified, and then compared to the input
+.meta key
+using
+.metn testfun .
+If
+.meta testfun
+is omitted, the
+.code equal
+function is used.
+The retained items are projected through
+.meta mapfun
+just like under
+.codn remove-if .
+
+The
.code keep-if
function is exactly like
.codn remove-if ,