Subject: bug#67456: closed (Re: bug#67456: [PATCH] seq.el: Add functions for mapping over subsequences)

From: [email-masked] (GNU bug Tracking System)
Date: Tue, 25 Feb 2025 02:40:02 +0000

Your bug report

#67456: [PATCH] seq.el: Add functions for mapping over subsequences

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
67456: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=67456
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#67456: [PATCH] seq.el: Add functions for mapping over subsequences
From: Stefan Kangas <[email-masked]>
To: Okamsn <[email-masked]>
Cc: [email-masked], [email-masked], [email-masked]
Date: Mon, 24 Feb 2025 20:39:31 -0600
List-Id: <debbugs-submit.debbugs.gnu.org>

Okamsn <[email-masked]> writes:

> Stefan Kangas wrote:
>> Okamsn <[email-masked]> writes:
>>
>>> Augusto Stoffel wrote:
>>>> This operation has quadratic complexity for anything other than regular
>>>> linked lists.  I'm not sure it's a good idea to add it to a generic
>>>> sequence library...
>>>
>>>
>>> Thank you for the feedback.
>>>
>>> Do you know whether there is a better way to implement the idea for
>>> arrays? Is there a way to apply a function to a portion of the array
>>> without copying the sub-sequences, if that is what you mean?
>>
>> Hmm, it seems like this issue got stuck due to problems with algorithmic
>> complexity.  Performance is a real issue in seq.el, so I think we should
>> solve that before we consider installing anything.
>>
>> Are you still working on this?
>
> I am no longer working on this.  For some things, I opted to use the
> Stream package from ELPA, which now better supports arrays.  Although it
> has the same problem when first traversing a seq.el sequence, it avoids
> recreating things for the sub-sequence.
>
> I am fine with this bug being closed.

Thanks, I guess that means that it's unlikely that we'll make more
progress here.  I'm therefore closing this bug report.

Subject: [PATCH] seq.el: Add functions for mapping over subsequences
From: Okamsn <[email-masked]>
To: [email-masked], Nicolas Petton <[email-masked]>
Date: Sun, 26 Nov 2023 17:17:35 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

Hello,

The attached features work like `cl-maplist` and `cl-mapl`, applying 
functions to a sequence and to the remaining parts of a sequence.  For 
example, `(seq-mapsub #'identity [1 2 3])` returns `([1 2 3] [2 3] [3])`.

The patch adds a `seq-mapsub`, `seq-dosub`, and a `seq-doseqsub`, 
similar to `seq-map`, `seq-do`, and `seq-doseq`, respectively.

I was looking for an equivalent for vectors of `cl-maplist`, `cl-mapl`, 
and `cl-loop`'s `for VAR on LIST`, and think that these would be useful 
additions.

To get the sub-sequences, the code uses `seq-rest` and stops when the 
returned sub-sequence is empty according to `seq-empty-p`.  This is 
similar to how I would do it for a list, using `cdr` and `null`, but is 
that a good way to do it for arrays and other sequences?

Thank you.

From 2f07383470b90009c78b0459ee09fcc513e0e12f Mon Sep 17 00:00:00 2001
From: Earl Hyatt <[email-masked]>
Date: Sun, 26 Nov 2023 11:20:28 -0500
Subject: [PATCH] Create seq functions for mapping over subsequences.

These functions are similar to 'cl-maplist' and 'cl-mapl'.

* lisp/emacs-lisp/seq.el (seq-mapsub, seq-dosub, seq-doseqsub):
Add functions for mapping over sub-sequences, like in 'cl-maplist',
similar to the existing 'seq-map', 'seq-do', and 'seq-doseq'.

* test/lisp/emacs-lisp/seq-tests.el (test-seq-mapsub, test-seq-dosub)
(test-seq-doseqsub): Add tests for the above functions.

* doc/lispref/sequences.texi (Sequence Functions): Describe the
functions.
---
 doc/lispref/sequences.texi        | 27 ++++++++++++++++++++
 lisp/emacs-lisp/seq.el            | 41 +++++++++++++++++++++++++++++++
 test/lisp/emacs-lisp/seq-tests.el | 36 +++++++++++++++++++++++++++
 3 files changed, 104 insertions(+)

diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi
index c9c6bb31350..e42f84be2b5 100644
--- a/doc/lispref/sequences.texi
+++ b/doc/lispref/sequences.texi
@@ -594,6 +594,15 @@ Sequence Functions
 @var{sequence}.
 @end defun
 
+@defun seq-dosub function sequence
+  This function applies @var{function} to @var{sequence} and its
+sub-sequences in turn (presumably for side effects), and returns
+@var{sequence}.  The first sequence used is @var{sequence}, the second
+sequence used is everything in @var{sequence} after the first element,
+the third sequence used is everything after the second element, and so
+on.
+@end defun
+
 @defun seq-map function sequence
   This function returns the result of applying @var{function} to each
 element of @var{sequence}.  The returned value is a list.
@@ -610,6 +619,19 @@ Sequence Functions
 @end example
 @end defun
 
