branch: externals/dash commit ba6b4a6115701251dc627a6c19cbb5a272c0a413 Merge: 3df46d7d9f 2acdae698e Author: Basil L. Contovounesios <conto...@tcd.ie> Commit: Basil L. Contovounesios <conto...@tcd.ie>
Merge branch 'blc/stefan' --- NEWS.md | 11 +- README.md | 217 +++++++++++++++++--------- dash.el | 290 +++++++++++++++++++++++------------ dash.texi | 271 +++++++++++++++++++++++---------- dev/examples.el | 439 ++++++++++++++++++++++++++++++++++++++++++++++------- readme-template.md | 13 -- 6 files changed, 922 insertions(+), 319 deletions(-) diff --git a/NEWS.md b/NEWS.md index ab08c1ee76..5ac57ea155 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,13 @@ See the end of the file for license conditions. ### From 2.19.1 to 2.20.0 +#### Deprecations + +- Calling `-zip` with two arguments now emits a warning. This + long-discouraged calling convention remains supported, but the + caller is now referred to the equivalent `-zip-pair` instead (Stefan + Monnier, #400). + #### Fixes - Fixed a regression from `2.18` in `-take` that caused it to @@ -132,8 +139,8 @@ https://github.com/magnars/dash.el/wiki/Obsoletion-of-dash-functional.el - Sped up `-uniq` by using hash-tables when possible (@cireu, #305). - Fixed `-inits` to be non-destructive (@SwiftLawnGnome, #313). - Fixed indent rules for `-some->` and family (@wbolster, #321). -- Added `-zip-lists` which always returns a list of proper lists, even for two - input lists (see issue #135). +- Added `-zip-lists` which always returns a list of proper lists, even + for two input lists, in contrast to `-zip` (see issue #135). ### From 2.15 to 2.16 diff --git a/README.md b/README.md index b9712bf118..e4ccf91d0e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ See the end of the file for license conditions. ## Contents * [Change log](#change-log) - * [Upcoming breaking change!](#upcoming-breaking-change) * [Installation](#installation) * [Functions](#functions) * [Contribute](#contribute) @@ -25,18 +24,6 @@ See the end of the file for license conditions. See the [`NEWS.md`](NEWS.md) file. -### Upcoming breaking change! - -- For backward compatibility reasons, `-zip` when called with two - lists returns a list of cons cells, rather than a list of proper - lists. This is a clunky API, and may be changed in a future release - to always return a list of proper lists, as `-zip-lists` currently - does. - - **N.B.:** Do not rely on the current behavior of `-zip` for two - lists. Instead, use `-zip-pair` for a list of cons cells, and - `-zip-lists` for a list of proper lists. - ## Installation Dash is available on [GNU ELPA](https://elpa.gnu.org/), [GNU-devel @@ -287,9 +274,12 @@ Other list functions not fit to be classified elsewhere. * [`-interleave`](#-interleave-rest-lists) `(&rest lists)` * [`-iota`](#-iota-count-optional-start-step) `(count &optional start step)` * [`-zip-with`](#-zip-with-fn-list1-list2) `(fn list1 list2)` -* [`-zip`](#-zip-rest-lists) `(&rest lists)` +* [`-zip-pair`](#-zip-pair-list1-list2) `(list1 list2)` * [`-zip-lists`](#-zip-lists-rest-lists) `(&rest lists)` +* [`-zip-lists-fill`](#-zip-lists-fill-fill-value-rest-lists) `(fill-value &rest lists)` +* [`-zip`](#-zip-rest-lists) `(&rest lists)` * [`-zip-fill`](#-zip-fill-fill-value-rest-lists) `(fill-value &rest lists)` +* [`-unzip-lists`](#-unzip-lists-lists) `(lists)` * [`-unzip`](#-unzip-lists) `(lists)` * [`-pad`](#-pad-fill-value-rest-lists) `(fill-value &rest lists)` * [`-table`](#-table-fn-rest-lists) `(fn &rest lists)` @@ -927,28 +917,38 @@ See also: [`-map-when`](#-map-when-pred-rep-list) #### -remove-at `(n list)` -Return a list with element at `n`th position in `list` removed. +Return `list` with its element at index `n` removed. +That is, remove any element selected as (nth `n` `list`) from `list` +and return the result. -See also: [`-remove-at-indices`](#-remove-at-indices-indices-list), [`-remove`](#-remove-pred-list) +This is a non-destructive operation: parts of `list` (but not +necessarily all of it) are copied as needed to avoid +destructively modifying it. + +See also: [`-remove-at-indices`](#-remove-at-indices-indices-list), [`-remove`](#-remove-pred-list). ```el -(-remove-at 0 '("0" "1" "2" "3" "4" "5")) ;; => ("1" "2" "3" "4" "5") -(-remove-at 1 '("0" "1" "2" "3" "4" "5")) ;; => ("0" "2" "3" "4" "5") -(-remove-at 2 '("0" "1" "2" "3" "4" "5")) ;; => ("0" "1" "3" "4" "5") +(-remove-at 0 '(a b c)) ;; => (b c) +(-remove-at 1 '(a b c)) ;; => (a c) +(-remove-at 2 '(a b c)) ;; => (a b) ``` #### -remove-at-indices `(indices list)` -Return a list whose elements are elements from `list` without -elements selected as `(nth i list)` for all i -from `indices`. +Return `list` with its elements at `indices` removed. +That is, for each index `i` in `indices`, remove any element selected +as (nth `i` `list`) from `list`. + +This is a non-destructive operation: parts of `list` (but not +necessarily all of it) are copied as needed to avoid +destructively modifying it. -See also: [`-remove-at`](#-remove-at-n-list), [`-remove`](#-remove-pred-list) +See also: [`-remove-at`](#-remove-at-n-list), [`-remove`](#-remove-pred-list). ```el -(-remove-at-indices '(0) '("0" "1" "2" "3" "4" "5")) ;; => ("1" "2" "3" "4" "5") -(-remove-at-indices '(0 2 4) '("0" "1" "2" "3" "4" "5")) ;; => ("1" "3" "5") -(-remove-at-indices '(0 5) '("0" "1" "2" "3" "4" "5")) ;; => ("1" "2" "3" "4") +(-remove-at-indices '(0) '(a b c d e)) ;; => (b c d e) +(-remove-at-indices '(1 3) '(a b c d e)) ;; => (a c e) +(-remove-at-indices '(4 0 2) '(a b c d e)) ;; => (b d) ``` ## Reductions @@ -1326,7 +1326,7 @@ from the beginning. ```el (-take 5 (-cycle '(1 2 3))) ;; => (1 2 3 1 2) (-take 7 (-cycle '(1 "and" 3))) ;; => (1 "and" 3 1 "and" 3 1) -(-zip (-cycle '(1 2 3)) '(1 2)) ;; => ((1 . 1) (2 . 2)) +(-zip-lists (-cycle '(3)) '(1 2)) ;; => ((3 1) (3 2)) ``` ## Predicates @@ -1982,53 +1982,52 @@ the `apl` language. #### -zip-with `(fn list1 list2)` -Zip the two lists `list1` and `list2` using a function `fn`. This -function is applied pairwise taking as first argument element of -`list1` and as second argument element of `list2` at corresponding -position. +Zip `list1` and `list2` into a new list using the function `fn`. +That is, apply `fn` pairwise taking as first argument the next +element of `list1` and as second argument the next element of `list2` +at the corresponding position. The result is as long as the +shorter list. + +This function's anaphoric counterpart is `--zip-with`. -The anaphoric form `--zip-with` binds the elements from `list1` as symbol `it`, -and the elements from `list2` as symbol `other`. +For other zips, see also [`-zip-lists`](#-zip-lists-rest-lists) and [`-zip-fill`](#-zip-fill-fill-value-rest-lists). ```el -(-zip-with '+ '(1 2 3) '(4 5 6)) ;; => (5 7 9) -(-zip-with 'cons '(1 2 3) '(4 5 6)) ;; => ((1 . 4) (2 . 5) (3 . 6)) -(--zip-with (concat it " and " other) '("Batman" "Jekyll") '("Robin" "Hyde")) ;; => ("Batman and Robin" "Jekyll and Hyde") +(-zip-with #'+ '(1 2 3 4) '(5 6 7)) ;; => (6 8 10) +(-zip-with #'cons '(1 2 3) '(4 5 6 7)) ;; => ((1 . 4) (2 . 5) (3 . 6)) +(--zip-with (format "%s & %s" it other) '(Batman Jekyll) '(Robin Hyde)) ;; => ("Batman & Robin" "Jekyll & Hyde") ``` -#### -zip `(&rest lists)` - -Zip `lists` together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. +#### -zip-pair `(list1 list2)` -If two lists are provided as arguments, return the groupings as a list -of cons cells. Otherwise, return the groupings as a list of lists. +Zip `list1` and `list2` together. -Use [`-zip-lists`](#-zip-lists-rest-lists) if you need the return value to always be a list -of lists. +Make a pair with the head of each list, followed by a pair with +the second element of each list, and so on. The number of pairs +returned is equal to the length of the shorter input list. -Alias: `-zip-pair` - -See also: [`-zip-lists`](#-zip-lists-rest-lists) +See also: [`-zip-lists`](#-zip-lists-rest-lists). ```el -(-zip '(1 2 3) '(4 5 6)) ;; => ((1 . 4) (2 . 5) (3 . 6)) -(-zip '(1 2 3) '(4 5 6 7)) ;; => ((1 . 4) (2 . 5) (3 . 6)) -(-zip '(1 2) '(3 4 5) '(6)) ;; => ((1 3 6)) +(-zip-pair '(1 2 3 4) '(5 6 7)) ;; => ((1 . 5) (2 . 6) (3 . 7)) +(-zip-pair '(1 2 3) '(4 5 6)) ;; => ((1 . 4) (2 . 5) (3 . 6)) +(-zip-pair '(1 2) '(3)) ;; => ((1 . 3)) ``` #### -zip-lists `(&rest lists)` -Zip `lists` together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. +Zip `lists` together. + +Group the head of each list, followed by the second element of +each list, and so on. The number of returned groupings is equal +to the length of the shortest input list, and the length of each +grouping is equal to the number of input `lists`. -The return value is always list of lists, which is a difference -from `-zip-pair` which returns a cons-cell in case two input -lists are provided. +The return value is always a list of proper lists, in contrast to +[`-zip`](#-zip-rest-lists) which returns a list of dotted pairs when only two input +`lists` are provided. -See also: [`-zip`](#-zip-rest-lists) +See also: [`-zip-pair`](#-zip-pair-list1-list2). ```el (-zip-lists '(1 2 3) '(4 5 6)) ;; => ((1 4) (2 5) (3 6)) @@ -2036,36 +2035,106 @@ See also: [`-zip`](#-zip-rest-lists) (-zip-lists '(1 2) '(3 4 5) '(6)) ;; => ((1 3 6)) ``` +#### -zip-lists-fill `(fill-value &rest lists)` + +Zip `lists` together, padding shorter lists with `fill-value`. +This is like [`-zip-lists`](#-zip-lists-rest-lists) (which see), except it retains all +elements at positions beyond the end of the shortest list. The +number of returned groupings is equal to the length of the +longest input list, and the length of each grouping is equal to +the number of input `lists`. + +```el +(-zip-lists-fill 0 '(1 2) '(3 4 5) '(6)) ;; => ((1 3 6) (2 4 0) (0 5 0)) +(-zip-lists-fill 0 '(1 2) '(3 4) '(5 6)) ;; => ((1 3 5) (2 4 6)) +(-zip-lists-fill 0 '(1 2 3) nil) ;; => ((1 0) (2 0) (3 0)) +``` + +#### -zip `(&rest lists)` + +Zip `lists` together. + +Group the head of each list, followed by the second element of +each list, and so on. The number of returned groupings is equal +to the length of the shortest input list, and the number of items +in each grouping is equal to the number of input `lists`. + +If only two `lists` are provided as arguments, return the groupings +as a list of dotted pairs. Otherwise, return the groupings as a +list of proper lists. + +Since the return value changes form depending on the number of +arguments, it is generally recommended to use [`-zip-lists`](#-zip-lists-rest-lists) +instead, or [`-zip-pair`](#-zip-pair-list1-list2) if a list of dotted pairs is desired. + +See also: [`-unzip`](#-unzip-lists). + +```el +(-zip '(1 2 3 4) '(5 6 7) '(8 9)) ;; => ((1 5 8) (2 6 9)) +(-zip '(1 2 3) '(4 5 6) '(7 8 9)) ;; => ((1 4 7) (2 5 8) (3 6 9)) +(-zip '(1 2 3)) ;; => ((1) (2) (3)) +``` + #### -zip-fill `(fill-value &rest lists)` -Zip `lists`, with `fill-value` padded onto the shorter lists. The -lengths of the returned groupings are equal to the length of the -longest input list. +Zip `lists` together, padding shorter lists with `fill-value`. +This is like [`-zip`](#-zip-rest-lists) (which see), except it retains all elements +at positions beyond the end of the shortest list. The number of +returned groupings is equal to the length of the longest input +list, and the length of each grouping is equal to the number of +input `lists`. + +Since the return value changes form depending on the number of +arguments, it is generally recommended to use [`-zip-lists-fill`](#-zip-lists-fill-fill-value-rest-lists) +instead, unless a list of dotted pairs is explicitly desired. + +```el +(-zip-fill 0 '(1 2 3) '(4 5)) ;; => ((1 . 4) (2 . 5) (3 . 0)) +(-zip-fill 0 () '(1 2 3)) ;; => ((0 . 1) (0 . 2) (0 . 3)) +(-zip-fill 0 '(1 2) '(3 4) '(5 6)) ;; => ((1 3 5) (2 4 6)) +``` + +#### -unzip-lists `(lists)` + +Unzip `lists`. + +This works just like [`-zip-lists`](#-zip-lists-rest-lists) (which see), but takes a list +of lists instead of a variable number of arguments, such that + + (-unzip-lists (-zip-lists `args`...)) + +is identity (given that the lists comprising `args` are of the same +length). ```el -(-zip-fill 0 '(1 2 3 4 5) '(6 7 8 9)) ;; => ((1 . 6) (2 . 7) (3 . 8) (4 . 9) (5 . 0)) +(-unzip-lists (-zip-lists '(1 2) '(3 4) '(5 6))) ;; => ((1 2) (3 4) (5 6)) +(-unzip-lists '((1 2 3) (4 5) (6 7) (8 9))) ;; => ((1 4 6 8) (2 5 7 9)) +(-unzip-lists '((1 2 3) (4 5 6))) ;; => ((1 4) (2 5) (3 6)) ``` #### -unzip `(lists)` Unzip `lists`. -This works just like [`-zip`](#-zip-rest-lists) but takes a list of lists instead of -a variable number of arguments, such that +This works just like [`-zip`](#-zip-rest-lists) (which see), but takes a list of +lists instead of a variable number of arguments, such that (-unzip (-zip `l1` `l2` `l3` ...)) -is identity (given that the lists are the same length). +is identity (given that the lists are of the same length, and +that [`-zip`](#-zip-rest-lists) is not called with two arguments, because of the +caveat described in its docstring). -Note in particular that calling this on a list of two lists will -return a list of cons-cells such that the above identity works. +Note in particular that calling [`-unzip`](#-unzip-lists) on a list of two lists +will return a list of dotted pairs. -See also: [`-zip`](#-zip-rest-lists) +Since the return value changes form depending on the number of +`lists`, it is generally recommended to use [`-unzip-lists`](#-unzip-lists-lists) instead. ```el -(-unzip (-zip '(1 2 3) '(a b c) '("e" "f" "g"))) ;; => ((1 2 3) (a b c) ("e" "f" "g")) -(-unzip '((1 2) (3 4) (5 6) (7 8) (9 10))) ;; => ((1 3 5 7 9) (2 4 6 8 10)) -(-unzip '((1 2) (3 4))) ;; => ((1 . 3) (2 . 4)) +(-unzip (-zip '(1 2) '(3 4) '(5 6))) ;; => ((1 . 2) (3 . 4) (5 . 6)) +(-unzip '((1 2 3) (4 5 6))) ;; => ((1 . 4) (2 . 5) (3 . 6)) +(-unzip '((1 2 3) (4 5) (6 7) (8 9))) ;; => ((1 4 6 8) (2 5 7 9)) ``` #### -pad `(fill-value &rest lists)` @@ -3204,9 +3273,9 @@ This function satisfies the following laws: = (-compose fn (-partial #'nth n)) ```el -(funcall (-prodfn '1+ '1- 'number-to-string) '(1 2 3)) ;; => (2 1 "3") -(-map (-prodfn '1+ '1-) '((1 2) (3 4) (5 6) (7 8))) ;; => ((2 1) (4 3) (6 5) (8 7)) -(apply '+ (funcall (-prodfn 'length 'string-to-number) '((1 2 3) "15"))) ;; => 18 +(funcall (-prodfn #'1+ #'1- #'number-to-string) '(1 2 3)) ;; => (2 1 "3") +(-map (-prodfn #'1- #'1+) '((1 2) (3 4) (5 6))) ;; => ((0 3) (2 5) (4 7)) +(apply #'+ (funcall (-prodfn #'length #'string-to-number) '((t) "5"))) ;; => 6 ``` ## Contribute diff --git a/dash.el b/dash.el index 6efdc24ea4..ef4f9d3263 100644 --- a/dash.el +++ b/dash.el @@ -1,6 +1,6 @@ ;;; dash.el --- A modern list library for Emacs -*- lexical-binding: t -*- -;; Copyright (C) 2012-2021 Free Software Foundation, Inc. +;; Copyright (C) 2012-2022 Free Software Foundation, Inc. ;; Author: Magnar Sveen <magn...@gmail.com> ;; Version: 2.19.1 @@ -536,7 +536,8 @@ This function's anaphoric counterpart is `--remove-first'. See also `-map-first', `-remove-item', and `-remove-last'." (--remove-first (funcall pred it) list)) -(defalias '-reject-first '-remove-first) +;; TODO: #'-quoting the macro upsets Emacs 24. +(defalias '-reject-first #'-remove-first) (defalias '--reject-first '--remove-first) (defmacro --remove-last (form list) @@ -706,7 +707,7 @@ See also: `-map-last'" (defmacro --mapcat (form list) "Anaphoric form of `-mapcat'." (declare (debug (form form))) - `(apply 'append (--map ,form ,list))) + `(apply #'append (--map ,form ,list))) (defun -mapcat (fn list) "Return the concatenation of the result of mapping FN over LIST. @@ -772,7 +773,7 @@ is just used as the tail of the new list. \(fn &rest SEQUENCES)") -(defalias '-copy 'copy-sequence +(defalias '-copy #'copy-sequence "Create a shallow copy of LIST. \(fn LIST)") @@ -1298,28 +1299,41 @@ See also: `-map-when'" `(-update-at ,n (lambda (it) (ignore it) ,form) ,list)) (defun -remove-at (n list) - "Return a list with element at Nth position in LIST removed. + "Return LIST with its element at index N removed. +That is, remove any element selected as (nth N LIST) from LIST +and return the result. -See also: `-remove-at-indices', `-remove'" +This is a non-destructive operation: parts of LIST (but not +necessarily all of it) are copied as needed to avoid +destructively modifying it. + +See also: `-remove-at-indices', `-remove'." (declare (pure t) (side-effect-free t)) - (-remove-at-indices (list n) list)) + (if (zerop n) + (cdr list) + (--remove-first (= it-index n) list))) (defun -remove-at-indices (indices list) - "Return a list whose elements are elements from LIST without -elements selected as `(nth i list)` for all i -from INDICES. + "Return LIST with its elements at INDICES removed. +That is, for each index I in INDICES, remove any element selected +as (nth I LIST) from LIST. + +This is a non-destructive operation: parts of LIST (but not +necessarily all of it) are copied as needed to avoid +destructively modifying it. -See also: `-remove-at', `-remove'" +See also: `-remove-at', `-remove'." (declare (pure t) (side-effect-free t)) - (let* ((indices (-sort '< indices)) - (diffs (cons (car indices) (-map '1- (-zip-with '- (cdr indices) indices)))) - r) - (--each diffs - (let ((split (-split-at it list))) - (!cons (car split) r) - (setq list (cdr (cadr split))))) - (!cons list r) - (apply '-concat (nreverse r)))) + (setq indices (--drop-while (< it 0) (-sort #'< indices))) + (let ((i (pop indices)) res) + (--each-while list i + (pop list) + (if (/= it-index i) + (push it res) + (while (and indices (= (car indices) i)) + (pop indices)) + (setq i (pop indices)))) + (nconc (nreverse res) list))) (defmacro --split-with (pred list) "Anaphoric form of `-split-with'." @@ -1603,104 +1617,193 @@ elements of LIST. Keys are compared by `equal'." (nreverse result)))) (defmacro --zip-with (form list1 list2) - "Anaphoric form of `-zip-with'. + "Zip LIST1 and LIST2 into a new list according to FORM. +That is, evaluate FORM for each item pair from the two lists, and +return the list of results. The result is as long as the shorter +list. -Each element in turn of LIST1 is bound to `it', and of LIST2 to -`other', before evaluating FORM." +Each element of LIST1 and each element of LIST2 in turn are bound +pairwise to `it' and `other', respectively, and their index +within the list to `it-index', before evaluating FORM. + +This is the anaphoric counterpart to `-zip-with'." (declare (debug (form form form))) (let ((r (make-symbol "result")) - (l1 (make-symbol "list1")) (l2 (make-symbol "list2"))) - `(let ((,r nil) - (,l1 ,list1) - (,l2 ,list2)) - (while (and ,l1 ,l2) - (let ((it (car ,l1)) - (other (car ,l2))) - (!cons ,form ,r) - (!cdr ,l1) - (!cdr ,l2))) + `(let ((,l2 ,list2) ,r) + (--each-while ,list1 ,l2 + (let ((other (pop ,l2))) + (ignore other) + (push ,form ,r))) (nreverse ,r)))) (defun -zip-with (fn list1 list2) - "Zip the two lists LIST1 and LIST2 using a function FN. This -function is applied pairwise taking as first argument element of -LIST1 and as second argument element of LIST2 at corresponding -position. + "Zip LIST1 and LIST2 into a new list using the function FN. +That is, apply FN pairwise taking as first argument the next +element of LIST1 and as second argument the next element of LIST2 +at the corresponding position. The result is as long as the +shorter list. + +This function's anaphoric counterpart is `--zip-with'. -The anaphoric form `--zip-with' binds the elements from LIST1 as symbol `it', -and the elements from LIST2 as symbol `other'." +For other zips, see also `-zip-lists' and `-zip-fill'." (--zip-with (funcall fn it other) list1 list2)) (defun -zip-lists (&rest lists) - "Zip LISTS together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. + "Zip LISTS together. -The return value is always list of lists, which is a difference -from `-zip-pair' which returns a cons-cell in case two input -lists are provided. +Group the head of each list, followed by the second element of +each list, and so on. The number of returned groupings is equal +to the length of the shortest input list, and the length of each +grouping is equal to the number of input LISTS. -See also: `-zip'" +The return value is always a list of proper lists, in contrast to +`-zip' which returns a list of dotted pairs when only two input +LISTS are provided. + +See also: `-zip-pair'." (declare (pure t) (side-effect-free t)) (when lists (let (results) - (while (-none? 'null lists) - (setq results (cons (mapcar 'car lists) results)) - (setq lists (mapcar 'cdr lists))) + (while (--every it lists) + (push (mapcar #'car lists) results) + (setq lists (mapcar #'cdr lists))) (nreverse results)))) -(defun -zip (&rest lists) - "Zip LISTS together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. +(defun -zip-lists-fill (fill-value &rest lists) + "Zip LISTS together, padding shorter lists with FILL-VALUE. +This is like `-zip-lists' (which see), except it retains all +elements at positions beyond the end of the shortest list. The +number of returned groupings is equal to the length of the +longest input list, and the length of each grouping is equal to +the number of input LISTS." + (declare (pure t) (side-effect-free t)) + (when lists + (let (results) + (while (--some it lists) + (push (--map (if it (car it) fill-value) lists) results) + (setq lists (mapcar #'cdr lists))) + (nreverse results)))) -If two lists are provided as arguments, return the groupings as a list -of cons cells. Otherwise, return the groupings as a list of lists. +(defun -unzip-lists (lists) + "Unzip LISTS. -Use `-zip-lists' if you need the return value to always be a list -of lists. +This works just like `-zip-lists' (which see), but takes a list +of lists instead of a variable number of arguments, such that -Alias: `-zip-pair' + (-unzip-lists (-zip-lists ARGS...)) -See also: `-zip-lists'" +is identity (given that the lists comprising ARGS are of the same +length)." (declare (pure t) (side-effect-free t)) - (when lists - (let (results) - (while (-none? 'null lists) - (setq results (cons (mapcar 'car lists) results)) - (setq lists (mapcar 'cdr lists))) - (setq results (nreverse results)) - (if (= (length lists) 2) - ;; to support backward compatibility, return - ;; a cons cell if two lists were provided - (--map (cons (car it) (cadr it)) results) - results)))) + (apply #'-zip-lists lists)) + +(defalias 'dash--length= + (if (fboundp 'length=) + #'length= + (lambda (list length) + (cond ((< length 0) nil) + ((zerop length) (null list)) + ((let ((last (nthcdr (1- length) list))) + (and last (null (cdr last)))))))) + "Return non-nil if LIST is of LENGTH. +This is a compatibility shim for `length=' in Emacs 28. +\n(fn LIST LENGTH)") + +(defun dash--zip-lists-or-pair (_form &rest lists) + "Return a form equivalent to applying `-zip' to LISTS. +This `compiler-macro' warns about discouraged `-zip' usage and +delegates to `-zip-lists' or `-zip-pair' depending on the number +of LISTS." + (if (not (dash--length= lists 2)) + (cons #'-zip-lists lists) + (let ((pair (cons #'-zip-pair lists)) + (msg "Use -zip-pair instead of -zip to get a list of pairs")) + (if (fboundp 'macroexp-warn-and-return) + (macroexp-warn-and-return msg pair) + (message msg) + pair)))) -(defalias '-zip-pair '-zip) +(defun -zip (&rest lists) + "Zip LISTS together. + +Group the head of each list, followed by the second element of +each list, and so on. The number of returned groupings is equal +to the length of the shortest input list, and the number of items +in each grouping is equal to the number of input LISTS. + +If only two LISTS are provided as arguments, return the groupings +as a list of dotted pairs. Otherwise, return the groupings as a +list of proper lists. + +Since the return value changes form depending on the number of +arguments, it is generally recommended to use `-zip-lists' +instead, or `-zip-pair' if a list of dotted pairs is desired. + +See also: `-unzip'." + (declare (compiler-macro dash--zip-lists-or-pair) + (pure t) (side-effect-free t)) + ;; For backward compatibility, return a list of dotted pairs if two + ;; arguments were provided. + (apply (if (dash--length= lists 2) #'-zip-pair #'-zip-lists) lists)) + +(defun -zip-pair (&rest lists) + "Zip LIST1 and LIST2 together. + +Make a pair with the head of each list, followed by a pair with +the second element of each list, and so on. The number of pairs +returned is equal to the length of the shorter input list. + +See also: `-zip-lists'." + (declare (advertised-calling-convention (list1 list2) "2.20.0") + (pure t) (side-effect-free t)) + (if (dash--length= lists 2) + (--zip-with (cons it other) (car lists) (cadr lists)) + (apply #'-zip-lists lists))) (defun -zip-fill (fill-value &rest lists) - "Zip LISTS, with FILL-VALUE padded onto the shorter lists. The -lengths of the returned groupings are equal to the length of the -longest input list." + "Zip LISTS together, padding shorter lists with FILL-VALUE. +This is like `-zip' (which see), except it retains all elements +at positions beyond the end of the shortest list. The number of +returned groupings is equal to the length of the longest input +list, and the length of each grouping is equal to the number of +input LISTS. + +Since the return value changes form depending on the number of +arguments, it is generally recommended to use `-zip-lists-fill' +instead, unless a list of dotted pairs is explicitly desired." (declare (pure t) (side-effect-free t)) - (apply '-zip (apply '-pad (cons fill-value lists)))) + (cond ((null lists) ()) + ((dash--length= lists 2) + (let ((list1 (car lists)) + (list2 (cadr lists)) + results) + (while (or list1 list2) + (push (cons (if list1 (pop list1) fill-value) + (if list2 (pop list2) fill-value)) + results)) + (nreverse results))) + ((apply #'-zip-lists-fill fill-value lists)))) (defun -unzip (lists) "Unzip LISTS. -This works just like `-zip' but takes a list of lists instead of -a variable number of arguments, such that +This works just like `-zip' (which see), but takes a list of +lists instead of a variable number of arguments, such that (-unzip (-zip L1 L2 L3 ...)) -is identity (given that the lists are the same length). +is identity (given that the lists are of the same length, and +that `-zip' is not called with two arguments, because of the +caveat described in its docstring). -Note in particular that calling this on a list of two lists will -return a list of cons-cells such that the above identity works. +Note in particular that calling `-unzip' on a list of two lists +will return a list of dotted pairs. -See also: `-zip'" - (apply '-zip lists)) +Since the return value changes form depending on the number of +LISTS, it is generally recommended to use `-unzip-lists' instead." + (declare (pure t) (side-effect-free t)) + (apply #'-zip lists)) (defun -cycle (list) "Return an infinite circular copy of LIST. @@ -2216,7 +2319,7 @@ This method normalizes PATTERN to the format expected by (let ((normalized (list (car pattern))) (skip nil) (fill-placeholder (make-symbol "--dash-fill-placeholder--"))) - (-each (apply '-zip (-pad fill-placeholder (cdr pattern) (cddr pattern))) + (-each (-zip-fill fill-placeholder (cdr pattern) (cddr pattern)) (lambda (pair) (let ((current (car pair)) (next (cdr pair))) @@ -2555,7 +2658,8 @@ because we need to support improper list binding." ,@body) (let* ((varlist (dash--normalize-let-varlist varlist)) (inputs (--map-indexed (list (make-symbol (format "input%d" it-index)) (cadr it)) varlist)) - (new-varlist (--map (list (caar it) (cadr it)) (-zip varlist inputs)))) + (new-varlist (--zip-with (list (car it) (car other)) + varlist inputs))) `(let ,inputs (-let* ,new-varlist ,@body))))) @@ -3156,7 +3260,7 @@ Return nil if N is less than 1." (defun -sum (list) "Return the sum of LIST." (declare (pure t) (side-effect-free t)) - (apply '+ list)) + (apply #'+ list)) (defun -running-sum (list) "Return a list with running sums of items in LIST. @@ -3168,7 +3272,7 @@ LIST must be non-empty." (defun -product (list) "Return the product of LIST." (declare (pure t) (side-effect-free t)) - (apply '* list)) + (apply #'* list)) (defun -running-product (list) "Return a list with running products of items in LIST. @@ -3180,12 +3284,12 @@ LIST must be non-empty." (defun -max (list) "Return the largest value from LIST of numbers or markers." (declare (pure t) (side-effect-free t)) - (apply 'max list)) + (apply #'max list)) (defun -min (list) "Return the smallest value from LIST of numbers or markers." (declare (pure t) (side-effect-free t)) - (apply 'min list)) + (apply #'min list)) (defun -max-by (comparator list) "Take a comparison function COMPARATOR and a LIST and return @@ -3730,7 +3834,8 @@ This function satisfies the following laws: (-compose (-partial #\\='nth n) (-prod f1 f2 ...)) = (-compose fn (-partial #\\='nth n))" - (lambda (x) (-zip-with 'funcall fns x))) + (declare (pure t) (side-effect-free t)) + (lambda (x) (--zip-with (funcall it other) fns x))) ;;; Font lock @@ -3841,7 +3946,6 @@ Either a string to display in the mode line when `dash-fontify-mode' is on, or nil to display nothing (the default)." :package-version '(dash . "2.18.0") - :group 'dash :type '(choice (string :tag "Lighter" :value " Dash") (const :tag "Nothing" nil))) @@ -3858,7 +3962,7 @@ additionally fontifies Dash macro calls. See also `dash-fontify-mode-lighter' and `global-dash-fontify-mode'." - :group 'dash :lighter dash-fontify-mode-lighter + :lighter dash-fontify-mode-lighter (if dash-fontify-mode (font-lock-add-keywords nil dash--keywords t) (font-lock-remove-keywords nil dash--keywords)) @@ -3879,12 +3983,10 @@ See also `dash-fontify-mode-lighter' and ;;;###autoload (define-globalized-minor-mode global-dash-fontify-mode - dash-fontify-mode dash--turn-on-fontify-mode - :group 'dash) + dash-fontify-mode dash--turn-on-fontify-mode) (defcustom dash-enable-fontlock nil "If non-nil, fontify Dash macro calls and special variables." - :group 'dash :set (lambda (sym val) (set-default sym val) (global-dash-fontify-mode (if val 1 0))) diff --git a/dash.texi b/dash.texi index 9226c304de..529294c60d 100644 --- a/dash.texi +++ b/dash.texi @@ -1120,46 +1120,56 @@ See also: @code{-map-when} (@pxref{-map-when}) @anchor{-remove-at} @defun -remove-at (n list) -Return a list with element at Nth position in @var{list} removed. +Return @var{list} with its element at index @var{n} removed. +That is, remove any element selected as (nth @var{n} @var{list}) from @var{list} +and return the result. -See also: @code{-remove-at-indices} (@pxref{-remove-at-indices}), @code{-remove} (@pxref{-remove}) +This is a non-destructive operation: parts of @var{list} (but not +necessarily all of it) are copied as needed to avoid +destructively modifying it. + +See also: @code{-remove-at-indices} (@pxref{-remove-at-indices}), @code{-remove} (@pxref{-remove}). @example @group -(-remove-at 0 '("0" "1" "2" "3" "4" "5")) - @result{} ("1" "2" "3" "4" "5") +(-remove-at 0 '(a b c)) + @result{} (b c) @end group @group -(-remove-at 1 '("0" "1" "2" "3" "4" "5")) - @result{} ("0" "2" "3" "4" "5") +(-remove-at 1 '(a b c)) + @result{} (a c) @end group @group -(-remove-at 2 '("0" "1" "2" "3" "4" "5")) - @result{} ("0" "1" "3" "4" "5") +(-remove-at 2 '(a b c)) + @result{} (a b) @end group @end example @end defun @anchor{-remove-at-indices} @defun -remove-at-indices (indices list) -Return a list whose elements are elements from @var{list} without -elements selected as `(nth i list)` for all i -from @var{indices}. +Return @var{list} with its elements at @var{indices} removed. +That is, for each index @var{i} in @var{indices}, remove any element selected +as (nth @var{i} @var{list}) from @var{list}. + +This is a non-destructive operation: parts of @var{list} (but not +necessarily all of it) are copied as needed to avoid +destructively modifying it. -See also: @code{-remove-at} (@pxref{-remove-at}), @code{-remove} (@pxref{-remove}) +See also: @code{-remove-at} (@pxref{-remove-at}), @code{-remove} (@pxref{-remove}). @example @group -(-remove-at-indices '(0) '("0" "1" "2" "3" "4" "5")) - @result{} ("1" "2" "3" "4" "5") +(-remove-at-indices '(0) '(a b c d e)) + @result{} (b c d e) @end group @group -(-remove-at-indices '(0 2 4) '("0" "1" "2" "3" "4" "5")) - @result{} ("1" "3" "5") +(-remove-at-indices '(1 3) '(a b c d e)) + @result{} (a c e) @end group @group -(-remove-at-indices '(0 5) '("0" "1" "2" "3" "4" "5")) - @result{} ("1" "2" "3" "4") +(-remove-at-indices '(4 0 2) '(a b c d e)) + @result{} (b d) @end group @end example @end defun @@ -1795,8 +1805,8 @@ from the beginning. @result{} (1 "and" 3 1 "and" 3 1) @end group @group -(-zip (-cycle '(1 2 3)) '(1 2)) - @result{} ((1 . 1) (2 . 2)) +(-zip-lists (-cycle '(3)) '(1 2)) + @result{} ((3 1) (3 2)) @end group @end example @end defun @@ -2923,73 +2933,72 @@ the @var{apl} language. @anchor{-zip-with} @defun -zip-with (fn list1 list2) -Zip the two lists @var{list1} and @var{list2} using a function @var{fn}. This -function is applied pairwise taking as first argument element of -@var{list1} and as second argument element of @var{list2} at corresponding -position. +Zip @var{list1} and @var{list2} into a new list using the function @var{fn}. +That is, apply @var{fn} pairwise taking as first argument the next +element of @var{list1} and as second argument the next element of @var{list2} +at the corresponding position. The result is as long as the +shorter list. -The anaphoric form @code{--zip-with} binds the elements from @var{list1} as symbol @code{it}, -and the elements from @var{list2} as symbol @code{other}. +This function's anaphoric counterpart is @code{--zip-with}. + +For other zips, see also @code{-zip-lists} (@pxref{-zip-lists}) and @code{-zip-fill} (@pxref{-zip-fill}). @example @group -(-zip-with '+ '(1 2 3) '(4 5 6)) - @result{} (5 7 9) +(-zip-with #'+ '(1 2 3 4) '(5 6 7)) + @result{} (6 8 10) @end group @group -(-zip-with 'cons '(1 2 3) '(4 5 6)) +(-zip-with #'cons '(1 2 3) '(4 5 6 7)) @result{} ((1 . 4) (2 . 5) (3 . 6)) @end group @group -(--zip-with (concat it " and " other) '("Batman" "Jekyll") '("Robin" "Hyde")) - @result{} ("Batman and Robin" "Jekyll and Hyde") +(--zip-with (format "%s & %s" it other) '(Batman Jekyll) '(Robin Hyde)) + @result{} ("Batman & Robin" "Jekyll & Hyde") @end group @end example @end defun -@anchor{-zip} -@defun -zip (&rest lists) -Zip @var{lists} together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. - -If two lists are provided as arguments, return the groupings as a list -of cons cells. Otherwise, return the groupings as a list of lists. +@anchor{-zip-pair} +@defun -zip-pair (list1 list2) +Zip @var{list1} and @var{list2} together. -Use @code{-zip-lists} (@pxref{-zip-lists}) if you need the return value to always be a list -of lists. +Make a pair with the head of each list, followed by a pair with +the second element of each list, and so on. The number of pairs +returned is equal to the length of the shorter input list. -Alias: @code{-zip-pair} - -See also: @code{-zip-lists} (@pxref{-zip-lists}) +See also: @code{-zip-lists} (@pxref{-zip-lists}). @example @group -(-zip '(1 2 3) '(4 5 6)) - @result{} ((1 . 4) (2 . 5) (3 . 6)) +(-zip-pair '(1 2 3 4) '(5 6 7)) + @result{} ((1 . 5) (2 . 6) (3 . 7)) @end group @group -(-zip '(1 2 3) '(4 5 6 7)) +(-zip-pair '(1 2 3) '(4 5 6)) @result{} ((1 . 4) (2 . 5) (3 . 6)) @end group @group -(-zip '(1 2) '(3 4 5) '(6)) - @result{} ((1 3 6)) +(-zip-pair '(1 2) '(3)) + @result{} ((1 . 3)) @end group @end example @end defun @anchor{-zip-lists} @defun -zip-lists (&rest lists) -Zip @var{lists} together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. +Zip @var{lists} together. + +Group the head of each list, followed by the second element of +each list, and so on. The number of returned groupings is equal +to the length of the shortest input list, and the length of each +grouping is equal to the number of input @var{lists}. -The return value is always list of lists, which is a difference -from @code{-zip-pair} which returns a cons-cell in case two input -lists are provided. +The return value is always a list of proper lists, in contrast to +@code{-zip} (@pxref{-zip}) which returns a list of dotted pairs when only two input +@var{lists} are provided. -See also: @code{-zip} (@pxref{-zip}) +See also: @code{-zip-pair} (@pxref{-zip-pair}). @example @group @@ -3007,16 +3016,119 @@ See also: @code{-zip} (@pxref{-zip}) @end example @end defun +@anchor{-zip-lists-fill} +@defun -zip-lists-fill (fill-value &rest lists) +Zip @var{lists} together, padding shorter lists with @var{fill-value}. +This is like @code{-zip-lists} (@pxref{-zip-lists}) (which see), except it retains all +elements at positions beyond the end of the shortest list. The +number of returned groupings is equal to the length of the +longest input list, and the length of each grouping is equal to +the number of input @var{lists}. + +@example +@group +(-zip-lists-fill 0 '(1 2) '(3 4 5) '(6)) + @result{} ((1 3 6) (2 4 0) (0 5 0)) +@end group +@group +(-zip-lists-fill 0 '(1 2) '(3 4) '(5 6)) + @result{} ((1 3 5) (2 4 6)) +@end group +@group +(-zip-lists-fill 0 '(1 2 3) nil) + @result{} ((1 0) (2 0) (3 0)) +@end group +@end example +@end defun + +@anchor{-zip} +@defun -zip (&rest lists) +Zip @var{lists} together. + +Group the head of each list, followed by the second element of +each list, and so on. The number of returned groupings is equal +to the length of the shortest input list, and the number of items +in each grouping is equal to the number of input @var{lists}. + +If only two @var{lists} are provided as arguments, return the groupings +as a list of dotted pairs. Otherwise, return the groupings as a +list of proper lists. + +Since the return value changes form depending on the number of +arguments, it is generally recommended to use @code{-zip-lists} (@pxref{-zip-lists}) +instead, or @code{-zip-pair} (@pxref{-zip-pair}) if a list of dotted pairs is desired. + +See also: @code{-unzip} (@pxref{-unzip}). + +@example +@group +(-zip '(1 2 3 4) '(5 6 7) '(8 9)) + @result{} ((1 5 8) (2 6 9)) +@end group +@group +(-zip '(1 2 3) '(4 5 6) '(7 8 9)) + @result{} ((1 4 7) (2 5 8) (3 6 9)) +@end group +@group +(-zip '(1 2 3)) + @result{} ((1) (2) (3)) +@end group +@end example +@end defun + @anchor{-zip-fill} @defun -zip-fill (fill-value &rest lists) -Zip @var{lists}, with @var{fill-value} padded onto the shorter lists. The -lengths of the returned groupings are equal to the length of the -longest input list. +Zip @var{lists} together, padding shorter lists with @var{fill-value}. +This is like @code{-zip} (@pxref{-zip}) (which see), except it retains all elements +at positions beyond the end of the shortest list. The number of +returned groupings is equal to the length of the longest input +list, and the length of each grouping is equal to the number of +input @var{lists}. + +Since the return value changes form depending on the number of +arguments, it is generally recommended to use @code{-zip-lists-fill} (@pxref{-zip-lists-fill}) +instead, unless a list of dotted pairs is explicitly desired. @example @group -(-zip-fill 0 '(1 2 3 4 5) '(6 7 8 9)) - @result{} ((1 . 6) (2 . 7) (3 . 8) (4 . 9) (5 . 0)) +(-zip-fill 0 '(1 2 3) '(4 5)) + @result{} ((1 . 4) (2 . 5) (3 . 0)) +@end group +@group +(-zip-fill 0 () '(1 2 3)) + @result{} ((0 . 1) (0 . 2) (0 . 3)) +@end group +@group +(-zip-fill 0 '(1 2) '(3 4) '(5 6)) + @result{} ((1 3 5) (2 4 6)) +@end group +@end example +@end defun + +@anchor{-unzip-lists} +@defun -unzip-lists (lists) +Unzip @var{lists}. + +This works just like @code{-zip-lists} (@pxref{-zip-lists}) (which see), but takes a list +of lists instead of a variable number of arguments, such that + + (-unzip-lists (-zip-lists @var{args}@dots{})) + +is identity (given that the lists comprising @var{args} are of the same +length). + +@example +@group +(-unzip-lists (-zip-lists '(1 2) '(3 4) '(5 6))) + @result{} ((1 2) (3 4) (5 6)) +@end group +@group +(-unzip-lists '((1 2 3) (4 5) (6 7) (8 9))) + @result{} ((1 4 6 8) (2 5 7 9)) +@end group +@group +(-unzip-lists '((1 2 3) (4 5 6))) + @result{} ((1 4) (2 5) (3 6)) @end group @end example @end defun @@ -3025,30 +3137,33 @@ longest input list. @defun -unzip (lists) Unzip @var{lists}. -This works just like @code{-zip} (@pxref{-zip}) but takes a list of lists instead of -a variable number of arguments, such that +This works just like @code{-zip} (@pxref{-zip}) (which see), but takes a list of +lists instead of a variable number of arguments, such that (-unzip (-zip @var{l1} @var{l2} @var{l3} @dots{})) -is identity (given that the lists are the same length). +is identity (given that the lists are of the same length, and +that @code{-zip} (@pxref{-zip}) is not called with two arguments, because of the +caveat described in its docstring). -Note in particular that calling this on a list of two lists will -return a list of cons-cells such that the above identity works. +Note in particular that calling @code{-unzip} (@pxref{-unzip}) on a list of two lists +will return a list of dotted pairs. -See also: @code{-zip} (@pxref{-zip}) +Since the return value changes form depending on the number of +@var{lists}, it is generally recommended to use @code{-unzip-lists} (@pxref{-unzip-lists}) instead. @example @group -(-unzip (-zip '(1 2 3) '(a b c) '("e" "f" "g"))) - @result{} ((1 2 3) (a b c) ("e" "f" "g")) +(-unzip (-zip '(1 2) '(3 4) '(5 6))) + @result{} ((1 . 2) (3 . 4) (5 . 6)) @end group @group -(-unzip '((1 2) (3 4) (5 6) (7 8) (9 10))) - @result{} ((1 3 5 7 9) (2 4 6 8 10)) +(-unzip '((1 2 3) (4 5 6))) + @result{} ((1 . 4) (2 . 5) (3 . 6)) @end group @group -(-unzip '((1 2) (3 4))) - @result{} ((1 . 3) (2 . 4)) +(-unzip '((1 2 3) (4 5) (6 7) (8 9))) + @result{} ((1 4 6 8) (2 5 7 9)) @end group @end example @end defun @@ -4792,16 +4907,16 @@ This function satisfies the following laws: @example @group -(funcall (-prodfn '1+ '1- 'number-to-string) '(1 2 3)) +(funcall (-prodfn #'1+ #'1- #'number-to-string) '(1 2 3)) @result{} (2 1 "3") @end group @group -(-map (-prodfn '1+ '1-) '((1 2) (3 4) (5 6) (7 8))) - @result{} ((2 1) (4 3) (6 5) (8 7)) +(-map (-prodfn #'1- #'1+) '((1 2) (3 4) (5 6))) + @result{} ((0 3) (2 5) (4 7)) @end group @group -(apply '+ (funcall (-prodfn 'length 'string-to-number) '((1 2 3) "15"))) - @result{} 18 +(apply #'+ (funcall (-prodfn #'length #'string-to-number) '((t) "5"))) + @result{} 6 @end group @end example @end defun diff --git a/dev/examples.el b/dev/examples.el index 33cef8bb58..aca7c69cea 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -466,34 +466,145 @@ new list." (--update-at 2 (concat it "zab") '("foo" "bar" "baz" "quux")) => '("foo" "bar" "bazzab" "quux")) (defexamples -remove-at - (-remove-at 0 '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4" "5") - (-remove-at 1 '("0" "1" "2" "3" "4" "5")) => '("0" "2" "3" "4" "5") - (-remove-at 2 '("0" "1" "2" "3" "4" "5")) => '("0" "1" "3" "4" "5") - (-remove-at 3 '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "4" "5") - (-remove-at 4 '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "3" "5") - (-remove-at 5 '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "3" "4") - (-remove-at 5 '((a b) (c d) (e f g) h i ((j) k) l (m))) => '((a b) (c d) (e f g) h i l (m)) - (-remove-at 0 '(((a b) (c d) (e f g) h i ((j) k) l (m)))) => nil) + (-remove-at 0 '(a b c)) => '(b c) + (-remove-at 1 '(a b c)) => '(a c) + (-remove-at 2 '(a b c)) => '(a b) + (-remove-at -1 ()) => () + (-remove-at 0 ()) => () + (-remove-at 1 ()) => () + (-remove-at -1 '(a)) => '(a) + (-remove-at 0 '(a)) => () + (-remove-at 1 '(a)) => '(a) + (-remove-at -1 '(a b)) => '(a b) + (-remove-at 0 '(a b)) => '(b) + (-remove-at 1 '(a b)) => '(a) + (-remove-at 2 '(a b)) => '(a b) + (-remove-at 0 '((a))) => () + (-remove-at 2 '((a) b (c . d) e)) => '((a) b e) + ;; Test for destructive modification. + (let ((l (list 0))) (ignore (-remove-at -1 l)) l) => '(0) + (let ((l (list 0))) (ignore (-remove-at 0 l)) l) => '(0) + (let ((l (list 0))) (ignore (-remove-at 1 l)) l) => '(0) + (let ((l (list 0 1))) (ignore (-remove-at -1 l)) l) => '(0 1) + (let ((l (list 0 1))) (ignore (-remove-at 0 l)) l) => '(0 1) + (let ((l (list 0 1))) (ignore (-remove-at 1 l)) l) => '(0 1) + (let ((l (list 0 1))) (ignore (-remove-at 2 l)) l) => '(0 1)) (defexamples -remove-at-indices - (-remove-at-indices '(0) '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4" "5") - (-remove-at-indices '(0 2 4) '("0" "1" "2" "3" "4" "5")) => '("1" "3" "5") - (-remove-at-indices '(0 5) '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4") - (-remove-at-indices '(1 2 3) '("0" "1" "2" "3" "4" "5")) => '("0" "4" "5") - (-remove-at-indices '(0 1 2 3 4 5) '("0" "1" "2" "3" "4" "5")) => nil - (-remove-at-indices '(2 0 4) '("0" "1" "2" "3" "4" "5")) => '("1" "3" "5") - (-remove-at-indices '(5 0) '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4") - (-remove-at-indices '(1 3 2) '("0" "1" "2" "3" "4" "5")) => '("0" "4" "5") - (-remove-at-indices '(0 3 4 2 5 1) '("0" "1" "2" "3" "4" "5")) => nil - (-remove-at-indices '(1) '("0" "1" "2" "3" "4" "5")) => '("0" "2" "3" "4" "5") - (-remove-at-indices '(2) '("0" "1" "2" "3" "4" "5")) => '("0" "1" "3" "4" "5") - (-remove-at-indices '(3) '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "4" "5") - (-remove-at-indices '(4) '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "3" "5") - (-remove-at-indices '(5) '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "3" "4") - (-remove-at-indices '(1 2 4) '((a b) (c d) (e f g) h i ((j) k) l (m))) => '((a b) h ((j) k) l (m)) - (-remove-at-indices '(5) '((a b) (c d) (e f g) h i ((j) k) l (m))) => '((a b) (c d) (e f g) h i l (m)) - (-remove-at-indices '(0) '(((a b) (c d) (e f g) h i ((j) k) l (m)))) => nil - (-remove-at-indices '(2 3) '((0) (1) (2) (3) (4) (5) (6))) => '((0) (1) (4) (5) (6)))) + (-remove-at-indices '(0) '(a b c d e)) => '(b c d e) + (-remove-at-indices '(1 3) '(a b c d e)) => '(a c e) + (-remove-at-indices '(4 0 2) '(a b c d e)) => '(b d) + (-remove-at-indices () ()) => () + (-remove-at-indices '(-1) ()) => () + (-remove-at-indices '(0) ()) => () + (-remove-at-indices '(-1 0) ()) => () + (-remove-at-indices '(0 -1) ()) => () + (-remove-at-indices '(-1 -1) ()) => () + (-remove-at-indices '(0 0) ()) => () + (-remove-at-indices () '(a)) => '(a) + (-remove-at-indices '(-1) '(a)) => '(a) + (-remove-at-indices '(0) '(a)) => () + (-remove-at-indices '(1) '(a)) => '(a) + (-remove-at-indices '(-1 -1) '(a)) => '(a) + (-remove-at-indices '(-1 0) '(a)) => () + (-remove-at-indices '(0 -1) '(a)) => () + (-remove-at-indices '(-1 1) '(a)) => '(a) + (-remove-at-indices '(1 -1) '(a)) => '(a) + (-remove-at-indices '(0 0) '(a)) => () + (-remove-at-indices '(0 1) '(a)) => () + (-remove-at-indices '(1 0) '(a)) => () + (-remove-at-indices '(1 1) '(a)) => '(a) + (-remove-at-indices () '(a b)) => '(a b) + (-remove-at-indices '(-1) '(a b)) => '(a b) + (-remove-at-indices '(0) '(a b)) => '(b) + (-remove-at-indices '(1) '(a b)) => '(a) + (-remove-at-indices '(2) '(a b)) => '(a b) + (-remove-at-indices '(-2 -1) '(a b)) => '(a b) + (-remove-at-indices '(-1 -1) '(a b)) => '(a b) + (-remove-at-indices '(-1 0) '(a b)) => '(b) + (-remove-at-indices '(0 -1) '(a b)) => '(b) + (-remove-at-indices '(-1 1) '(a b)) => '(a) + (-remove-at-indices '(1 -1) '(a b)) => '(a) + (-remove-at-indices '(-1 2) '(a b)) => '(a b) + (-remove-at-indices '(2 -1) '(a b)) => '(a b) + (-remove-at-indices '(0 0) '(a b)) => '(b) + (-remove-at-indices '(0 1) '(a b)) => () + (-remove-at-indices '(1 0) '(a b)) => () + (-remove-at-indices '(0 2) '(a b)) => '(b) + (-remove-at-indices '(2 0) '(a b)) => '(b) + (-remove-at-indices '(1 1) '(a b)) => '(a) + (-remove-at-indices '(1 2) '(a b)) => '(a) + (-remove-at-indices '(2 1) '(a b)) => '(a) + (-remove-at-indices '(2 2) '(a b)) => '(a b) + (-remove-at-indices '(-1 0 0) '(a b)) => '(b) + (-remove-at-indices '(-1 0 1) '(a b)) => () + (-remove-at-indices '(-1 1 1) '(a b)) => '(a) + (-remove-at-indices '(1 -1 0) '(a b)) => () + (-remove-at-indices '(-1 -2 -3) '(a b)) => '(a b) + (-remove-at-indices '(4 3 2) '(a b)) => '(a b) + (-remove-at-indices '(0 0 0) '(a b)) => '(b) + (-remove-at-indices '(1 1 1) '(a b)) => '(a) + (-remove-at-indices () '(a b c)) => '(a b c) + (-remove-at-indices '(-1) '(a b c)) => '(a b c) + (-remove-at-indices '(3) '(a b c)) => '(a b c) + (-remove-at-indices '(-1 -1) '(a b c)) => '(a b c) + (-remove-at-indices '(-1 0) '(a b c)) => '(b c) + (-remove-at-indices '(0 -1) '(a b c)) => '(b c) + (-remove-at-indices '(-1 1) '(a b c)) => '(a c) + (-remove-at-indices '(1 -1) '(a b c)) => '(a c) + (-remove-at-indices '(-1 2) '(a b c)) => '(a b) + (-remove-at-indices '(2 -1) '(a b c)) => '(a b) + (-remove-at-indices '(-1 3) '(a b c)) => '(a b c) + (-remove-at-indices '(3 -1) '(a b c)) => '(a b c) + (-remove-at-indices '(-1 -2) '(a b c)) => '(a b c) + (-remove-at-indices '(3 3) '(a b c)) => '(a b c) + (-remove-at-indices '(4 3) '(a b c)) => '(a b c) + (-remove-at-indices '(2 -1 0) '(a b c)) => '(b) + (-remove-at-indices '(3 -1 2 1) '(a b c)) => '(a) + (-remove-at-indices '(3 0 -1 1) '(a b c)) => '(c) + (-remove-at-indices '(2 2 0 0) '(a b c)) => '(b) + (-remove-at-indices '(0 0 2) '(a b c)) => '(b) + (-remove-at-indices '(0) '(0 1 2 3 4 5)) => '(1 2 3 4 5) + (-remove-at-indices '(0 2 4) '(0 1 2 3 4 5)) => '(1 3 5) + (-remove-at-indices '(0 5) '(0 1 2 3 4 5)) => '(1 2 3 4) + (-remove-at-indices '(1 2 3) '(0 1 2 3 4 5)) => '(0 4 5) + (-remove-at-indices '(0 1 2 3 4 5) '(0 1 2 3 4 5)) => () + (-remove-at-indices '(2 0 4) '(0 1 2 3 4 5)) => '(1 3 5) + (-remove-at-indices '(5 0) '(0 1 2 3 4 5)) => '(1 2 3 4) + (-remove-at-indices '(1 3 2) '(0 1 2 3 4 5)) => '(0 4 5) + (-remove-at-indices '(0 3 4 2 5 1) '(0 1 2 3 4 5)) => () + (-remove-at-indices '(1) '(0 1 2 3 4 5)) => '(0 2 3 4 5) + (-remove-at-indices '(2) '(0 1 2 3 4 5)) => '(0 1 3 4 5) + (-remove-at-indices '(3) '(0 1 2 3 4 5)) => '(0 1 2 4 5) + (-remove-at-indices '(4) '(0 1 2 3 4 5)) => '(0 1 2 3 5) + (-remove-at-indices '(5) '(0 1 2 3 4 5)) => '(0 1 2 3 4) + (-remove-at-indices '(1 2 4) '((a b) (c d) (e f g) h i ((j) k) l (m))) + => '((a b) h ((j) k) l (m)) + (-remove-at-indices '(5) '((a b) (c d) (e f g) h i ((j) k) l (m))) + => '((a b) (c d) (e f g) h i l (m)) + (-remove-at-indices '(0) '(((a b) (c d) (e f g) h i ((j) k) l (m)))) => () + (-remove-at-indices '(2 3) '((0) (1) (2) (3) (4) (5) (6))) + => '((0) (1) (4) (5) (6)) + ;; Test for destructive modification. + (let ((l (list 0))) (ignore (-remove-at-indices '(0) l)) l) => '(0) + (let ((l (list 0 1))) (ignore (-remove-at-indices '(0) l)) l) => '(0 1) + (let ((l (list 0 1))) (ignore (-remove-at-indices '(1) l)) l) => '(0 1) + (let ((l (list 0 1))) (ignore (-remove-at-indices '(0 1) l)) l) => '(0 1) + (let ((l (list 0 1 2))) (ignore (-remove-at-indices '(0) l)) l) => '(0 1 2) + (let ((l (list 0 1 2))) (ignore (-remove-at-indices '(1) l)) l) => '(0 1 2) + (let ((l (list 0 1 2))) (ignore (-remove-at-indices '(2) l)) l) => '(0 1 2) + (let ((l (list 0 1 2))) (ignore (-remove-at-indices '(0 0) l)) l) + => '(0 1 2) + (let ((l (list 0 1 2))) (ignore (-remove-at-indices '(0 1) l)) l) + => '(0 1 2) + (let ((l (list 0 1 2))) (ignore (-remove-at-indices '(0 2) l)) l) + => '(0 1 2) + (let ((l (list 0 1 2))) (ignore (-remove-at-indices '(1 1) l)) l) + => '(0 1 2) + (let ((l (list 0 1 2))) (ignore (-remove-at-indices '(1 2) l)) l) + => '(0 1 2) + (let ((l (list 0 1 2))) (ignore (-remove-at-indices '(2 2) l)) l) + => '(0 1 2))) (def-example-group "Reductions" "Functions reducing lists to a single value (which may also be a list)." @@ -736,9 +847,10 @@ value rather than consuming a list to produce a single value." (defexamples -cycle (-take 5 (-cycle '(1 2 3))) => '(1 2 3 1 2) (-take 7 (-cycle '(1 "and" 3))) => '(1 "and" 3 1 "and" 3 1) - (-zip (-cycle '(1 2 3)) '(1 2)) => '((1 . 1) (2 . 2)) - (-zip-with #'cons (-cycle '(1 2 3)) '(1 2)) => '((1 . 1) (2 . 2)) - (-map (-partial #'-take 5) (-split-at 5 (-cycle '(1 2 3)))) => '((1 2 3 1 2) (3 1 2 3 1)) + (-zip-lists (-cycle '(3)) '(1 2)) => '((3 1) (3 2)) + (-zip-with #'cons (-cycle '(3)) '(1 2)) => '((3 . 1) (3 . 2)) + (--map (-take 5 it) (-split-at 5 (-cycle '(1 2 3)))) + => '((1 2 3 1 2) (3 1 2 3 1)) (let ((l (list 1))) (eq l (-cycle l))) => nil)) (def-example-group "Predicates" @@ -1468,17 +1580,60 @@ related predicates." (-iota 1 nil -1) => '(0)) (defexamples -zip-with - (-zip-with '+ '(1 2 3) '(4 5 6)) => '(5 7 9) - (-zip-with 'cons '(1 2 3) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) - (--zip-with (concat it " and " other) '("Batman" "Jekyll") '("Robin" "Hyde")) => '("Batman and Robin" "Jekyll and Hyde")) - - (defexamples -zip - (-zip '(1 2 3) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) - (-zip '(1 2 3) '(4 5 6 7)) => '((1 . 4) (2 . 5) (3 . 6)) - (-zip '(1 2) '(3 4 5) '(6)) => '((1 3 6)) - (-zip '(1 2 3 4) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) - (-zip '(1 2 3) '(4 5 6) '(7 8 9)) => '((1 4 7) (2 5 8) (3 6 9)) - (-zip) => nil) + (-zip-with #'+ '(1 2 3 4) '(5 6 7)) => '(6 8 10) + (-zip-with #'cons '(1 2 3) '(4 5 6 7)) => '((1 . 4) (2 . 5) (3 . 6)) + (--zip-with (format "%s & %s" it other) '(Batman Jekyll) '(Robin Hyde)) + => '("Batman & Robin" "Jekyll & Hyde") + (-zip-with #'cons '(nil) '(nil)) => '((nil)) + (-zip-with #'cons '(nil) '(nil nil)) => '((nil)) + (-zip-with #'cons '(nil nil) '(nil)) => '((nil)) + (-zip-with #'cons '(nil nil) '(nil nil)) => '((nil) (nil)) + (--zip-with (cons it other) () ()) => () + (--zip-with (cons it other) () '(nil)) => () + (--zip-with (cons it other) '(nil) '()) => () + (--zip-with (cons it other) '(nil) '(nil)) => '((nil)) + (--zip-with (cons it other) '(nil) '(nil nil)) => '((nil)) + (--zip-with (cons it other) '(nil nil) '(nil)) => '((nil)) + (--zip-with (cons it other) '(nil nil) '(nil nil)) => '((nil) (nil)) + (-zip-with #'signal () ()) => () + (-zip-with #'signal '(arith-error) ()) => () + (-zip-with #'signal () '(())) => () + (-zip-with #'signal '(arith-error) '(())) !!> (arith-error)) + + (defexamples -zip-pair + (-zip-pair '(1 2 3 4) '(5 6 7)) => '((1 . 5) (2 . 6) (3 . 7)) + (-zip-pair '(1 2 3) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) + (-zip-pair '(1 2) '(3)) => '((1 . 3)) + (-zip-pair () ()) => () + (-zip-pair '(0) ()) => () + (-zip-pair () '(0)) => () + (-zip-pair '(1 2 3 4) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) + (-zip-pair '(1 2 3) '(4 5 6 7)) => '((1 . 4) (2 . 5) (3 . 6)) + (with-no-warnings (-zip-pair '(1 2 3 4) '(5 6 7) '(8 9))) + => '((1 5 8) (2 6 9)) + (with-no-warnings (-zip-pair '(1 2 3) '(4 5 6) '(7 8 9))) + => '((1 4 7) (2 5 8) (3 6 9)) + (with-no-warnings (-zip-pair '(1 2 3))) => '((1) (2) (3)) + (with-no-warnings (-zip-pair)) => () + (with-no-warnings (-zip-pair ())) => () + (with-no-warnings (-zip-pair '(0))) => '((0)) + (with-no-warnings (-zip-pair '(0 1))) => '((0) (1)) + (with-no-warnings (-zip-pair () () ())) => () + (with-no-warnings (-zip-pair '(0) () ())) => () + (with-no-warnings (-zip-pair () '(0) ())) => () + (with-no-warnings (-zip-pair () () '(0))) => () + (with-no-warnings (-zip-pair '(0) '(1) ())) => () + (with-no-warnings (-zip-pair '(0) () '(1))) => () + (with-no-warnings (-zip-pair () '(0) '(1))) => () + (with-no-warnings (-zip-pair '(0) '(1) '(2))) => '((0 1 2)) + (with-no-warnings (-zip-pair '(0 1) '(2) '(3))) => '((0 2 3)) + (with-no-warnings (-zip-pair '(0) '(1 2) '(3))) => '((0 1 3)) + (with-no-warnings (-zip-pair '(0) '(1) '(2 3))) => '((0 1 2)) + (with-no-warnings (-zip-pair '(0 1) '(2 3) '(4))) => '((0 2 4)) + (with-no-warnings (-zip-pair '(0 1) '(2) '(3 4))) => '((0 2 3)) + (with-no-warnings (-zip-pair '(0) '(1 2) '(3 4))) => '((0 1 3)) + (with-no-warnings (-zip-pair '(0 1) '(2 3) '(4 5))) => '((0 2 4) (1 3 5)) + (with-no-warnings (-zip-pair '(0 1) '(2 3 4) '(5))) => '((0 2 5))) (defexamples -zip-lists (-zip-lists '(1 2 3) '(4 5 6)) => '((1 4) (2 5) (3 6)) @@ -1486,15 +1641,173 @@ related predicates." (-zip-lists '(1 2) '(3 4 5) '(6)) => '((1 3 6)) (-zip-lists '(1 2 3 4) '(4 5 6)) => '((1 4) (2 5) (3 6)) (-zip-lists '(1 2 3) '(4 5 6) '(7 8 9)) => '((1 4 7) (2 5 8) (3 6 9)) - (-zip-lists) => nil) + (-zip-lists) => () + (-zip-lists ()) => () + (-zip-lists '(0)) => '((0)) + (-zip-lists '(0 1)) => '((0) (1)) + (-zip-lists '(0 1 2)) => '((0) (1) (2)) + (-zip-lists () ()) => () + (-zip-lists '(0) ()) => () + (-zip-lists () '(0)) => () + (-zip-lists () () ()) => () + (-zip-lists '(0) () ()) => () + (-zip-lists () '(0) ()) => () + (-zip-lists () () '(0)) => () + (-zip-lists '(0) '(1) ()) => () + (-zip-lists '(0) () '(1)) => () + (-zip-lists () '(0) '(1)) => () + (-zip-lists '(0) '(1) '(2)) => '((0 1 2)) + (-zip-lists '(0 1) '(2) '(3)) => '((0 2 3)) + (-zip-lists '(0) '(1 2) '(3)) => '((0 1 3)) + (-zip-lists '(0) '(1) '(2 3)) => '((0 1 2)) + (-zip-lists '(0 1) '(2 3) '(4)) => '((0 2 4)) + (-zip-lists '(0 1) '(2) '(3 4)) => '((0 2 3)) + (-zip-lists '(0) '(1 2) '(3 4)) => '((0 1 3)) + (-zip-lists '(0 1) '(2 3) '(4 5)) => '((0 2 4) (1 3 5))) + + (defexamples -zip-lists-fill + (-zip-lists-fill 0 '(1 2) '(3 4 5) '(6)) => '((1 3 6) (2 4 0) (0 5 0)) + (-zip-lists-fill 0 '(1 2) '(3 4) '(5 6)) => '((1 3 5) (2 4 6)) + (-zip-lists-fill 0 '(1 2 3) ()) => '((1 0) (2 0) (3 0)) + (-zip-lists-fill 0) => () + (-zip-lists-fill 0 ()) => () + (-zip-lists-fill 0 '(1)) => '((1)) + (-zip-lists-fill 0 '(1 2)) => '((1) (2)) + (-zip-lists-fill 0 '(1 2 3)) => '((1) (2) (3)) + (-zip-lists-fill 0 () ()) => () + (-zip-lists-fill 0 '(1) ()) => '((1 0)) + (-zip-lists-fill 0 () '(1)) => '((0 1)) + (-zip-lists-fill 0 '(1) '(2)) => '((1 2)) + (-zip-lists-fill 0 '(1 2) ()) => '((1 0) (2 0)) + (-zip-lists-fill 0 () '(1 2)) => '((0 1) (0 2)) + (-zip-lists-fill 0 () '(1 2 3)) => '((0 1) (0 2) (0 3)) + (-zip-lists-fill 0 '(1 2) '(3)) => '((1 3) (2 0)) + (-zip-lists-fill 0 '(1) '(2 3)) => '((1 2) (0 3)) + (-zip-lists-fill 0 () () ()) => () + (-zip-lists-fill 0 '(1) () ()) => '((1 0 0)) + (-zip-lists-fill 0 () '(1) ()) => '((0 1 0)) + (-zip-lists-fill 0 () () '(1)) => '((0 0 1)) + (-zip-lists-fill 0 '(1 2) () ()) => '((1 0 0) (2 0 0)) + (-zip-lists-fill 0 () '(1 2) ()) => '((0 1 0) (0 2 0)) + (-zip-lists-fill 0 () () '(1 2)) => '((0 0 1) (0 0 2))) + + (defexamples -zip + (-zip '(1 2 3 4) '(5 6 7) '(8 9)) => '((1 5 8) (2 6 9)) + (-zip '(1 2 3) '(4 5 6) '(7 8 9)) => '((1 4 7) (2 5 8) (3 6 9)) + (-zip '(1 2 3)) => '((1) (2) (3)) + (-zip) => () + (-zip ()) => () + (-zip '(0)) => '((0)) + (-zip '(0 1)) => '((0) (1)) + (with-no-warnings (-zip () ())) => () + (with-no-warnings (-zip '(0) ())) => () + (with-no-warnings (-zip () '(0))) => () + (with-no-warnings (-zip '(1 2 3 4) '(4 5 6))) => '((1 . 4) (2 . 5) (3 . 6)) + (with-no-warnings (-zip '(1 2 3) '(4 5 6 7))) => '((1 . 4) (2 . 5) (3 . 6)) + (-zip () () ()) => () + (-zip '(0) () ()) => () + (-zip () '(0) ()) => () + (-zip () () '(0)) => () + (-zip '(0) '(1) ()) => () + (-zip '(0) () '(1)) => () + (-zip () '(0) '(1)) => () + (-zip '(0) '(1) '(2)) => '((0 1 2)) + (-zip '(0 1) '(2) '(3)) => '((0 2 3)) + (-zip '(0) '(1 2) '(3)) => '((0 1 3)) + (-zip '(0) '(1) '(2 3)) => '((0 1 2)) + (-zip '(0 1) '(2 3) '(4)) => '((0 2 4)) + (-zip '(0 1) '(2) '(3 4)) => '((0 2 3)) + (-zip '(0) '(1 2) '(3 4)) => '((0 1 3)) + (-zip '(0 1) '(2 3) '(4 5)) => '((0 2 4) (1 3 5)) + (-zip '(0 1) '(2 3 4) '(5)) => '((0 2 5))) (defexamples -zip-fill - (-zip-fill 0 '(1 2 3 4 5) '(6 7 8 9)) => '((1 . 6) (2 . 7) (3 . 8) (4 . 9) (5 . 0))) + (-zip-fill 0 '(1 2 3) '(4 5)) => '((1 . 4) (2 . 5) (3 . 0)) + (-zip-fill 0 '() '(1 2 3)) => '((0 . 1) (0 . 2) (0 . 3)) + (-zip-fill 0 '(1 2) '(3 4) '(5 6)) => '((1 3 5) (2 4 6)) + (-zip-fill 0 '(1 2) '(3 4 5) '(6)) => '((1 3 6) (2 4 0) (0 5 0)) + (-zip-fill 0) => () + (-zip-fill 0 ()) => () + (-zip-fill 0 '(1)) => '((1)) + (-zip-fill 0 '(1 2)) => '((1) (2)) + (-zip-fill 0 '(1 2 3)) => '((1) (2) (3)) + (-zip-fill 0 () ()) => () + (-zip-fill 0 '(1) ()) => '((1 . 0)) + (-zip-fill 0 () '(1)) => '((0 . 1)) + (-zip-fill 0 '(1) '(2)) => '((1 . 2)) + (-zip-fill 0 '(1 2) ()) => '((1 . 0) (2 . 0)) + (-zip-fill 0 () '(1 2)) => '((0 . 1) (0 . 2)) + (-zip-fill 0 '(1 2 3) ()) => '((1 . 0) (2 . 0) (3 . 0)) + (-zip-fill 0 () '(1 2 3)) => '((0 . 1) (0 . 2) (0 . 3)) + (-zip-fill 0 '(1 2) '(3)) => '((1 . 3) (2 . 0)) + (-zip-fill 0 '(1) '(2 3)) => '((1 . 2) (0 . 3)) + (-zip-fill 0 () () ()) => () + (-zip-fill 0 '(1) () ()) => '((1 0 0)) + (-zip-fill 0 () '(1) ()) => '((0 1 0)) + (-zip-fill 0 () () '(1)) => '((0 0 1)) + (-zip-fill 0 '(1 2) () ()) => '((1 0 0) (2 0 0)) + (-zip-fill 0 () '(1 2) ()) => '((0 1 0) (0 2 0)) + (-zip-fill 0 () () '(1 2)) => '((0 0 1) (0 0 2))) + + (defexamples -unzip-lists + (-unzip-lists (-zip-lists '(1 2) '(3 4) '(5 6))) => '((1 2) (3 4) (5 6)) + (-unzip-lists '((1 2 3) (4 5) (6 7) (8 9))) => '((1 4 6 8) (2 5 7 9)) + (-unzip-lists '((1 2 3) (4 5 6))) => '((1 4) (2 5) (3 6)) + (-unzip-lists ()) => () + (-unzip-lists '(())) => () + (-unzip-lists '((1))) => '((1)) + (-unzip-lists '((1 2))) => '((1) (2)) + (-unzip-lists '((1 2 3))) => '((1) (2) (3)) + (-unzip-lists '(() ())) => () + (-unzip-lists '((1) ())) => () + (-unzip-lists '(() (1))) => () + (-unzip-lists '((1) (2))) => '((1 2)) + (-unzip-lists '((1 2) (3))) => '((1 3)) + (-unzip-lists '((1) (2 3))) => '((1 2)) + (-unzip-lists '((1 2) (3 4))) => '((1 3) (2 4)) + (-unzip-lists '(() () ())) => () + (-unzip-lists '((1) () ())) => () + (-unzip-lists '(() (1) ())) => () + (-unzip-lists '(() () (1))) => () + (-unzip-lists '((1) (2) ())) => () + (-unzip-lists '((1) () (2))) => () + (-unzip-lists '(() (1) (2))) => () + (-unzip-lists '((1) (2) (3))) => '((1 2 3)) + (-unzip-lists '((1 2) (3) (4))) => '((1 3 4)) + (-unzip-lists '((1) (2 3) (4))) => '((1 2 4)) + (-unzip-lists '((1) (2) (3 4))) => '((1 2 3)) + (-unzip-lists '((1 2) (3 4) (5 6))) => '((1 3 5) (2 4 6)) + (-unzip-lists '((1 2 3) (4 5 6) (7 8 9))) => '((1 4 7) (2 5 8) (3 6 9))) (defexamples -unzip - (-unzip (-zip '(1 2 3) '(a b c) '("e" "f" "g"))) => '((1 2 3) (a b c) ("e" "f" "g")) - (-unzip '((1 2) (3 4) (5 6) (7 8) (9 10))) => '((1 3 5 7 9) (2 4 6 8 10)) - (-unzip '((1 2) (3 4))) => '((1 . 3) (2 . 4))) + (-unzip (-zip '(1 2) '(3 4) '(5 6))) => '((1 . 2) (3 . 4) (5 . 6)) + (-unzip '((1 2 3) (4 5 6))) => '((1 . 4) (2 . 5) (3 . 6)) + (-unzip '((1 2 3) (4 5) (6 7) (8 9))) => '((1 4 6 8) (2 5 7 9)) + (-unzip ()) => () + (-unzip '(())) => () + (-unzip '((1))) => '((1)) + (-unzip '((1 2))) => '((1) (2)) + (-unzip '((1 2 3))) => '((1) (2) (3)) + (-unzip '(() ())) => () + (-unzip '((1) ())) => () + (-unzip '(() (1))) => () + (-unzip '((1) (2))) => '((1 . 2)) + (-unzip '((1 2) (3))) => '((1 . 3)) + (-unzip '((1) (2 3))) => '((1 . 2)) + (-unzip '((1 2) (3 4))) => '((1 . 3) (2 . 4)) + (-unzip '(() () ())) => () + (-unzip '((1) () ())) => () + (-unzip '(() (1) ())) => () + (-unzip '(() () (1))) => () + (-unzip '((1) (2) ())) => () + (-unzip '((1) () (2))) => () + (-unzip '(() (1) (2))) => () + (-unzip '((1) (2) (3))) => '((1 2 3)) + (-unzip '((1 2) (3) (4))) => '((1 3 4)) + (-unzip '((1) (2 3) (4))) => '((1 2 4)) + (-unzip '((1) (2) (3 4))) => '((1 2 3)) + (-unzip '((1 2) (3 4) (5 6))) => '((1 3 5) (2 4 6)) + (-unzip '((1 2 3) (4 5 6) (7 8 9))) => '((1 4 7) (2 5 8) (3 6 9))) (defexamples -pad (-pad 0 '()) => '(()) @@ -2344,26 +2657,28 @@ or readability." (funcall (-fixfn #'sin #'approx=) 0.1) => '(halted . t)) (defexamples -prodfn - (funcall (-prodfn '1+ '1- 'number-to-string) '(1 2 3)) => '(2 1 "3") - (-map (-prodfn '1+ '1-) '((1 2) (3 4) (5 6) (7 8))) => '((2 1) (4 3) (6 5) (8 7)) - (apply '+ (funcall (-prodfn 'length 'string-to-number) '((1 2 3) "15"))) => 18 - (let ((f '1+) - (g '1-) - (ff 'string-to-number) - (gg 'length) + (funcall (-prodfn #'1+ #'1- #'number-to-string) '(1 2 3)) => '(2 1 "3") + (-map (-prodfn #'1- #'1+) '((1 2) (3 4) (5 6))) => '((0 3) (2 5) (4 7)) + (apply #'+ (funcall (-prodfn #'length #'string-to-number) '((t) "5"))) => 6 + (let ((f #'1+) + (g #'1-) + (ff #'string-to-number) + (gg #'length) (input '(1 2)) (input2 "foo") (input3 '("10" '(1 2 3)))) (and (equal (funcall (-prodfn f g) input) - (funcall (-juxt (-compose f (-partial 'nth 0)) (-compose g (-partial 'nth 1))) input)) + (funcall (-juxt (-compose f #'car) (-compose g #'cadr)) + input)) (equal (funcall (-compose (-prodfn f g) (-juxt ff gg)) input2) (funcall (-juxt (-compose f ff) (-compose g gg)) input2)) - (equal (funcall (-compose (-partial 'nth 0) (-prodfn f g)) input) - (funcall (-compose f (-partial 'nth 0)) input)) - (equal (funcall (-compose (-partial 'nth 1) (-prodfn f g)) input) - (funcall (-compose g (-partial 'nth 1)) input)) + (equal (funcall (-compose (-partial #'nth 0) (-prodfn f g)) input) + (funcall (-compose f (-partial #'nth 0)) input)) + (equal (funcall (-compose (-partial #'nth 1) (-prodfn f g)) input) + (funcall (-compose g (-partial #'nth 1)) input)) (equal (funcall (-compose (-prodfn f g) (-prodfn ff gg)) input3) - (funcall (-prodfn (-compose f ff) (-compose g gg)) input3)))) => t)) + (funcall (-prodfn (-compose f ff) (-compose g gg)) input3)))) + => t)) (ert-deftest dash--member-fn () "Test `dash--member-fn'." @@ -2488,4 +2803,12 @@ or readability." (3 5 4) (3 4 5))))) +(ert-deftest dash--length= () + "Test `dash--length='." + (dotimes (n 10) + (let ((l (make-list n nil))) + (should (dash--length= l n)) + (should-not (dash--length= l (1- n))) + (should-not (dash--length= l (1+ n)))))) + ;;; examples.el ends here diff --git a/readme-template.md b/readme-template.md index 3065d2c80a..ef33241438 100644 --- a/readme-template.md +++ b/readme-template.md @@ -14,7 +14,6 @@ See the end of the file for license conditions. ## Contents * [Change log](#change-log) - * [Upcoming breaking change!](#upcoming-breaking-change) * [Installation](#installation) * [Functions](#functions) * [Contribute](#contribute) @@ -25,18 +24,6 @@ See the end of the file for license conditions. See the [`NEWS.md`](NEWS.md) file. -### Upcoming breaking change! - -- For backward compatibility reasons, `-zip` when called with two - lists returns a list of cons cells, rather than a list of proper - lists. This is a clunky API, and may be changed in a future release - to always return a list of proper lists, as `-zip-lists` currently - does. - - **N.B.:** Do not rely on the current behavior of `-zip` for two - lists. Instead, use `-zip-pair` for a list of cons cells, and - `-zip-lists` for a list of proper lists. - ## Installation Dash is available on [GNU ELPA](https://elpa.gnu.org/), [GNU-devel