Commit cf077327 authored by Nicolas Petton's avatar Nicolas Petton Committed by Stefan Monnier

* lisp/emacs-lisp/seq.el: New file.

* doc/lispref/sequences.texi (Seq Library): Add documentation for seq.el.
* test/automated/seq-tests.el: New file.
parent 005b86c0
2014-12-16 Nicolas Petton <petton.nicolas@gmail.com>
* sequences.texi (Seq Library): Add documentation for seq.el.
2014-12-15 Alan Mackenzie <acm@muc.de>
"Advice" is a mass noun. Amend text accordingly.
......@@ -16,21 +20,21 @@
2014-12-09 Lars Magne Ingebrigtsen <larsi@gnus.org>
* files.texi (Contents of Directories): Document
directory-files-recursively.
* files.texi (Contents of Directories):
Document directory-files-recursively.
2014-12-04 Eli Zaretskii <eliz@gnu.org>
* display.texi (Bidirectional Display): Document
'buffer-substring-with-bidi-context'.
* display.texi (Bidirectional Display):
Document 'buffer-substring-with-bidi-context'.
* text.texi (Buffer Contents): Mention
'buffer-substring-with-bidi-context' with a cross-reference.
* text.texi (Buffer Contents):
Mention 'buffer-substring-with-bidi-context' with a cross-reference.
2014-12-02 Eli Zaretskii <eliz@gnu.org>
* display.texi (Bidirectional Display): Document
'bidi-find-overridden-directionality'.
* display.texi (Bidirectional Display):
Document 'bidi-find-overridden-directionality'.
2014-11-29 Paul Eggert <eggert@cs.ucla.edu>
......@@ -51,7 +55,7 @@
* processes.texi (Network Security): Made into its own section and
fleshed out.
(Network Security): Mention more NSM variables.
(Processes): Moved the Network Security Manager stuff to the Emacs
(Processes): Move the Network Security Manager stuff to the Emacs
manual.
2014-11-23 Lars Magne Ingebrigtsen <larsi@gnus.org>
......@@ -67,8 +71,8 @@
2014-11-18 Leo Liu <sdl.web@gmail.com>
* functions.texi (Advising Named Functions): Document
define-advice.
* functions.texi (Advising Named Functions):
Document define-advice.
2014-11-17 Paul Eggert <eggert@cs.ucla.edu>
......@@ -233,8 +237,8 @@
2014-08-29 Dmitry Antipov <dmantipov@yandex.ru>
* lists.texi (Functions that Rearrange Lists): Remove
description of sort ...
* lists.texi (Functions that Rearrange Lists):
Remove description of sort ...
* sequences.texi (Sequence Functions): ... and generalize
it for sequences. Add an example.
......@@ -382,8 +386,8 @@
2014-05-15 Dmitry Antipov <dmantipov@yandex.ru>
* lists.texi (Building Cons Cells and Lists): Remove
description of `reverse' and `'nreverse' to generalize them...
* lists.texi (Building Cons Cells and Lists):
Remove description of `reverse' and `'nreverse' to generalize them...
* sequences.texi (Sequences): ...for sequences here.
2014-05-14 Glenn Morris <rgm@gnu.org>
......@@ -13525,7 +13529,7 @@
1998-01-30 Richard Stallman <rms@psilocin.gnu.org>
* Makefile (SHELL): Defined.
* Makefile (SHELL): Define.
1998-01-27 Richard Stallman <rms@psilocin.gnu.org>
......
......@@ -419,6 +419,366 @@ See @code{documentation} in @ref{Accessing Documentation}, for a
useful example of @code{sort}.
@end defun
@cindex sequence functions in seq
@cindex seq library
The @file{seq} library provides the following additional sequence
manipulation macros and functions, prefixed with @code{seq-}. To use
them, you need to load the @file{seq} library first.
All functions defined in the @code{seq} library are free of
side-effects, meaning that sequence(s) passed as argument(s) to
functions defined in @code{seq} are not modified.
@defun seq-drop seq n
This function returns a sequence of all but the first @var{n}
elements of the sequence @var{seq}.
@var{seq} may be a list, vector or string and @var{n} must be an
integer. The result is the same type of sequence as @var{seq}.
If @var{n} is a negative integer or zero, @var{seq} is returned.
@example
@group
(seq-drop [1 2 3 4 5 6] 3)
@result{} [4 5 6]
@end group
@group
(seq-drop "hello world" -4)
@result{} "hello world"
@end group
@end example
@end defun
@defun seq-take seq n
This function returns a sequence of the first @var{n} elements of
@var{seq}.
@var{seq} may be a list, vector or string and @var{n} must be an
integer. The result is the same type of sequence as @var{seq}.
If @var{n} is a negative integer or zero, an empty sequence is returned.
@example
@group
(seq-take '(1 2 3 4) 3)
@result{} (1 2 3)
@end group
@group
(seq-take [1 2 3 4] 0)
@result{} []
@end group
@end example
@end defun
@defun seq-take-while pred seq
This function returns a sub-sequence of the successive elements of
@var{seq} for which calling @code{pred} with that element returns
non-nil.
@var{pred} must be a one-argument function and @var{seq} may be a
list, vector or string. The result is the same type of sequence as
@var{seq}.
If evaluating @var{pred} with the first element of @var{seq} as argument
returns @code{nil}, an empty sequence is returned.
@example
@group
(seq-take-while (lambda (elt) (> elt 0)) '(1 2 3 -1 -2))
@result{} (1 2 3)
@end group
@group
(seq-take-while (lambda (elt) (> elt 0)) [-1 4 6])
@result{} []
@end group
@end example
@end defun
@defun seq-drop-while pred seq
This function returns a sub-sequence of @var{seq} from the first
element for which calling @var{pred} with that element returns
@code{nil}.
@var{pred} must be a one-argument function and @var{seq} may be a
list, vector or string. The result is the same type of sequence as
@var{seq}.
If evaluating @var{pred} with every element of @var{seq} returns
@code{nil}, @var{seq} is returned.
@example
@group
(seq-drop-while (lambda (elt) (> elt 0)) '(1 2 3 -1 -2))
@result{} (-1 -2)
@end group
@group
(seq-drop-while (lambda (elt) (< elt 0)) [1 4 6])
@result{} [1 4 6]
@end group
@end example
@end defun
@defun seq-filter pred seq
@cindex filtering sequences
This function returns a list of all the elements in @var{seq} for
which calling @var{pred} with that element returns non-nil.
@var{pred} must be a one-argument function and @var{seq} may be a
list, vector or string.
@example
@group
(seq-filter (lambda (elt) (> elt 0)) [1 -1 3 -3 5])
@result{} (1 3 5)
@end group
@group
(seq-filter (lambda (elt) (> elt 0)) '(-1 -3 -5))
@result{} nil
@end group
@end example
@end defun
@defun seq-remove pred seq
@cindex removing from sequences
This function returns a list of all the elements in @var{seq} for
which calling @var{pred} with that element returns @code{nil}.
@var{pred} must be a one-argument function and @var{seq} may be a
list, vector or string.
@example
@group
(seq-remove (lambda (elt) (> elt 0)) [1 -1 3 -3 5])
@result{} (-1 -3)
@end group
@group
(seq-remove (lambda (elt) (< elt 0)) '(-1 -3 -5))
@result{} nil
@end group
@end example
@end defun
@defun seq-reduce function seq initial-value
@cindex reducing sequences
This function returns the result of calling @var{function} with
@var{initial-value} and the first element of @var{seq}, then calling
@var{function} with that result and the second element of @var{seq},
then with that result and the third element of @var{seq}, etc.
@var{function} must be a two-arguments function and @var{seq} may be a
list, vector or string.
If @var{seq} is empty, @var{initial-value} is returned and
@var{function} is not called.
@example
@group
(seq-reduce #'+ [1 2 3 4] 0)
@result{} 10
@end group
@group
(seq-reduce #'+ '(1 2 3 4) 5)
@result{} 15
@end group
@group
(seq-reduce #'+ '() 3)
@result{} 3
@end group
@end example
@end defun
@defun seq-some-p pred seq
This function returns any element in @var{seq} for which calling
@var{pred} with that element returns non-nil. If successively calling
@var{pred} with each element of @var{seq} always returns @code{nil},
@code{nil} is returned.
@var{pred} must be a one-argument function and @var{seq} may be a
list, vector or string.
@example
@group
(seq-some-p #'numberp ["abc" 1 nil])
@result{} 1
@end group
@group
(seq-some-p #'numberp ["abc" "def"])
@result{} nil
@end group
@end example
@end defun
@defun seq-every-p pred seq
This function returns non-nil if successively calling @var{pred} with
each element of @var{seq} always returns non-nil, @code{nil} otherwise.
@var{pred} must be a one-argument function and @var{seq} may be a
list, vector or string.
@example
@group
(seq-every-p #'numberp [2 4 6])
@result{} t
@end group
@group
(seq-some-p #'numberp [2 4 "6"])
@result{} nil
@end group
@end example
@end defun
@defun seq-empty-p seq
This function returns non-nil if the sequence @var{seq} is empty,
@code{nil} otherwise.
@var{seq} may be a list, vector or string.
@example
@group
(seq-empty-p "not empty")
@result{} nil
@end group
@group
(seq-empty-p "")
@result{} t
@end group
@end example
@end defun
@defun seq-count pred seq
This function returns the number of elements in @var{seq} for which
calling @var{pred} with that element returns non-nil.
@var{pred} must be a one-argument function and @var{seq} may be a
list, vector or string.
@example
(seq-count (lambda (elt) (> elt 0)) [-1 2 0 3 -2])
@result{} 2
@end example
@end defun
@defun seq-sort pred seq
This function returns a sorted sequence of the elements of
@var{seq}, comparing its elements with @var{pred}. Called with two
elements of @var{seq}, @var{pred} should return non-nil if the first
element should sort before the second.
@var{pred} must be a two-arguments function, @var{seq} may be a list,
vector or string.
The result is a sequence of the same type as SEQ.
@cindex sorting sequences
@end defun
@defun seq-contains-p seq elt testfn
This function returns the first element in @var{seq} that equals to
@var{elt}.
Equality is defined by @var{testfn} if non-nil or by @code{equal} if
@code{nil}.
@var{seq} may be a list, vector or string.
@example
@group
(seq-contains-p '(symbol1 symbol2) 'symbol1)
@result{} symbol1
@end group
@group
(seq-contains-p '(symbol1 symbol2) 'symbol3)
@result{} nil
@end group
@end example
@end defun
@defun seq-uniq seq testfn
This function returns a list of the elements of @var{seq} with
duplicates removed. @var{testfn} is used to compare elements, or
@code{equal} if @var{testfn} is @code{nil}.
@var{testfn} must be a two-argument function or @code{nil} and
@var{seq} may be a list, vector or string.
@example
@group
(seq-uniq '(1 2 2 1 3))
@result{} (1 2 3)
@end group
@group
(seq-uniq '(1 2 2.0 1.0) #'=)
@result{} [3 4]
@end group
@end example
@end defun
@defun seq-subseq seq start &optional end
This function returns a sub-sequence of @var{seq} from @var{start}
to @var{end}. If @var{end} is omitted, it default to the length of
@var{seq}. If @var{start} or @var{end} is negative, it counts from
the end of @var{seq}.
@var{seq} may be a list, vector or string.
The result is the same type of sequence as @var{seq}.
@example
@group
(seq-subseq '(1 2 3 4 5) 1)
@result{} (2 3 4 5)
@end group
@group
(seq-subseq '[1 2 3 4 5] 1 3)
@result{} [2 3]
@end group
@group
(seq-subseq '[1 2 3 4 5] -3 -1)
@result{} [3 4]
@end group
@end example
@end defun
@defun seq-concatenate type &rest seqs
This function returns a sequence made of the concatenation of
@var{seqs}. The result is a sequence of type @var{type}. @var{type}
may be one of the following symbols: @code{vector}, @code{list} or
@code{string}.
@example
@group
(seq-concatenate 'list '(1 2) '(3 4) [5 6])
@result{} (1 2 3 5 6)
@end group
@group
(seq-concatenate 'string "Hello " "world")
@result{} "Hello world"
@end group
@end example
@end defun
@defmac seq-doseq (var seq [result]) body@dots{}
@cindex sequence iteration
This macro is like @code{dolist}, except that @var{seq} can be a list,
vector or string (@pxref{Iteration} for more information about the
@code{dolist} macro).
@var{seq-doseq} is primarily useful for side-effects.
@example
(seq-doseq (elt [1 2 3])
(print (* 2 elt)))
@print{}
@print{} 2
@print{}
@print{} 4
@print{}
@print{} 6
@result{} nil
@end example
@end defmac
@node Arrays
@section Arrays
@cindex array
......
2014-12-16 Nicolas Petton <petton.nicolas@gmail.com>
* emacs-lisp/seq.el: New file.
2014-12-16 Stefan Monnier <monnier@iro.umontreal.ca>
* jit-lock.el (jit-lock-function): Don't defer if jit-lock-defer-time
......
;;; seq.el --- Sequence manipulation functions -*- lexical-binding: t -*-
;; Copyright (C) 2014 Free Software Foundation, Inc.
;; Author: Nicolas Petton <petton.nicolas@gmail.com>
;; Keywords: sequences
;; Version: 1.0
;; 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 that complement basic functions
;; provided by subr.el.
;;
;; All functions are prefixed with "seq-".
;;
;; All provided functions work on lists, strings and vectors.
;;
;; Functions taking a predicate or a function iterating over the
;; sequence as argument take the function as their first argument and
;; the sequence as their second argument. All other functions take
;; the sequence as their first argument.
;;
;; All functions are tested in test/automated/seq-tests.el
;;; Code:
(defmacro seq-doseq (spec &rest body)
"Loop over a sequence.
Similar to `dolist' but can be applied lists, strings and vectors.
Evaluate BODY with VAR bound to each element of SEQ, in turn.
Then evaluate RESULT to get return value, default nil.
\(fn (VAR SEQ [RESULT]) BODY...)"
(declare (indent 1) (debug ((symbolp form &optional form) body)))
(let ((is-list (make-symbol "is-list"))
(seq (make-symbol "seq"))
(index (make-symbol "index")))
`(let* ((,seq ,(cadr spec))
(,is-list (listp ,seq))
(,index (if ,is-list ,seq 0)))
(while (if ,is-list
(consp ,index)
(< ,index (seq-length ,seq)))
(let ((,(car spec) (if ,is-list
(car ,index)
(seq-elt ,seq ,index))))
,@body
(setq ,index (if ,is-list
(cdr ,index)
(+ ,index 1)))))
,@(if (cddr spec)
`((setq ,(car spec) nil) ,@(cddr spec))))))
(defun seq-drop (seq n)
"Return a subsequence of SEQ without its first N elements.
The result is a sequence of the same type as SEQ.
If N is a negative integer or zero, SEQ is returned."
(if (<= n 0)
seq
(if (listp seq)
(seq--drop-list seq n)
(let ((length (seq-length seq)))
(seq-subseq seq (min n length) length)))))
(defun seq-take (seq n)
"Return a subsequence of SEQ with its first N elements.
The result is a sequence of the same type as SEQ.
If N is a negative integer or zero, an empty sequence is
returned."
(if (listp seq)
(seq--take-list seq n)
(seq-subseq seq 0 (min (max n 0) (seq-length seq)))))
(defun seq-drop-while (pred seq)
"Return a sequence, from the first element for which (PRED element) is nil, of SEQ.
The result is a sequence of the same type as SEQ."
(if (listp seq)
(seq--drop-while-list pred seq)
(seq-drop seq (seq--count-successive pred seq))))
(defun seq-take-while (pred seq)
"Return a sequence of the successive elements for which (PRED element) is non-nil in SEQ.
The result is a sequence of the same type as SEQ."
(if (listp seq)
(seq--take-while-list pred seq)
(seq-take seq (seq--count-successive pred seq))))
(defun seq-filter (pred seq)
"Return a list of all the elements for which (PRED element) is non-nil in SEQ."
(let ((exclude (make-symbol "exclude")))
(delq exclude (seq-map (lambda (elt)
(if (funcall pred elt)
elt
exclude))
seq))))
(defun seq-remove (pred seq)
"Return a list of all the elements for which (PRED element) is nil in SEQ."
(seq-filter (lambda (elt) (not (funcall pred elt)))
seq))
(defun seq-reduce (function seq initial-value)
"Reduce the function FUNCTION across SEQ, starting with INITIAL-VALUE.
Return the result of calling FUNCTION with INITIAL-VALUE and the
first element of SEQ, then calling FUNCTION with that result and
the second element of SEQ, then with that result and the third
element of SEQ, etc.
If SEQ is empty, return INITIAL-VALUE and FUNCTION is not called."
(if (seq-empty-p seq)
initial-value
(let ((acc initial-value))
(seq-doseq (elt seq)
(setq acc (funcall function acc elt)))
acc)))
(defun seq-some-p (pred seq)
"Return any element for which (PRED element) is non-nil in SEQ, nil otherwise."
(catch 'seq--break
(seq-doseq (elt seq)
(when (funcall pred elt)
(throw 'seq--break elt)))
nil))
(defun seq-every-p (pred seq)
"Return non-nil if (PRED element) is non-nil for all elements of the sequence SEQ."
(catch 'seq--break
(seq-doseq (elt seq)
(or (funcall pred elt)
(throw 'seq--break nil)))
t))
(defun seq-count (pred seq)
"Return the number of elements for which (PRED element) returns non-nil in seq."
(let ((count 0))
(seq-doseq (elt seq)
(when (funcall pred elt)
(setq count (+ 1 count))))
count))
(defun seq-empty-p (seq)
"Return non-nil if the sequence SEQ is empty, nil otherwise."
(if (listp seq)
(null seq)
(= 0 (seq-length seq))))
(defun seq-sort (pred seq)
"Return a sorted sequence comparing using PRED the elements of SEQ.
The result is a sequence of the same type as SEQ."
(if (listp seq)
(sort (seq-copy seq) pred)
(let ((result (seq-sort pred (append seq nil))))
(cond ((stringp seq) (concat result))
((vectorp seq) (vconcat result))
(t (error "Unsupported sequence: %s" seq))))))
(defun seq-contains-p (seq elt &optional testfn)
"Return the first element in SEQ that equals to ELT.
Equality is defined by TESTFN if non-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.