+@defun seq-mapsub function sequence
+  This function returns the result of applying @var{function} to
+@var{sequence} and its sub-sequences, like in @code{seq-dosub}.  The
+returned value is a list.
+
+@example
+@group
+(seq-mapsub #'identity [1 2 3])
+@result{} ([1 2 3] [2 3] [3])
+@end group
+@end example
+@end defun
+
 @defun seq-map-indexed function sequence
   This function returns the result of applying @var{function} to each
 element of @var{sequence} and its index within @var{seq}.  The
@@ -1152,6 +1174,11 @@ Sequence Functions
 primarily useful for side-effects.
 @end defmac
 
+@defmac seq-doseqsub (var sequence) body@dots{}
+  This macro is like @code{seq-doseq}, except that it iterates
+over the sequence and its sub-sequences, like in @code{seq-mapsub}.
+@end defmac
+
 @anchor{seq-let}
 @defmac seq-let var-sequence val-sequence body@dots{}
 @cindex sequence destructuring
diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el
index 346250c1d35..5b9eb29a3e2 100644
--- a/lisp/emacs-lisp/seq.el
+++ b/lisp/emacs-lisp/seq.el
@@ -235,6 +235,47 @@ seq-mapn
       (setq sequences (seq-map #'cdr sequences)))
     (nreverse result)))
 
+(cl-defgeneric seq-mapsub (function sequence)
+  "Return the result of applying FUNCTION to SEQUENCE and its sub-sequences.
+
+The first sequence is the entire sequence, the second sequence
+is everything after the first element, the third sequence
+is everything after the second element, and so on."
+  (let ((result nil))
+    (while (not (seq-empty-p sequence))
+      (push (funcall function sequence)
+            result)
+      (setq sequence (seq-rest sequence)))
+    (nreverse result)))
+
+(cl-defgeneric seq-dosub (function sequence)
+  "Loop over SEQUENCE and its sub-sequences.
+
+Presumably, FUNCTION has useful side effects.
+Return SEQUENCE.
+
+The first sequence is the entire sequence, the second sequence
+is everything after the first element, the third sequence
+is everything after the second element, and so on."
+  (let ((orig sequence))
+    (while (not (seq-empty-p sequence))
+      (funcall function sequence)
+      (setq sequence (seq-rest sequence)))
+    orig))
+
+(defmacro seq-doseqsub (spec &rest body)
+  "Loop over SEQUENCE, evaluating BODY with VAR bound to its sub-sequences.
+
+The first sequence is the entire sequence, the second sequence
+is everything after the first element, the third sequence
+is everything after the second element, and so on.
+
+\(fn (VAR SEQUENCE) BODY...)"
+  (declare (indent 1) (debug ((symbolp form &optional form) body)))
+  `(seq-dosub (lambda (,(car spec))
+                ,@body)
+              ,(cadr spec)))
+
 (cl-defgeneric seq-drop (sequence n)
   "Remove the first N elements of SEQUENCE and return the resulting sequence.
 The result is a sequence of the same type as SEQUENCE.
diff --git a/test/lisp/emacs-lisp/seq-tests.el b/test/lisp/emacs-lisp/seq-tests.el
index 71ff991c215..1c117d06974 100644
--- a/test/lisp/emacs-lisp/seq-tests.el
+++ b/test/lisp/emacs-lisp/seq-tests.el
@@ -121,6 +121,42 @@ test-seq-do-indexed
       (should (equal (seq-elt result 1) '(5 1)))
       (should (equal (seq-elt result 2) '(4 0))))))
 
+(ert-deftest test-seq-mapsub ()
+  (should (equal (seq-mapsub #'identity [1 2 3])
+                 '([1 2 3] [2 3] [3])))
+  (should (equal (seq-mapsub #'identity '(1 2 3))
+                 '((1 2 3) (2 3) (3)))))
+
+(ert-deftest test-seq-dosub ()
+  (should (equal (seq-dosub #'identity [1 2 3])
+                 [1 2 3]))
+  (should (equal (seq-dosub #'identity '(1 2 3))
+                 '(1 2 3)))
+  (should (equal (let ((res (list)))
+                   (seq-dosub (lambda (x)
+                                (push x res))
+                              [1 2 3])
+                   (nreverse res))
+                 '([1 2 3] [2 3] [3])))
+  (should (equal (let ((res (list)))
+                   (seq-dosub (lambda (x)
+                                (push x res))
+                              '(1 2 3))
+                   (nreverse res))
+                 '((1 2 3) (2 3) (3)))))
+
+(ert-deftest test-seq-doseqsub ()
+  (should (equal (let ((res (list)))
+                   (seq-doseqsub (x [1 2 3])
+                     (push x res))
+                   (nreverse res))
+                 '([1 2 3] [2 3] [3])))
+  (should (equal (let ((res (list)))
+                   (seq-doseqsub (x '(1 2 3))
+                     (push x res))
+                   (nreverse res))
+                 '((1 2 3) (2 3) (3)))))
+
 (ert-deftest test-seq-filter ()
   (with-test-sequences (seq '(6 7 8 9 10))
     (should (equal (seq-filter #'test-sequences-evenp seq) '(6 8 10)))
-- 
2.34.1