summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eval.c2
-rw-r--r--lib.c28
-rw-r--r--lib.h2
-rw-r--r--tests/012/seq.tl20
-rw-r--r--txr.186
5 files changed, 138 insertions, 0 deletions
diff --git a/eval.c b/eval.c
index d5cc61c8..42a7d67f 100644
--- a/eval.c
+++ b/eval.c
@@ -7280,6 +7280,7 @@ void eval_init(void)
reg_fun(intern(lit("proper-list-p"), user_package), proper_list_p_f);
}
reg_fun(intern(lit("length-list"), user_package), func_n1(length_list));
+ reg_fun(intern(lit("length-list-<"), user_package), func_n2(length_list_lt));
reg_fun(intern(lit("mapcar"), user_package), func_n1v(mapcarv));
reg_fun(intern(lit("mapcar*"), user_package), func_n1v(lazy_mapcarv));
@@ -7739,6 +7740,7 @@ void eval_init(void)
reg_fun(intern(lit("str-seq"), user_package), func_n1(str_seq));
reg_fun(intern(lit("length"), user_package), length_f);
reg_fun(intern(lit("len"), user_package), length_f);
+ reg_fun(intern(lit("length-<"), user_package), func_n2(length_lt));
reg_fun(intern(lit("empty"), user_package), func_n1(empty));
reg_fun(intern(lit("copy"), user_package), func_n1(copy));
reg_fun(intern(lit("sub"), user_package), func_n3o(sub, 1));
diff --git a/lib.c b/lib.c
index 0fd695a5..515d5439 100644
--- a/lib.c
+++ b/lib.c
@@ -4857,6 +4857,19 @@ val length_list(val list)
return bn_len;
}
+val length_list_lt(val list, val len)
+{
+ val self = lit("length-list-lt");
+ cnum le = c_num(len, self);
+
+ while (consp(list) && le > 0) {
+ list = cdr(list);
+ le--;
+ }
+
+ return tnil(le > 0);
+}
+
static val length_proper_list(val list)
{
cnum len = 0;
@@ -13147,6 +13160,21 @@ val length(val seq)
}
}
+val length_lt(val seq, val len)
+{
+ switch (type(seq)) {
+ case NIL:
+ return if3(plusp(len), t, nil);
+ case CONS:
+ case LCONS:
+ return length_list_lt(seq, len);
+ case LSTR:
+ return length_str_lt(seq, len);
+ default:
+ return lt(length(seq), len);
+ }
+}
+
val sub(val seq, val from, val to)
{
switch (type(seq)) {
diff --git a/lib.h b/lib.h
index 3d49e7c9..b86e85bd 100644
--- a/lib.h
+++ b/lib.h
@@ -909,6 +909,7 @@ val listp(val obj);
val endp(val obj);
val proper_list_p(val obj);
val length_list(val list);
+val length_list_lt(val list, val len);
val getplist(val list, val key);
val getplist_f(val list, val key, loc found);
val memp(val key, val plist);
@@ -1052,6 +1053,7 @@ val stringp(val str);
val lazy_stringp(val str);
val length_str(val str);
val coded_length(val str);
+val length_lt(val seq, val len);
const wchar_t *c_str(val str, val self);
val search_str(val haystack, val needle, val start_num, val from_end);
val search_str_tree(val haystack, val tree, val start_num, val from_end);
diff --git a/tests/012/seq.tl b/tests/012/seq.tl
index 95754c67..4b84787d 100644
--- a/tests/012/seq.tl
+++ b/tests/012/seq.tl
@@ -645,3 +645,23 @@
(flatcar* '(() b . c)) (nil b c)
(flatcar* '((()) b . c)) (nil b c)
(flatcar* '(((a)) b . c)) (a b c))
+
+(mtest
+ (length-< nil 0) nil
+ (length-< nil 1) t
+ (length-< '(a) 1) nil
+ (length-< '(a) 2) t
+ (length-< '(a . b) 1) nil
+ (length-< '(a . b) 2) t)
+
+(mtest
+ (length-< "" 0) nil
+ (length-< "" 1) t
+ (length-< "a" 1) nil
+ (length-< "a" 2) t)
+
+(mtest
+ (length-< #() 0) nil
+ (length-< #() 1) t
+ (length-< #(a) 1) nil
+ (length-< #(a) 2) t)
diff --git a/txr.1 b/txr.1
index e4b2b41c..ecc15ca2 100644
--- a/txr.1
+++ b/txr.1
@@ -22520,6 +22520,62 @@ Common Lisp does not allow the argument to be an atom, except
for the empty list
.codn nil .
+.coNP Function @ length-list-<
+.synb
+.mets (length-list-< < list << len )
+.syne
+.desc
+The
+.code length-list-<
+function determines whether the length of
+.metn list ,
+is less than the integer
+.metn len .
+
+The expression
+
+.verb
+ (length-list-< x y)
+.brev
+
+is similar to, but usefully different from
+
+.verb
+ (< (length-list x) y)
+.brev
+
+because
+.code length-list-<
+is required to only traverses
+.meta list
+far enough to be able to determine the return value.
+If the end of the list is reached before
+.meta len
+conses are encountered, the function returns
+.codn t ,
+otherwise if
+.code len
+conses are encountered, the function terminates immediately and returns
+.codn nil .
+
+The
+.code length-list-<
+function is therefore safe to use with infinite lazy lists and circular
+lists, for which
+.code length
+would not terminate.
+
+Note: there is more generic function
+.code length-<
+which works with efficiently with different kinds of sequences.
+
+Note: the
+.code length-list-<
+is useful in situations when a decision must be made between two
+algorithms based on the length of one or more input lists.
+The decision can be made without wastefully performing a full pass over the
+input lists to measure their length.
+
.coNP Function @ copy-cons
.synb
.mets (copy-cons << cons )
@@ -34033,6 +34089,36 @@ An attempt to calculate the length of infinite lazy lists will not terminate.
Iterable objects representing infinite ranges, such as integers and characters
are invalid arguments.
+.coNP Function @ length-<
+.synb
+.mets (length-< < iterable << len )
+.syne
+.desc
+The
+.code length-<
+function efficiently determines whether
+.mono
+.meti (length << iterable)
+.onom
+is less than the integer value
+.metn len .
+In cases when
+.meta iterable
+would have to be fully traversed in order to measure its length, the
+.code length-<
+function avoids this traversal, by making use of the functions
+.code length-str-<
+or
+.code length-list-<
+as appropriate.
+
+Note: this function is useful when a decision must be made between
+two algorithms, depending on whether the length is less than a certain
+small constant. It is also safe on lazy, infinite sequences and
+circular lists, for which
+.code length
+will fail to terminate.
+
.coNP Function @ empty
.synb
.mets (empty << iterable )