unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Nicolas Petton <petton.nicolas@gmail.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: Emacs developers <emacs-devel@gnu.org>
Subject: [PATCH] sequence manipulation functions
Date: Fri, 07 Nov 2014 18:35:43 +0100	[thread overview]
Message-ID: <87zjc2dic0.fsf@gmail.com> (raw)
In-Reply-To: <jwv61etqzd5.fsf-monnier+emacs@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 95 bytes --]

Hi,

Here is a patch containing the new version of sequences.el and its
associated test file.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: sequences.diff --]
[-- Type: text/x-patch, Size: 11983 bytes --]

=== modified file 'lisp/ChangeLog'
--- lisp/ChangeLog	2014-11-07 10:49:22 +0000
+++ lisp/ChangeLog	2014-11-07 17:23:09 +0000
@@ -1,3 +1,7 @@
+2014-11-07  Nicolas Petton <petton.nicolas@gmail.com>
+
+	* emacs-lisp/sequences.el: New file.
+
 2014-11-07  Martin Rudalics  <rudalics@gmx.at>
 
 	* cus-start.el (frame-resize-pixelwise): Fix group.

=== added file 'lisp/emacs-lisp/sequences.el'
--- lisp/emacs-lisp/sequences.el	1970-01-01 00:00:00 +0000
+++ lisp/emacs-lisp/sequences.el	2014-11-07 17:28:33 +0000
@@ -0,0 +1,137 @@
+;;; sequences.el --- Sequence manipulation functions  -*- lexical-binding: t -*-
+
+;; Copyright (C) 2014 Free Software Foundation, Inc.
+
+;; Author: Nicolas Petton <petton.nicolas@gmail.com>
+;; Keywords: sequences
+
+;; Maintainer: emacs-devel@gnu.org
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Sequence manipulation functions
+
+;;; Code:
+
+(require 'pcase)
+
+(defun seq-drop (n seq)
+  "Return a subsequence, without its first N elements, of the sequence SEQ."
+  (let ((length (seq-length seq)))
+    (seq-subseq seq (min n length) length)))
+
+(defun seq-take (n seq)
+  "Return a sequence of the first N elements of SEQ."
+  (seq-subseq seq 0 (min n (seq-length seq))))
+
+(defun seq-filter (pred seq)
+  "Return a list filtering with PRED all elements of SEQ."
+  (delq nil (mapcar (lambda (elt)
+                      (and (funcall pred elt) elt))
+                    seq)))
+
+(defun seq-reduce (function seq &optional initial-value)
+  "Reduce FUNCTION across SEQ, starting with INITIAL-VALUE if not nil."
+  (or (listp seq) (setq seq (append seq '())))
+  (let ((acc (or initial-value (if (seq-empty-p seq)
+                                   (funcall function)
+                                 (car seq)))))
+    (mapc (lambda (item)
+            (setq acc (funcall function acc item)))
+          (if initial-value seq (cdr seq)))
+    acc))
+
+(defun seq-some-p (pred seq)
+  "Return any element for which PRED is true in SEQ, nil otherwise."
+  (catch 'seq-some-p-break
+    (mapc (lambda (elt)
+            (when (funcall pred elt)
+              (throw 'seq-some-p-break elt)))
+          seq)
+    nil))
+
+(defun seq-every-p (pred seq)
+  "Return t if (PRED item) is non-nil for all elements of the sequence SEQ."
+  (catch 'seq-every-p-break
+    (mapc (lambda (elt)
+            (or (funcall pred elt)
+                (throw 'seq-every-p-break nil)))
+          seq)
+    t))
+
+(defun seq-empty-p (seq)
+  "Return t if the sequence SEQ is empty, nil otherwise."
+  (= 0 (seq-length seq)))
+
+(defun seq-sort (seq pred)
+  "Return a sorted list of the elements of SEQ compared using PRED."
+  (if (listp seq)
+      (sort (seq-copy seq) pred)
+    (seq-sort (append seq nil) pred)))
+
+(defun seq-contains-p (seq elt &optional testfn)
+  "Return the first element in SEQ that equals to ELT.
+Equality is defined by TESTFN if not nil or by `equal' if nil."
+  (seq-some-p (lambda (e)
+                (funcall (or testfn #'equal) elt e))
+              seq))
+
+(defun seq-uniq (seq &optional testfn)
+  "Return a list of the elements of SEQ with duplicates removed.
+Use TESTFN to compare elements, or `equal' if TESTFN is nil."
+  (let ((result '()))
+    (mapc (lambda (elt)
+            (unless (seq-contains-p result elt testfn)
+              (setq result (cons elt result))))
+          seq)
+    (nreverse result)))
+
+
+(defun seq-subseq (seq start &optional end)
+  "Return the subsequence of SEQ from START to END.
+If END is omitted, it defaults to the length of the sequence.
+If START or END is negative, it counts from the end."
+  (cond ((or (stringp seq) (vectorp seq)) (substring seq start end))
+        ((listp seq)
+         (let (len)
+           (and end (< end 0) (setq end (+ end (setq len (seq-length seq)))))
+           (if (< start 0) (setq start (+ start (or len (setq len (seq-length seq))))))
+           (if (> start 0) (setq seq (nthcdr start seq)))
+           (if end
+               (let ((res nil))
+                 (while (>= (setq end (1- end)) start)
+                   (push (pop seq) res))
+                 (nreverse res))
+             (seq-copy seq))))
+        (t (error "Unsupported sequence: %s" seq))))
+
+(defun seq-concatenate (type &rest seqs)
+  "Concatenate, into a sequence of type TYPE, the argument SEQS.
+\n(fn TYPE SEQUENCE...)"
+  (pcase type
+    (`vector (apply #'vconcat seqs))
+    (`string (apply #'concat seqs))
+    (`list (apply #'append (append seqs '(nil))))
+    (t (error "Not a sequence type name: %s" type))))
+
+(defalias 'seq-copy #'copy-sequence)
+(defalias 'seq-elt #'elt)
+(defalias 'seq-length #'length)
+
+(provide 'sequences)
+;;; sequences.el ends here

=== modified file 'lisp/loadup.el'
--- lisp/loadup.el	2014-10-15 17:32:41 +0000
+++ lisp/loadup.el	2014-11-07 17:17:48 +0000
@@ -93,6 +93,7 @@
 
 (load "version")
 
+(load "emacs-lisp/sequences")
 (load "widget")
 (load "custom")
 (load "emacs-lisp/map-ynp")

=== modified file 'test/ChangeLog'
--- test/ChangeLog	2014-10-28 20:33:12 +0000
+++ test/ChangeLog	2014-11-07 17:20:11 +0000
@@ -1,3 +1,7 @@
+2014-11-07  Nicolas Petton <petton.nicolas@gmail.com>
+
+	* automated/sequences-tests.el: New file.
+
 2014-10-28  Ulf Jasper  <ulf.jasper@web.de>
 
 	* automated/libxml-tests.el: New file.

=== added file 'test/automated/sequences-tests.el'
--- test/automated/sequences-tests.el	1970-01-01 00:00:00 +0000
+++ test/automated/sequences-tests.el	2014-11-07 17:28:47 +0000
@@ -0,0 +1,153 @@
+;;; sequences-tests.el --- Tests for sequences.el
+
+;; Copyright (C) 2014 Free Software Foundation, Inc.
+
+;; Author: Nicolas Petton <petton.nicolas@gmail.com>
+;; Maintainer: emacs-devel@gnu.org
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Tests for sequences.el
+
+;;; Code:
+
+(require 'ert)
+(require 'sequences)
+
+(defmacro with-test-sequences (spec &rest body)
+  "Successively bind VAR to a list, vector, and string built from SEQ.
+Evaluate BODY for each created sequence.
+
+\(fn (var seq) body)"
+  (declare (indent 1) (debug ((symbolp form) body)))
+  (let ((initial-seq (make-symbol "initial-seq")))
+    `(let ((,initial-seq ,(cadr spec))) 
+       ,@(mapcar (lambda (s)
+                   `(let ((,(car spec) (apply (function ,s) ,initial-seq)))
+                      ,@body))
+                 '(list vector string)))))
+
+(defun same-contents-p (seq1 seq2)
+  "Return t if SEQ1 and SEQ2 have the same contents, nil otherwise."
+  (equal (append seq1 '()) (append seq2 '())))
+
+(ert-deftest test-seq-drop ()
+  (with-test-sequences (seq '(1 2 3 4))
+    (should (equal (seq-drop 0 seq) seq))
+    (should (equal (seq-drop 1 seq) (seq-rest seq)))
+    (should (equal (seq-drop 2 seq) (seq-rest (seq-rest seq))))
+    (should (seq-empty-p (seq-drop 4 seq)))
+    (should (seq-empty-p (seq-drop 10 seq))))
+  (with-test-sequences (seq '())
+    (should (seq-empty-p (seq-drop 0 seq)))
+    (should (seq-empty-p (seq-drop 1 seq)))))
+
+(ert-deftest test-seq-take ()
+  (with-test-sequences (seq '(2 3 4 5))
+    (should (seq-empty-p (seq-take 0 seq)))
+    (should (= (seq-length (seq-take 1 seq)) 1))
+    (should (= (seq-first (seq-take 1 seq)) 2))
+    (should (same-contents-p (seq-take 3 seq) '(2 3 4)))
+    (should (equal (seq-take 10 seq) seq))))
+
+(ert-deftest test-seq-filter ()
+  (with-test-sequences (seq '(6 7 8 9 10))
+    (should (equal (seq-filter #'evenp seq) '(6 8 10)))
+    (should (equal (seq-filter #'oddp seq) '(7 9)))
+    (should (equal (seq-filter (lambda (elt) nil) seq) '())))
+  (with-test-sequences (seq '())
+    (should (equal (seq-filter #'evenp seq) '()))))
+
+(ert-deftest test-seq-reduce ()
+  (with-test-sequences (seq '(1 2 3 4))
+    (should (= (seq-reduce #'+ seq) 10))
+    (should (= (seq-reduce #'+ seq 5) 15)))
+  (with-test-sequences (seq '())
+    (should (eq (seq-reduce #'+ seq) 0))
+    (should (eq (seq-reduce #'+ seq 7) 7))))
+
+(ert-deftest test-seq-some-p ()
+  (with-test-sequences (seq '(4 3 2 1))
+    (should (= (seq-some-p #'evenp seq) 4))
+    (should (= (seq-some-p #'oddp seq) 3))
+    (should-not (seq-some-p (lambda (elt) (> elt 10)) seq)))
+  (with-test-sequences (seq '())
+    (should-not (seq-some-p #'oddp seq))))
+
+(ert-deftest test-seq-contains-p ()
+  (with-test-sequences (seq '(3 4 5 6))
+    (should (seq-contains-p seq 3))
+    (should-not (seq-contains-p seq 7)))
+  (with-test-sequences (seq '())
+    (should-not (seq-contains-p seq 3))
+    (should-not (seq-contains-p seq nil))))
+
+(ert-deftest test-seq-every-p ()
+  (with-test-sequences (seq '(43 54 22 1))
+    (should (seq-every-p (lambda (elt) t) seq))
+    (should-not (seq-every-p #'oddp seq))
+    (should-not (seq-every-p #'evenp seq)))
+  (with-test-sequences (seq '(42 54 22 2))
+    (should (seq-every-p #'evenp seq))
+    (should-not (seq-every-p #'oddp seq)))
+  (with-test-sequences (seq '())
+    (should (seq-every-p #'identity seq))
+    (should (seq-every-p #'evenp seq))))
+
+(ert-deftest test-seq-empty-p ()
+  (with-test-sequences (seq '(0))
+    (should-not (seq-empty-p seq)))
+  (with-test-sequences (seq '(0 1 2))
+    (should-not (seq-empty-p seq)))
+  (with-test-sequences (seq '())
+    (should (seq-empty-p seq))))
+
+(ert-deftest test-seq-sort ()
+  (with-test-sequences (seq '(89 32 12 3 5 1 1))
+    (should (equal (seq-sort seq #'<) '(1 1 3 5 12 32 89))))
+  (with-test-sequences (seq '())
+    (should (equal (seq-sort seq #'<) '()))))
+
+(ert-deftest test-seq-uniq ()
+  (with-test-sequences (seq '(2 4 6 8 6 4 3))
+    (should (equal (seq-uniq seq) '(2 4 6 8 3))))
+  (with-test-sequences (seq '(3 3 3 3 3))
+    (should (equal (seq-uniq seq) '(3))))
+  (with-test-sequences (seq '())
+    (should (equal (seq-uniq seq) '()))))
+
+(ert-deftest test-seq-subseq ()
+  (with-test-sequences (seq '(2 3 4 5))
+    (should (equal (seq-subseq seq 0 4) seq))
+    (should (equal (seq-subseq seq 2 4) (seq-rest (seq-rest seq))))
+    (should (same-contents-p (seq-subseq seq 1 3) '(3 4)))
+    (should (same-contents-p (seq-subseq seq 1 -1) '(3 4))))
+  (should (vectorp (seq-subseq [2 3 4 5] 2)))
+  (should (stringp (seq-subseq "foo" 2 3)))
+  (should (listp (seq-subseq '(2 3 4 4) 2 3))))
+
+(ert-deftest test-seq-concatenate ()
+  (with-test-sequences (seq '(2 4 6))
+    (should (equal (seq-concatenate 'string seq [8]) (string 2 4 6 8)))
+    (should (equal (seq-concatenate 'list seq '(8 10)) '(2 4 6 8 10)))
+    (should (equal (seq-concatenate 'vector seq '(8 10)) [2 4 6 8 10]))
+    (should (equal (seq-concatenate 'vector nil '(8 10)) [8 10]))
+    (should (equal (seq-concatenate 'vector seq nil) [2 4 6]))))
+
+(provide 'sequences-tests)
+;;; sequences-tests.el ends here


[-- Attachment #3: Type: text/plain, Size: 1707 bytes --]


- I added the prefix "seq-" to all functions
- I removed "seq-first" and "seq-rest"
- I added "seq-contains-p" and "seq-uniq"
- Both new functions are tested
- I rewrote "seq-some-p"
- "sequences.el" is loaded in "loadup.el"

I'm also willing to document these functions in the Elisp manual.

Regards,
Nico


Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> I understand your point.  Then what do you think about having all other
>> sequence functions prefixed with "seq-"?
>
> Yes, you'll want to add seq-* aliases for things like elt.
>
>> For backward-compatibility, I could keep the current names as aliases,
>> and maybe later obsolete them.
>
> We can start by having seq-elt be an alias for elt (rather than the
> other way around) so as not to need changing anything else.
>
>>> The different of cost between "rest of a list" and "rest of an array" is
>>> so large that merging the two this way is probably not a good idea.
>> Do you mean that the implementation is lacking in some way or that you
>> would remove the "rest" function all together?
>
> I think it's not just an implementation detail but a fundamental
> limitation, so I'd just remove it.  Those who really want it can use
> subseq (or whatever its new name will be, e.g. seq-subseq).
>
> Sebastien Vauban <sva-news@mygooglest.com> writes:
>> Why?  Isn't it still useful for packages in general (and ELPA in
>> particular) so that autoloads files are generated, and these only are
>> what's needed to be loaded... allowing performance gains at startup?
>
> You might as well put a
>
>    ;;;###autoload (require 'seq)
>
> at that point (or add it to loadup.el).
>
>
>         Stefan

-- 
Nicolas Petton
http://nicolas-petton.fr

  reply	other threads:[~2014-11-07 17:35 UTC|newest]

Thread overview: 56+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-11-04 22:17 sequence manipulation functions Nicolas Petton
2014-11-05  1:21 ` Glenn Morris
2014-11-05  9:46   ` Nicolas Petton
2014-11-05  4:58 ` Richard Stallman
2014-11-05  9:45   ` Nicolas Petton
2014-11-06  0:54   ` Daniel Colascione
2014-11-05  9:23 ` Damien Cassou
2014-11-05 18:12   ` Richard Stallman
2014-11-05 21:59     ` Nicolas Petton
2014-11-05 22:40       ` Stefan Monnier
2014-11-06  0:30     ` Richard Stallman
2014-11-05 15:26 ` Stefan Monnier
2014-11-05 15:36   ` Nicolas Petton
2014-11-05 15:52     ` Sebastien Vauban
2014-11-05 18:32     ` Stefan Monnier
2014-11-07 17:35       ` Nicolas Petton [this message]
2014-11-07 17:43         ` [PATCH] " Daniel Colascione
2014-11-10 17:41         ` Stefan Monnier
2014-11-10 22:28           ` Nicolas Petton
2014-11-10 23:12           ` Nicolas Petton
2014-11-11  2:20             ` Stefan Monnier
2014-11-12 17:49               ` Nicolas Petton
2014-11-12 19:12                 ` Bozhidar Batsov
2014-11-12 19:30                   ` Nicolas Petton
2014-11-12 20:28                     ` Stefan Monnier
2014-11-12 20:56                       ` Nicolas Petton
2014-11-12 23:06                         ` Nicolas Petton
2014-11-12 23:17                           ` Nic Ferrier
2014-11-13  1:29                             ` Leo Liu
2014-11-13  5:21                               ` Drew Adams
2014-11-14  5:16                                 ` Leo Liu
2014-11-16 12:52                                   ` Bozhidar Batsov
2014-11-16 14:16                                     ` Nicolas Petton
2014-11-16 17:22                                       ` Drew Adams
2014-11-16 19:13                                       ` Stefan Monnier
2014-11-17  2:52                                         ` Richard Stallman
2014-11-17 11:46                                         ` Nicolas Petton
2014-11-17 13:53                                           ` Stefan Monnier
2014-11-16  7:38                           ` Damien Cassou
2014-11-20 23:16                           ` Stefan Monnier
2014-11-21 12:40                             ` Nicolas Petton
2014-11-21 13:11                               ` Stefan Monnier
2014-11-21 13:28                                 ` Nicolas Petton
2014-11-21 14:44                                   ` Stefan Monnier
2014-11-24 17:49                                     ` Nicolas Petton
2014-11-24 18:01                                       ` Nicolas Petton
2014-11-05 16:25   ` Drew Adams
2014-11-05 17:21     ` Artur Malabarba
2014-11-05 18:27       ` Drew Adams
2014-11-05 17:22     ` Damien Cassou
2014-11-05 18:28       ` Drew Adams
2014-11-05 17:41     ` Nicolas Petton
2014-11-05 18:23       ` Tom Tromey
2014-11-05 18:29         ` Drew Adams
2014-11-05 18:29       ` Drew Adams
2014-11-06  4:39         ` Richard Stallman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87zjc2dic0.fsf@gmail.com \
    --to=petton.nicolas@gmail.com \
    --cc=emacs-devel@gnu.org \
    --cc=monnier@iro.umontreal.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).