.emacs.d/elpa/dash-20221013.836/dash.el

3934 lines
137 KiB
EmacsLisp
Raw Normal View History

2017-09-12 21:23:27 +02:00
;;; dash.el --- A modern list library for Emacs -*- lexical-binding: t -*-
2021-01-17 12:49:07 +01:00
;; Copyright (C) 2012-2021 Free Software Foundation, Inc.
2017-09-12 21:23:27 +02:00
;; Author: Magnar Sveen <magnars@gmail.com>
2021-09-04 14:47:04 +02:00
;; Version: 2.19.1
2021-01-17 12:49:07 +01:00
;; Package-Requires: ((emacs "24"))
2021-01-01 17:31:26 +01:00
;; Keywords: extensions, lisp
2021-01-17 12:49:07 +01:00
;; Homepage: https://github.com/magnars/dash.el
2017-09-12 21:23:27 +02:00
2021-01-17 12:49:07 +01:00
;; This program is free software: you can redistribute it and/or modify
2017-09-12 21:23:27 +02:00
;; 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.
;; This program 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
2021-01-17 12:49:07 +01:00
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
2017-09-12 21:23:27 +02:00
;;; Commentary:
2021-01-17 12:49:07 +01:00
;; A modern list API for Emacs.
2017-09-12 21:23:27 +02:00
;;
2021-01-17 12:49:07 +01:00
;; See its overview at https://github.com/magnars/dash.el#functions.
2017-09-12 21:23:27 +02:00
;;; Code:
2020-03-22 10:57:42 +01:00
(eval-when-compile
2022-08-13 10:20:02 +02:00
;; TODO: Emacs 24.3 first introduced `gv', so remove this and all
;; calls to `defsetf' when support for earlier versions is dropped.
2020-03-22 10:57:42 +01:00
(unless (fboundp 'gv-define-setter)
2022-08-13 10:20:02 +02:00
(require 'cl))
;; TODO: Emacs versions 24.3..24.5 complain about unknown `declare'
;; props, so remove this when support for those versions is dropped.
(and (< emacs-major-version 25)
(boundp 'defun-declarations-alist)
(dolist (prop '(pure side-effect-free))
(unless (assq prop defun-declarations-alist)
(push (list prop #'ignore) defun-declarations-alist)))))
2020-03-22 10:57:42 +01:00
2017-09-12 21:23:27 +02:00
(defgroup dash ()
2021-01-01 17:31:26 +01:00
"Customize group for Dash, a modern list library."
:group 'extensions
2017-09-12 21:23:27 +02:00
:group 'lisp
:prefix "dash-")
(defmacro !cons (car cdr)
"Destructive: Set CDR to the cons of CAR and CDR."
2021-06-12 09:26:46 +02:00
(declare (debug (form symbolp)))
2017-09-12 21:23:27 +02:00
`(setq ,cdr (cons ,car ,cdr)))
(defmacro !cdr (list)
"Destructive: Set LIST to the cdr of LIST."
2021-06-12 09:26:46 +02:00
(declare (debug (symbolp)))
2017-09-12 21:23:27 +02:00
`(setq ,list (cdr ,list)))
(defmacro --each (list &rest body)
2021-01-17 12:49:07 +01:00
"Evaluate BODY for each element of LIST and return nil.
Each element of LIST in turn is bound to `it' and its index
within LIST to `it-index' before evaluating BODY.
This is the anaphoric counterpart to `-each'."
(declare (debug (form body)) (indent 1))
(let ((l (make-symbol "list"))
(i (make-symbol "i")))
2017-09-12 21:23:27 +02:00
`(let ((,l ,list)
2022-08-13 10:20:02 +02:00
(,i 0))
2017-09-12 21:23:27 +02:00
(while ,l
2022-08-13 10:20:02 +02:00
(let ((it (pop ,l)) (it-index ,i))
(ignore it it-index)
,@body)
(setq ,i (1+ ,i))))))
2019-03-31 19:05:44 +02:00
2017-09-12 21:23:27 +02:00
(defun -each (list fn)
2021-01-17 12:49:07 +01:00
"Call FN on each element of LIST.
Return nil; this function is intended for side effects.
2021-04-16 15:19:22 +02:00
Its anaphoric counterpart is `--each'.
For access to the current element's index in LIST, see
`-each-indexed'."
2021-01-01 17:31:26 +01:00
(declare (indent 1))
2021-01-17 12:49:07 +01:00
(ignore (mapc fn list)))
2017-09-12 21:23:27 +02:00
(defalias '--each-indexed '--each)
(defun -each-indexed (list fn)
2021-01-17 12:49:07 +01:00
"Call FN on each index and element of LIST.
For each ITEM at INDEX in LIST, call (funcall FN INDEX ITEM).
Return nil; this function is intended for side effects.
2021-04-16 15:19:22 +02:00
2017-09-12 21:23:27 +02:00
See also: `-map-indexed'."
2021-01-01 17:31:26 +01:00
(declare (indent 1))
2017-09-12 21:23:27 +02:00
(--each list (funcall fn it-index it)))
(defmacro --each-while (list pred &rest body)
2021-01-17 12:49:07 +01:00
"Evaluate BODY for each item in LIST, while PRED evaluates to non-nil.
Each element of LIST in turn is bound to `it' and its index
within LIST to `it-index' before evaluating PRED or BODY. Once
an element is reached for which PRED evaluates to nil, no further
BODY is evaluated. The return value is always nil.
This is the anaphoric counterpart to `-each-while'."
(declare (debug (form form body)) (indent 2))
(let ((l (make-symbol "list"))
(i (make-symbol "i"))
(elt (make-symbol "elt")))
2017-09-12 21:23:27 +02:00
`(let ((,l ,list)
2021-01-17 12:49:07 +01:00
(,i 0)
2022-08-13 10:20:02 +02:00
,elt)
(while (when ,l
(setq ,elt (car-safe ,l))
(let ((it ,elt) (it-index ,i))
(ignore it it-index)
,pred))
(let ((it ,elt) (it-index ,i))
(ignore it it-index)
,@body)
(setq ,i (1+ ,i) ,l (cdr ,l))))))
2017-09-12 21:23:27 +02:00
(defun -each-while (list pred fn)
2021-01-17 12:49:07 +01:00
"Call FN on each ITEM in LIST, while (PRED ITEM) is non-nil.
Once an ITEM is reached for which PRED returns nil, FN is no
longer called. Return nil; this function is intended for side
effects.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
Its anaphoric counterpart is `--each-while'."
2021-01-01 17:31:26 +01:00
(declare (indent 2))
2017-09-12 21:23:27 +02:00
(--each-while list (funcall pred it) (funcall fn it)))
2018-09-06 19:52:34 +02:00
(defmacro --each-r (list &rest body)
2021-01-17 12:49:07 +01:00
"Evaluate BODY for each element of LIST in reversed order.
Each element of LIST in turn, starting at its end, is bound to
`it' and its index within LIST to `it-index' before evaluating
BODY. The return value is always nil.
This is the anaphoric counterpart to `-each-r'."
(declare (debug (form body)) (indent 1))
(let ((v (make-symbol "vector"))
(i (make-symbol "i")))
;; Implementation note: building a vector is considerably faster
2018-09-06 19:52:34 +02:00
;; than building a reversed list (vector takes less memory, so
2021-01-17 12:49:07 +01:00
;; there is less GC), plus `length' comes naturally. In-place
;; `nreverse' would be faster still, but BODY would be able to see
;; that, even if the modification was undone before we return.
2018-09-06 19:52:34 +02:00
`(let* ((,v (vconcat ,list))
2021-01-17 12:49:07 +01:00
(,i (length ,v))
it it-index)
(ignore it it-index)
(while (> ,i 0)
(setq ,i (1- ,i) it-index ,i it (aref ,v ,i))
2018-09-06 19:52:34 +02:00
,@body))))
(defun -each-r (list fn)
2021-01-17 12:49:07 +01:00
"Call FN on each element of LIST in reversed order.
Return nil; this function is intended for side effects.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
Its anaphoric counterpart is `--each-r'."
2018-09-06 19:52:34 +02:00
(--each-r list (funcall fn it)))
(defmacro --each-r-while (list pred &rest body)
2021-01-17 12:49:07 +01:00
"Eval BODY for each item in reversed LIST, while PRED evals to non-nil.
Each element of LIST in turn, starting at its end, is bound to
`it' and its index within LIST to `it-index' before evaluating
PRED or BODY. Once an element is reached for which PRED
evaluates to nil, no further BODY is evaluated. The return value
is always nil.
This is the anaphoric counterpart to `-each-r-while'."
(declare (debug (form form body)) (indent 2))
(let ((v (make-symbol "vector"))
(i (make-symbol "i"))
(elt (make-symbol "elt")))
2018-09-06 19:52:34 +02:00
`(let* ((,v (vconcat ,list))
2021-01-17 12:49:07 +01:00
(,i (length ,v))
,elt it it-index)
(ignore it it-index)
(while (when (> ,i 0)
(setq ,i (1- ,i) it-index ,i)
(setq ,elt (aref ,v ,i) it ,elt)
,pred)
(setq it-index ,i it ,elt)
,@body))))
2018-09-06 19:52:34 +02:00
(defun -each-r-while (list pred fn)
2021-01-17 12:49:07 +01:00
"Call FN on each ITEM in reversed LIST, while (PRED ITEM) is non-nil.
Once an ITEM is reached for which PRED returns nil, FN is no
longer called. Return nil; this function is intended for side
effects.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
Its anaphoric counterpart is `--each-r-while'."
2018-09-06 19:52:34 +02:00
(--each-r-while list (funcall pred it) (funcall fn it)))
2017-09-12 21:23:27 +02:00
(defmacro --dotimes (num &rest body)
2021-01-17 12:49:07 +01:00
"Evaluate BODY NUM times, presumably for side effects.
BODY is evaluated with the local variable `it' temporarily bound
to successive integers running from 0, inclusive, to NUM,
exclusive. BODY is not evaluated if NUM is less than 1.
This is the anaphoric counterpart to `-dotimes'."
(declare (debug (form body)) (indent 1))
(let ((n (make-symbol "num"))
(i (make-symbol "i")))
2017-09-12 21:23:27 +02:00
`(let ((,n ,num)
2021-01-17 12:49:07 +01:00
(,i 0)
it)
(ignore it)
(while (< ,i ,n)
(setq it ,i ,i (1+ ,i))
,@body))))
2017-09-12 21:23:27 +02:00
(defun -dotimes (num fn)
2021-01-17 12:49:07 +01:00
"Call FN NUM times, presumably for side effects.
FN is called with a single argument on successive integers
running from 0, inclusive, to NUM, exclusive. FN is not called
if NUM is less than 1.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
This function's anaphoric counterpart is `--dotimes'."
2021-01-01 17:31:26 +01:00
(declare (indent 1))
2017-09-12 21:23:27 +02:00
(--dotimes num (funcall fn it)))
(defun -map (fn list)
2021-01-17 12:49:07 +01:00
"Apply FN to each item in LIST and return the list of results.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
This function's anaphoric counterpart is `--map'."
2017-09-12 21:23:27 +02:00
(mapcar fn list))
(defmacro --map (form list)
2021-01-17 12:49:07 +01:00
"Eval FORM for each item in LIST and return the list of results.
Each element of LIST in turn is bound to `it' before evaluating
2021-04-16 15:19:22 +02:00
FORM.
2021-01-17 12:49:07 +01:00
This is the anaphoric counterpart to `-map'."
(declare (debug (def-form form)))
`(mapcar (lambda (it) (ignore it) ,form) ,list))
(defmacro --reduce-from (form init list)
"Accumulate a value by evaluating FORM across LIST.
This macro is like `--each' (which see), but it additionally
provides an accumulator variable `acc' which it successively
binds to the result of evaluating FORM for the current LIST
element before processing the next element. For the first
element, `acc' is initialized with the result of evaluating INIT.
The return value is the resulting value of `acc'. If LIST is
empty, FORM is not evaluated, and the return value is the result
of INIT.
This is the anaphoric counterpart to `-reduce-from'."
2017-09-12 21:23:27 +02:00
(declare (debug (form form form)))
2021-01-17 12:49:07 +01:00
`(let ((acc ,init))
2017-09-12 21:23:27 +02:00
(--each ,list (setq acc ,form))
acc))
2021-01-17 12:49:07 +01:00
(defun -reduce-from (fn init list)
"Reduce the function FN across LIST, starting with INIT.
Return the result of applying FN to INIT and the first element of
LIST, then applying FN to that result and the second element,
etc. If LIST is empty, return INIT without calling FN.
2017-09-12 21:23:27 +02:00
2021-01-17 12:49:07 +01:00
This function's anaphoric counterpart is `--reduce-from'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
For other folds, see also `-reduce' and `-reduce-r'."
(--reduce-from (funcall fn acc it) init list))
2017-09-12 21:23:27 +02:00
(defmacro --reduce (form list)
2021-01-17 12:49:07 +01:00
"Accumulate a value by evaluating FORM across LIST.
This macro is like `--reduce-from' (which see), except the first
element of LIST is taken as INIT. Thus if LIST contains a single
item, it is returned without evaluating FORM. If LIST is empty,
FORM is evaluated with `it' and `acc' bound to nil.
This is the anaphoric counterpart to `-reduce'."
2017-09-12 21:23:27 +02:00
(declare (debug (form form)))
(let ((lv (make-symbol "list-value")))
`(let ((,lv ,list))
(if ,lv
(--reduce-from ,form (car ,lv) (cdr ,lv))
2021-04-16 15:19:22 +02:00
;; Explicit nil binding pacifies lexical "variable left uninitialized"
;; warning. See issue #377 and upstream https://bugs.gnu.org/47080.
(let ((acc nil) (it nil))
2021-01-17 12:49:07 +01:00
(ignore acc it)
,form)))))
2017-09-12 21:23:27 +02:00
(defun -reduce (fn list)
2021-01-17 12:49:07 +01:00
"Reduce the function FN across LIST.
Return the result of applying FN to the first two elements of
LIST, then applying FN to that result and the third element, etc.
If LIST contains a single element, return it without calling FN.
If LIST is empty, return the result of calling FN with no
arguments.
This function's anaphoric counterpart is `--reduce'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
For other folds, see also `-reduce-from' and `-reduce-r'."
2017-09-12 21:23:27 +02:00
(if list
(-reduce-from fn (car list) (cdr list))
(funcall fn)))
2021-01-17 12:49:07 +01:00
(defmacro --reduce-r-from (form init list)
"Accumulate a value by evaluating FORM across LIST in reverse.
This macro is like `--reduce-from', except it starts from the end
of LIST.
This is the anaphoric counterpart to `-reduce-r-from'."
2018-11-03 18:33:59 +01:00
(declare (debug (form form form)))
2021-01-17 12:49:07 +01:00
`(let ((acc ,init))
(--each-r ,list (setq acc ,form))
acc))
2018-11-03 18:33:59 +01:00
2021-01-17 12:49:07 +01:00
(defun -reduce-r-from (fn init list)
"Reduce the function FN across LIST in reverse, starting with INIT.
Return the result of applying FN to the last element of LIST and
INIT, then applying FN to the second-to-last element and the
previous result of FN, etc. That is, the first argument of FN is
the current element, and its second argument the accumulated
value. If LIST is empty, return INIT without calling FN.
2017-09-12 21:23:27 +02:00
2021-01-17 12:49:07 +01:00
This function is like `-reduce-from' but the operation associates
from the right rather than left. In other words, it starts from
the end of LIST and flips the arguments to FN. Conceptually, it
is like replacing the conses in LIST with applications of FN, and
its last link with INIT, and evaluating the resulting expression.
2017-09-12 21:23:27 +02:00
2021-01-17 12:49:07 +01:00
This function's anaphoric counterpart is `--reduce-r-from'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
For other folds, see also `-reduce-r' and `-reduce'."
(--reduce-r-from (funcall fn it acc) init list))
2017-09-12 21:23:27 +02:00
2018-11-03 18:33:59 +01:00
(defmacro --reduce-r (form list)
2021-01-17 12:49:07 +01:00
"Accumulate a value by evaluating FORM across LIST in reverse order.
This macro is like `--reduce', except it starts from the end of
LIST.
This is the anaphoric counterpart to `-reduce-r'."
2018-11-03 18:33:59 +01:00
(declare (debug (form form)))
`(--reduce ,form (reverse ,list)))
2017-09-12 21:23:27 +02:00
(defun -reduce-r (fn list)
2021-01-17 12:49:07 +01:00
"Reduce the function FN across LIST in reverse.
Return the result of applying FN to the last two elements of
LIST, then applying FN to the third-to-last element and the
previous result of FN, etc. That is, the first argument of FN is
the current element, and its second argument the accumulated
value. If LIST contains a single element, return it without
calling FN. If LIST is empty, return the result of calling FN
with no arguments.
This function is like `-reduce' but the operation associates from
the right rather than left. In other words, it starts from the
end of LIST and flips the arguments to FN. Conceptually, it is
like replacing the conses in LIST with applications of FN,
ignoring its last link, and evaluating the resulting expression.
This function's anaphoric counterpart is `--reduce-r'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
For other folds, see also `-reduce-r-from' and `-reduce'."
2018-11-03 18:33:59 +01:00
(if list
(--reduce-r (funcall fn it acc) list)
(funcall fn)))
2017-09-12 21:23:27 +02:00
2021-01-17 12:49:07 +01:00
(defmacro --reductions-from (form init list)
"Return a list of FORM's intermediate reductions across LIST.
That is, a list of the intermediate values of the accumulator
when `--reduce-from' (which see) is called with the same
arguments.
This is the anaphoric counterpart to `-reductions-from'."
(declare (debug (form form form)))
`(nreverse
(--reduce-from (cons (let ((acc (car acc))) (ignore acc) ,form) acc)
(list ,init)
,list)))
2017-10-28 19:19:00 +02:00
2021-01-17 12:49:07 +01:00
(defun -reductions-from (fn init list)
"Return a list of FN's intermediate reductions across LIST.
That is, a list of the intermediate values of the accumulator
when `-reduce-from' (which see) is called with the same
arguments.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
This function's anaphoric counterpart is `--reductions-from'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
For other folds, see also `-reductions' and `-reductions-r'."
(--reductions-from (funcall fn acc it) init list))
(defmacro --reductions (form list)
"Return a list of FORM's intermediate reductions across LIST.
That is, a list of the intermediate values of the accumulator
when `--reduce' (which see) is called with the same arguments.
This is the anaphoric counterpart to `-reductions'."
(declare (debug (form form)))
(let ((lv (make-symbol "list-value")))
`(let ((,lv ,list))
(if ,lv
(--reductions-from ,form (car ,lv) (cdr ,lv))
2022-08-13 10:20:02 +02:00
;; Explicit nil binding pacifies lexical "variable left uninitialized"
;; warning. See issue #377 and upstream https://bugs.gnu.org/47080.
(let ((acc nil) (it nil))
2021-01-17 12:49:07 +01:00
(ignore acc it)
(list ,form))))))
2017-10-28 19:19:00 +02:00
(defun -reductions (fn list)
2021-01-17 12:49:07 +01:00
"Return a list of FN's intermediate reductions across LIST.
That is, a list of the intermediate values of the accumulator
when `-reduce' (which see) is called with the same arguments.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
This function's anaphoric counterpart is `--reductions'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
For other folds, see also `-reductions' and `-reductions-r'."
(if list
(--reductions-from (funcall fn acc it) (car list) (cdr list))
(list (funcall fn))))
(defmacro --reductions-r-from (form init list)
"Return a list of FORM's intermediate reductions across reversed LIST.
That is, a list of the intermediate values of the accumulator
when `--reduce-r-from' (which see) is called with the same
arguments.
This is the anaphoric counterpart to `-reductions-r-from'."
(declare (debug (form form form)))
`(--reduce-r-from (cons (let ((acc (car acc))) (ignore acc) ,form) acc)
(list ,init)
,list))
2017-10-28 19:19:00 +02:00
(defun -reductions-r-from (fn init list)
2021-01-17 12:49:07 +01:00
"Return a list of FN's intermediate reductions across reversed LIST.
That is, a list of the intermediate values of the accumulator
when `-reduce-r-from' (which see) is called with the same
arguments.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
This function's anaphoric counterpart is `--reductions-r-from'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
For other folds, see also `-reductions' and `-reductions-r'."
(--reductions-r-from (funcall fn it acc) init list))
(defmacro --reductions-r (form list)
"Return a list of FORM's intermediate reductions across reversed LIST.
That is, a list of the intermediate values of the accumulator
when `--reduce-re' (which see) is called with the same arguments.
This is the anaphoric counterpart to `-reductions-r'."
(declare (debug (form list)))
(let ((lv (make-symbol "list-value")))
`(let ((,lv (reverse ,list)))
(if ,lv
(--reduce-from (cons (let ((acc (car acc))) (ignore acc) ,form) acc)
(list (car ,lv))
(cdr ,lv))
2021-04-16 15:19:22 +02:00
;; Explicit nil binding pacifies lexical "variable left uninitialized"
;; warning. See issue #377 and upstream https://bugs.gnu.org/47080.
(let ((acc nil) (it nil))
2021-01-17 12:49:07 +01:00
(ignore acc it)
(list ,form))))))
2017-10-28 19:19:00 +02:00
(defun -reductions-r (fn list)
2021-01-17 12:49:07 +01:00
"Return a list of FN's intermediate reductions across reversed LIST.
That is, a list of the intermediate values of the accumulator
when `-reduce-r' (which see) is called with the same arguments.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
This function's anaphoric counterpart is `--reductions-r'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
For other folds, see also `-reductions-r-from' and
`-reductions'."
(if list
(--reductions-r (funcall fn it acc) list)
(list (funcall fn))))
2017-10-28 19:19:00 +02:00
2017-09-12 21:23:27 +02:00
(defmacro --filter (form list)
2021-01-17 12:49:07 +01:00
"Return a new list of the items in LIST for which FORM evals to non-nil.
Each element of LIST in turn is bound to `it' and its index
within LIST to `it-index' before evaluating FORM.
This is the anaphoric counterpart to `-filter'.
For the opposite operation, see also `--remove'."
2017-09-12 21:23:27 +02:00
(declare (debug (form form)))
(let ((r (make-symbol "result")))
`(let (,r)
2021-01-17 12:49:07 +01:00
(--each ,list (when ,form (push it ,r)))
2017-09-12 21:23:27 +02:00
(nreverse ,r))))
(defun -filter (pred list)
2021-01-17 12:49:07 +01:00
"Return a new list of the items in LIST for which PRED returns non-nil.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
Alias: `-select'.
2021-04-16 15:19:22 +02:00
This function's anaphoric counterpart is `--filter'.
2021-01-17 12:49:07 +01:00
For similar operations, see also `-keep' and `-remove'."
2017-09-12 21:23:27 +02:00
(--filter (funcall pred it) list))
(defalias '-select '-filter)
(defalias '--select '--filter)
(defmacro --remove (form list)
2021-01-17 12:49:07 +01:00
"Return a new list of the items in LIST for which FORM evals to nil.
Each element of LIST in turn is bound to `it' and its index
within LIST to `it-index' before evaluating FORM.
This is the anaphoric counterpart to `-remove'.
For the opposite operation, see also `--filter'."
2017-09-12 21:23:27 +02:00
(declare (debug (form form)))
`(--filter (not ,form) ,list))
(defun -remove (pred list)
"Return a new list of the items in LIST for which PRED returns nil.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
Alias: `-reject'.
2021-04-16 15:19:22 +02:00
This function's anaphoric counterpart is `--remove'.
2021-01-17 12:49:07 +01:00
For similar operations, see also `-keep' and `-filter'."
2017-09-12 21:23:27 +02:00
(--remove (funcall pred it) list))
(defalias '-reject '-remove)
(defalias '--reject '--remove)
(defmacro --remove-first (form list)
2021-01-17 12:49:07 +01:00
"Remove the first item from LIST for which FORM evals to non-nil.
Each element of LIST in turn is bound to `it' and its index
within LIST to `it-index' before evaluating FORM. This is a
non-destructive operation, but only the front of LIST leading up
to the removed item is a copy; the rest is LIST's original tail.
If no item is removed, then the result is a complete copy.
This is the anaphoric counterpart to `-remove-first'."
2017-09-12 21:23:27 +02:00
(declare (debug (form form)))
2021-01-17 12:49:07 +01:00
(let ((front (make-symbol "front"))
(tail (make-symbol "tail")))
`(let ((,tail ,list) ,front)
(--each-while ,tail (not ,form)
(push (pop ,tail) ,front))
(if ,tail
(nconc (nreverse ,front) (cdr ,tail))
(nreverse ,front)))))
(defun -remove-first (pred list)
"Remove the first item from LIST for which PRED returns non-nil.
This is a non-destructive operation, but only the front of LIST
leading up to the removed item is a copy; the rest is LIST's
original tail. If no item is removed, then the result is a
complete copy.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
Alias: `-reject-first'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
This function's anaphoric counterpart is `--remove-first'.
2021-04-16 15:19:22 +02:00
2021-01-17 12:49:07 +01:00
See also `-map-first', `-remove-item', and `-remove-last'."
(--remove-first (funcall pred it) list))
2017-09-12 21:23:27 +02:00
(defalias '-reject-first '-remove-first)
(defalias '--reject-first '--remove-first)
2021-04-16 15:19:22 +02:00
(defmacro --remove-last (form list)
"Remove the last item from LIST for which FORM evals to non-nil.
Each element of LIST in turn is bound to `it' before evaluating
FORM. The result is a copy of LIST regardless of whether an
element is removed.
This is the anaphoric counterpart to `-remove-last'."
(declare (debug (form form)))
`(nreverse (--remove-first ,form (reverse ,list))))
2017-09-12 21:23:27 +02:00
(defun -remove-last (pred list)
2021-04-16 15:19:22 +02:00
"Remove the last item from LIST for which PRED returns non-nil.
The result is a copy of LIST regardless of whether an element is
removed.
2017-09-12 21:23:27 +02:00
2021-04-16 15:19:22 +02:00
Alias: `-reject-last'.
2017-09-12 21:23:27 +02:00
2021-04-16 15:19:22 +02:00
This function's anaphoric counterpart is `--remove-last'.
2017-09-12 21:23:27 +02:00
2021-04-16 15:19:22 +02:00
See also `-map-last', `-remove-item', and `-remove-first'."
(--remove-last (funcall pred it) list))
2017-09-12 21:23:27 +02:00
(defalias '-reject-last '-remove-last)
(defalias '--reject-last '--remove-last)
2021-04-16 15:19:22 +02:00
(defalias '-remove-item #'remove
"Return a copy of LIST with all occurrences of ITEM removed.
The comparison is done with `equal'.
\n(fn ITEM LIST)")
2017-09-12 21:23:27 +02:00
(defmacro --keep (form list)
2021-04-16 15:19:22 +02:00
"Eval FORM for each item in LIST and return the non-nil results.
Like `--filter', but returns the non-nil results of FORM instead
of the corresponding elements of LIST. Each element of LIST in
turn is bound to `it' and its index within LIST to `it-index'
before evaluating FORM.
This is the anaphoric counterpart to `-keep'."
2017-09-12 21:23:27 +02:00
(declare (debug (form form)))
(let ((r (make-symbol "result"))
(m (make-symbol "mapped")))
`(let (,r)
2021-04-16 15:19:22 +02:00
(--each ,list (let ((,m ,form)) (when ,m (push ,m ,r))))
2017-09-12 21:23:27 +02:00
(nreverse ,r))))
(defun -keep (fn list)
2021-04-16 15:19:22 +02:00
"Return a new list of the non-nil results of applying FN to each item in LIST.
Like `-filter', but returns the non-nil results of FN instead of
the corresponding elements of LIST.
2017-09-12 21:23:27 +02:00
2021-04-16 15:19:22 +02:00
Its anaphoric counterpart is `--keep'."
2017-09-12 21:23:27 +02:00
(--keep (funcall fn it) list))
(defun -non-nil (list)
2021-04-16 15:19:22 +02:00
"Return a copy of LIST with all nil items removed."
2017-09-12 21:23:27 +02:00
(declare (pure t) (side-effect-free t))
2021-04-16 15:19:22 +02:00
(--filter it list))
2017-09-12 21:23:27 +02:00
(defmacro --map-indexed (form list)
2021-04-16 15:19:22 +02:00
"Eval FORM for each item in LIST and return the list of results.
Each element of LIST in turn is bound to `it' and its index
within LIST to `it-index' before evaluating FORM. This is like
`--map', but additionally makes `it-index' available to FORM.
This is the anaphoric counterpart to `-map-indexed'."
2017-09-12 21:23:27 +02:00
(declare (debug (form form)))
(let ((r (make-symbol "result")))
`(let (,r)
(--each ,list
2021-04-16 15:19:22 +02:00
(push ,form ,r))
2017-09-12 21:23:27 +02:00
(nreverse ,r))))
(defun -map-indexed (fn list)
2021-04-16 15:19:22 +02:00
"Apply FN to each index and item in LIST and return the list of results.
This is like `-map', but FN takes two arguments: the index of the
current element within LIST, and the element itself.
2017-09-12 21:23:27 +02:00
2021-04-16 15:19:22 +02:00
This function's anaphoric counterpart is `--map-indexed'.
2017-09-12 21:23:27 +02:00
2021-04-16 15:19:22 +02:00
For a side-effecting variant, see also `-each-indexed'."
2017-09-12 21:23:27 +02:00
(--map-indexed (funcall fn it-index it) list))
(defmacro --map-when (pred rep list)
"Anaphoric form of `-map-when'."
(declare (debug (form form form)))
(let ((r (make-symbol "result")))
`(let (,r)
(--each ,list (!cons (if ,pred ,rep it) ,r))
(nreverse ,r))))
(defun -map-when (pred rep list)
2022-04-29 08:48:39 +02:00
"Use PRED to conditionally apply REP to each item in LIST.
Return a copy of LIST where the items for which PRED returns nil
are unchanged, and the rest are mapped through the REP function.
2017-09-12 21:23:27 +02:00
Alias: `-replace-where'
See also: `-update-at'"
(--map-when (funcall pred it) (funcall rep it) list))
(defalias '-replace-where '-map-when)
(defalias '--replace-where '--map-when)
(defun -map-first (pred rep list)
2022-04-29 08:48:39 +02:00
"Use PRED to determine the first item in LIST to call REP on.
Return a copy of LIST where the first item for which PRED returns
non-nil is replaced with the result of calling REP on that item.
2017-09-12 21:23:27 +02:00
See also: `-map-when', `-replace-first'"
(let (front)
(while (and list (not (funcall pred (car list))))
(push (car list) front)
(!cdr list))
(if list
(-concat (nreverse front) (cons (funcall rep (car list)) (cdr list)))
(nreverse front))))
(defmacro --map-first (pred rep list)
"Anaphoric form of `-map-first'."
2021-06-12 09:26:46 +02:00
(declare (debug (def-form def-form form)))
2022-08-13 10:20:02 +02:00
`(-map-first (lambda (it) (ignore it) ,pred)
(lambda (it) (ignore it) ,rep)
,list))
2017-09-12 21:23:27 +02:00
(defun -map-last (pred rep list)
2022-04-29 08:48:39 +02:00
"Use PRED to determine the last item in LIST to call REP on.
Return a copy of LIST where the last item for which PRED returns
non-nil is replaced with the result of calling REP on that item.
2017-09-12 21:23:27 +02:00
See also: `-map-when', `-replace-last'"
(nreverse (-map-first pred rep (reverse list))))
(defmacro --map-last (pred rep list)
"Anaphoric form of `-map-last'."
2021-06-12 09:26:46 +02:00
(declare (debug (def-form def-form form)))
2022-08-13 10:20:02 +02:00
`(-map-last (lambda (it) (ignore it) ,pred)
(lambda (it) (ignore it) ,rep)
,list))
2017-09-12 21:23:27 +02:00
(defun -replace (old new list)
"Replace all OLD items in LIST with NEW.
Elements are compared using `equal'.
See also: `-replace-at'"
(declare (pure t) (side-effect-free t))
(--map-when (equal it old) new list))
(defun -replace-first (old new list)
2020-03-22 10:57:42 +01:00
"Replace the first occurrence of OLD with NEW in LIST.
2017-09-12 21:23:27 +02:00
Elements are compared using `equal'.
See also: `-map-first'"
(declare (pure t) (side-effect-free t))
(--map-first (equal old it) new list))
(defun -replace-last (old new list)
2020-03-22 10:57:42 +01:00
"Replace the last occurrence of OLD with NEW in LIST.
2017-09-12 21:23:27 +02:00
Elements are compared using `equal'.
See also: `-map-last'"
(declare (pure t) (side-effect-free t))
(--map-last (equal old it) new list))
(defmacro --mapcat (form list)
"Anaphoric form of `-mapcat'."
(declare (debug (form form)))
`(apply 'append (--map ,form ,list)))
(defun -mapcat (fn list)
"Return the concatenation of the result of mapping FN over LIST.
Thus function FN should return a list."
(--mapcat (funcall fn it) list))
2021-01-17 12:49:07 +01:00
(defmacro --iterate (form init n)
"Anaphoric version of `-iterate'."
(declare (debug (form form form)))
2021-04-16 15:19:22 +02:00
(let ((res (make-symbol "result"))
(len (make-symbol "n")))
`(let ((,len ,n))
(when (> ,len 0)
(let* ((it ,init)
(,res (list it)))
(dotimes (_ (1- ,len))
(push (setq it ,form) ,res))
(nreverse ,res))))))
2021-01-17 12:49:07 +01:00
(defun -iterate (fun init n)
"Return a list of iterated applications of FUN to INIT.
This means a list of the form:
(INIT (FUN INIT) (FUN (FUN INIT)) ...)
N is the length of the returned list."
(--iterate (funcall fun it) init n))
2017-09-12 21:23:27 +02:00
(defun -flatten (l)
"Take a nested list L and return its contents as a single, flat list.
2022-04-29 08:48:39 +02:00
Note that because nil represents a list of zero elements (an
2017-09-12 21:23:27 +02:00
empty list), any mention of nil in L will disappear after
flattening. If you need to preserve nils, consider `-flatten-n'
or map them to some unique symbol and then map them back.
Conses of two atoms are considered \"terminals\", that is, they
aren't flattened further.
See also: `-flatten-n'"
(declare (pure t) (side-effect-free t))
(if (and (listp l) (listp (cdr l)))
(-mapcat '-flatten l)
(list l)))
(defun -flatten-n (num list)
"Flatten NUM levels of a nested LIST.
See also: `-flatten'"
(declare (pure t) (side-effect-free t))
2021-04-16 15:19:22 +02:00
(dotimes (_ num)
(setq list (apply #'append (mapcar #'-list list))))
list)
2017-09-12 21:23:27 +02:00
2022-11-06 19:47:37 +01:00
(defalias '-concat #'append
"Concatenate all the arguments and make the result a list.
The result is a list whose elements are the elements of all the arguments.
Each argument may be a list, vector or string.
All arguments except the last argument are copied. The last argument
is just used as the tail of the new list.
\(fn &rest SEQUENCES)")
2017-09-12 21:23:27 +02:00
(defalias '-copy 'copy-sequence
"Create a shallow copy of LIST.
\(fn LIST)")
2022-08-13 10:20:02 +02:00