Fix take/drop splits (again)

This reverts some of the copying added in PR #354 so as to maintain
some support for circular and dotted list tails, which can't be
copied.  For more discussion, see also issue #225.

* dash.el (--take-while, -take-while, -take, -take-last)
(-drop-last): Improve docstring.
(--drop-while, -drop): Revert to not returning a copy.
(-drop-while): Update docstring accordingly.
(-split-at): Simplify.  Improve docstring.
(-rotate): Prefer nthcdr over -drop for clarity.

* dev/examples.el (-drop, -drop-while): Update tests for reverted
behavior.
(-take-while, -split-at, -rotate, -cycle, -fix): Improve tests.

* README.md:
* dash.texi: Regenerate docs.
master
Basil L. Contovounesios 5 years ago
parent 565055e95c
commit 558cd95367
No known key found for this signature in database
GPG Key ID: 205AB54A5D5D8CFF
  1. 46
      README.md
  2. 61
      dash.el
  3. 53
      dash.texi
  4. 57
      dev/examples.el

@ -610,7 +610,7 @@ Return a copy of the first `n` items in `list`.
Return a copy of `list` if it contains `n` items or fewer.
Return nil if `n` is zero or less.
See also: [`-take-last`](#-take-last-n-list)
See also: [`-take-last`](#-take-last-n-list).
```el
(-take 3 '(1 2 3 4 5)) ;; => '(1 2 3)
@ -624,7 +624,7 @@ Return a copy of the last `n` items of `list` in order.
Return a copy of `list` if it contains `n` items or fewer.
Return nil if `n` is zero or less.
See also: [`-take`](#-take-n-list)
See also: [`-take`](#-take-n-list).
```el
(-take-last 3 '(1 2 3 4 5)) ;; => '(3 4 5)
@ -634,11 +634,12 @@ See also: [`-take`](#-take-n-list)
#### -drop `(n list)`
Return a copy of the tail of `list` without the first `n` items.
Return a copy of `list` if `n` is zero or less.
Return the tail (not a copy) of `list` without the first `n` items.
Return nil if `list` contains `n` items or fewer.
Return `list` if `n` is zero or less.
For another variant, see also [`-drop-last`](#-drop-last-n-list).
See also: [`-drop-last`](#-drop-last-n-list)
(fn `n` `list`)
```el
(-drop 3 '(1 2 3 4 5)) ;; => '(4 5)
@ -652,7 +653,7 @@ Return a copy of `list` without its last `n` items.
Return a copy of `list` if `n` is zero or less.
Return nil if `list` contains `n` items or fewer.
See also: [`-drop`](#-drop-n-list)
See also: [`-drop`](#-drop-n-list).
```el
(-drop-last 3 '(1 2 3 4 5)) ;; => '(1 2)
@ -666,26 +667,27 @@ Take successive items from `list` for which `pred` returns non-nil.
`pred` is a function of one argument. Return a new list of the
successive elements from the start of `list` for which `pred` returns
non-nil.
See also: [`-drop-while`](#-drop-while-pred-list)
This function's anaphoric counterpart is `--take-while`.
For another variant, see also [`-drop-while`](#-drop-while-pred-list).
```el
(-take-while 'even? '(1 2 3 4)) ;; => nil
(-take-while 'even? '(2 4 5 6)) ;; => '(2 4)
(-take-while #'even? '(1 2 3 4)) ;; => nil
(-take-while #'even? '(2 4 5 6)) ;; => '(2 4)
(--take-while (< it 4) '(1 2 3 4 3 2 1)) ;; => '(1 2 3)
```
#### -drop-while `(pred list)`
Drop successive items from `list` for which `pred` returns non-nil.
`pred` is a function of one argument. Return a copy of the tail of
`list` starting from its first element for which `pred` returns nil.
See also: [`-take-while`](#-take-while-pred-list)
`pred` is a function of one argument. Return the tail (not a copy)
of `list` starting from its first element for which `pred` returns
nil.
This function's anaphoric counterpart is `--drop-while`.
For another variant, see also [`-take-while`](#-take-while-pred-list).
```el
(-drop-while 'even? '(1 2 3 4)) ;; => '(1 2 3 4)
(-drop-while 'even? '(2 4 5 6)) ;; => '(5 6)
(-drop-while #'even? '(1 2 3 4)) ;; => '(1 2 3 4)
(-drop-while #'even? '(2 4 5 6)) ;; => '(5 6)
(--drop-while (< it 4) '(1 2 3 4 3 2 1)) ;; => '(4 3 2 1)
```
@ -1366,11 +1368,17 @@ Functions partitioning the input list into a list of lists.
#### -split-at `(n list)`
Return a list of ((-take `n` `list`) (-drop `n` `list`)), in no more than one pass through the list.
Split `list` into two sublists after the Nth element.
The result is a list of two elements (`take` `drop`) where `take` is a
new list of the first `n` elements of `list`, and `drop` is the
remaining elements of `list` (not a copy). `take` and `drop` are like
the results of [`-take`](#-take-n-list) and [`-drop`](#-drop-n-list), respectively, but the split
is done in a single list traversal.
```el
(-split-at 3 '(1 2 3 4 5)) ;; => '((1 2 3) (4 5))
(-split-at 17 '(1 2 3 4 5)) ;; => '((1 2 3 4 5) nil)
(-split-at 0 '(1 2 3 4 5)) ;; => '(nil (1 2 3 4 5))
```
#### -split-with `(pred list)`
@ -2090,8 +2098,8 @@ Compute the (least) fixpoint of `fn` with initial input `list`.
`fn` is called at least once, results are compared with `equal`.
```el
(-fix (lambda (l) (-non-nil (--mapcat (-split-at (/ (length it) 2) it) l))) '((1 2 3 4 5 6))) ;; => '((1) (2) (3) (4) (5) (6))
(let ((data '(("starwars" "scifi") ("jedi" "starwars" "warrior")))) (--fix (-uniq (--mapcat (cons it (cdr (assoc it data))) it)) '("jedi" "book"))) ;; => '("jedi" "starwars" "warrior" "scifi" "book")
(-fix (lambda (l) (-non-nil (--mapcat (-split-at (/ (length it) 2) it) l))) '((1 2 3))) ;; => '((1) (2) (3))
(let ((l '((starwars scifi) (jedi starwars warrior)))) (--fix (-uniq (--mapcat (cons it (cdr (assq it l))) it)) '(jedi book))) ;; => '(jedi starwars warrior scifi book)
```

@ -966,7 +966,12 @@ section is returned. Defaults to 1."
(nreverse new-list)))
(defmacro --take-while (form list)
"Anaphoric form of `-take-while'."
"Take successive items 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. Return a new
list of the successive elements from the start of LIST for which
FORM evaluates to non-nil.
This is the anaphoric counterpart to `-take-while'."
(declare (debug (form form)))
(let ((r (make-symbol "result")))
`(let (,r)
@ -978,24 +983,30 @@ section is returned. Defaults to 1."
PRED is a function of one argument. Return a new list of the
successive elements from the start of LIST for which PRED returns
non-nil.
See also: `-drop-while'"
This function's anaphoric counterpart is `--take-while'.
For another variant, see also `-drop-while'."
(--take-while (funcall pred it) list))
(defmacro --drop-while (form list)
"Anaphoric form of `-drop-while'."
"Drop successive items 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. Return the
tail (not a copy) of LIST starting from its first element for
which FORM evaluates to nil.
This is the anaphoric counterpart to `-drop-while'."
(declare (debug (form form)))
(let ((l (make-symbol "list")))
`(let ((,l ,list))
(--each-while ,l ,form (pop ,l))
(copy-sequence ,l))))
,l)))
(defun -drop-while (pred list)
"Drop successive items from LIST for which PRED returns non-nil.
PRED is a function of one argument. Return a copy of the tail of
LIST starting from its first element for which PRED returns nil.
See also: `-take-while'"
PRED is a function of one argument. Return the tail (not a copy)
of LIST starting from its first element for which PRED returns
nil.
This function's anaphoric counterpart is `--drop-while'.
For another variant, see also `-take-while'."
(--drop-while (funcall pred it) list))
(defun -take (n list)
@ -1003,7 +1014,7 @@ See also: `-take-while'"
Return a copy of LIST if it contains N items or fewer.
Return nil if N is zero or less.
See also: `-take-last'"
See also: `-take-last'."
(declare (pure t) (side-effect-free t))
(--take-while (< it-index n) list))
@ -1012,35 +1023,37 @@ See also: `-take-last'"
Return a copy of LIST if it contains N items or fewer.
Return nil if N is zero or less.
See also: `-take'"
See also: `-take'."
(declare (pure t) (side-effect-free t))
(copy-sequence (last list n)))
(defun -drop (n list)
"Return a copy of the tail of LIST without the first N items.
Return a copy of LIST if N is zero or less.
(defalias '-drop #'nthcdr
"Return the tail (not a copy) of LIST without the first N items.
Return nil if LIST contains N items or fewer.
See also: `-drop-last'"
(copy-sequence (nthcdr n list)))
Return LIST if N is zero or less.
For another variant, see also `-drop-last'.
\n(fn N LIST)")
(defun -drop-last (n list)
"Return a copy of LIST without its last N items.
Return a copy of LIST if N is zero or less.
Return nil if LIST contains N items or fewer.
See also: `-drop'"
See also: `-drop'."
(declare (pure t) (side-effect-free t))
(nbutlast (copy-sequence list) n))
(defun -split-at (n list)
"Return a list of ((-take N LIST) (-drop N LIST)), in no more than one pass through the list."
"Split LIST into two sublists after the Nth element.
The result is a list of two elements (TAKE DROP) where TAKE is a
new list of the first N elements of LIST, and DROP is the
remaining elements of LIST (not a copy). TAKE and DROP are like
the results of `-take' and `-drop', respectively, but the split
is done in a single list traversal."
(declare (pure t) (side-effect-free t))
(let (result)
(--dotimes n
(when list
(!cons (car list) result)
(!cdr list)))
(--each-while list (< it-index n)
(push (pop list) result))
(list (nreverse result) list)))
(defun -rotate (n list)
@ -1051,7 +1064,7 @@ The time complexity is O(n)."
(let* ((len (length list))
(n-mod-len (mod n len))
(new-tail-len (- len n-mod-len)))
(append (-drop new-tail-len list) (-take new-tail-len list)))))
(append (nthcdr new-tail-len list) (-take new-tail-len list)))))
(defun -insert-at (n x list)
"Return a list with X inserted into LIST at position N.

@ -642,7 +642,7 @@ Return a copy of the first @var{n} items in @var{list}.
Return a copy of @var{list} if it contains @var{n} items or fewer.
Return nil if @var{n} is zero or less.
See also: @code{-take-last} (@pxref{-take-last})
See also: @code{-take-last} (@pxref{-take-last}).
@example
@group
@ -666,7 +666,7 @@ Return a copy of the last @var{n} items of @var{list} in order.
Return a copy of @var{list} if it contains @var{n} items or fewer.
Return nil if @var{n} is zero or less.
See also: @code{-take} (@pxref{-take})
See also: @code{-take} (@pxref{-take}).
@example
@group
@ -686,11 +686,12 @@ See also: @code{-take} (@pxref{-take})
@anchor{-drop}
@defun -drop (n list)
Return a copy of the tail of @var{list} without the first @var{n} items.
Return a copy of @var{list} if @var{n} is zero or less.
Return the tail (not a copy) of @var{list} without the first @var{n} items.
Return nil if @var{list} contains @var{n} items or fewer.
Return @var{list} if @var{n} is zero or less.
For another variant, see also @code{-drop-last} (@pxref{-drop-last}).
See also: @code{-drop-last} (@pxref{-drop-last})
(fn @var{n} @var{list})
@example
@group
@ -714,7 +715,7 @@ Return a copy of @var{list} without its last @var{n} items.
Return a copy of @var{list} if @var{n} is zero or less.
Return nil if @var{list} contains @var{n} items or fewer.
See also: @code{-drop} (@pxref{-drop})
See also: @code{-drop} (@pxref{-drop}).
@example
@group
@ -738,16 +739,16 @@ Take successive items from @var{list} for which @var{pred} returns non-nil.
@var{pred} is a function of one argument. Return a new list of the
successive elements from the start of @var{list} for which @var{pred} returns
non-nil.
See also: @code{-drop-while} (@pxref{-drop-while})
This function's anaphoric counterpart is @code{--take-while}.
For another variant, see also @code{-drop-while} (@pxref{-drop-while}).
@example
@group
(-take-while 'even? '(1 2 3 4))
(-take-while #'even? '(1 2 3 4))
@result{} nil
@end group
@group
(-take-while 'even? '(2 4 5 6))
(-take-while #'even? '(2 4 5 6))
@result{} '(2 4)
@end group
@group
@ -760,18 +761,19 @@ See also: @code{-drop-while} (@pxref{-drop-while})
@anchor{-drop-while}
@defun -drop-while (pred list)
Drop successive items from @var{list} for which @var{pred} returns non-nil.
@var{pred} is a function of one argument. Return a copy of the tail of
@var{list} starting from its first element for which @var{pred} returns nil.
See also: @code{-take-while} (@pxref{-take-while})
@var{pred} is a function of one argument. Return the tail (not a copy)
of @var{list} starting from its first element for which @var{pred} returns
nil.
This function's anaphoric counterpart is @code{--drop-while}.
For another variant, see also @code{-take-while} (@pxref{-take-while}).
@example
@group
(-drop-while 'even? '(1 2 3 4))
(-drop-while #'even? '(1 2 3 4))
@result{} '(1 2 3 4)
@end group
@group
(-drop-while 'even? '(2 4 5 6))
(-drop-while #'even? '(2 4 5 6))
@result{} '(5 6)
@end group
@group
@ -1935,7 +1937,12 @@ Functions partitioning the input list into a list of lists.
@anchor{-split-at}
@defun -split-at (n list)
Return a list of ((-take @var{n} @var{list}) (-drop @var{n} @var{list})), in no more than one pass through the list.
Split @var{list} into two sublists after the Nth element.
The result is a list of two elements (@var{take} @var{drop}) where @var{take} is a
new list of the first @var{n} elements of @var{list}, and @var{drop} is the
remaining elements of @var{list} (not a copy). @var{take} and @var{drop} are like
the results of @code{-take} (@pxref{-take}) and @code{-drop} (@pxref{-drop}), respectively, but the split
is done in a single list traversal.
@example
@group
@ -1946,6 +1953,10 @@ Return a list of ((-take @var{n} @var{list}) (-drop @var{n} @var{list})), in no
(-split-at 17 '(1 2 3 4 5))
@result{} '((1 2 3 4 5) nil)
@end group
@group
(-split-at 0 '(1 2 3 4 5))
@result{} '(nil (1 2 3 4 5))
@end group
@end example
@end defun
@ -3196,12 +3207,12 @@ Compute the (least) fixpoint of @var{fn} with initial input @var{list}.
@example
@group
(-fix (lambda (l) (-non-nil (--mapcat (-split-at (/ (length it) 2) it) l))) '((1 2 3 4 5 6)))
@result{} '((1) (2) (3) (4) (5) (6))
(-fix (lambda (l) (-non-nil (--mapcat (-split-at (/ (length it) 2) it) l))) '((1 2 3)))
@result{} '((1) (2) (3))
@end group
@group
(let ((data '(("starwars" "scifi") ("jedi" "starwars" "warrior")))) (--fix (-uniq (--mapcat (cons it (cdr (assoc it data))) it)) '("jedi" "book")))
@result{} '("jedi" "starwars" "warrior" "scifi" "book")
(let ((l '((starwars scifi) (jedi starwars warrior)))) (--fix (-uniq (--mapcat (cons it (cdr (assq it l))) it)) '(jedi book)))
@result{} '(jedi starwars warrior scifi book)
@end group
@end example
@end defun

@ -211,8 +211,8 @@ new list."
(-drop -1 ()) => ()
(-drop -1 '(1)) => '(1)
(-drop 1 ()) => ()
(let ((l (list 1 2))) (setcar (-drop 1 l) 0) l) => '(1 2)
(let ((l (list 1 2))) (eq (-drop 0 l) l)) => nil)
(let ((l (list 1 2))) (setcar (-drop 1 l) 0) l) => '(1 0)
(let ((l (list 1 2))) (eq (-drop 0 l) l)) => t)
(defexamples -drop-last
(-drop-last 3 '(1 2 3 4 5)) => '(1 2)
@ -226,8 +226,8 @@ new list."
(let ((l (list 1 2))) (eq (-drop-last 0 l) l)) => nil)
(defexamples -take-while
(-take-while 'even? '(1 2 3 4)) => ()
(-take-while 'even? '(2 4 5 6)) => '(2 4)
(-take-while #'even? '(1 2 3 4)) => ()
(-take-while #'even? '(2 4 5 6)) => '(2 4)
(--take-while (< it 4) '(1 2 3 4 3 2 1)) => '(1 2 3)
(--take-while t ()) => ()
(--take-while nil ()) => ()
@ -237,8 +237,8 @@ new list."
(let ((l (list 1 2))) (eq (--take-while t l) l)) => nil)
(defexamples -drop-while
(-drop-while 'even? '(1 2 3 4)) => '(1 2 3 4)
(-drop-while 'even? '(2 4 5 6)) => '(5 6)
(-drop-while #'even? '(1 2 3 4)) => '(1 2 3 4)
(-drop-while #'even? '(2 4 5 6)) => '(5 6)
(--drop-while (< it 4) '(1 2 3 4 3 2 1)) => '(4 3 2 1)
(--drop-while t ()) => ()
(--drop-while nil ()) => ()
@ -246,8 +246,8 @@ new list."
(--drop-while nil '(1 2)) => '(1 2)
(--drop-while t '(1)) => ()
(--drop-while t '(1 2)) => ()
(let ((l (list 1 2))) (setcar (-drop-while 'odd? l) 0) l) => '(1 2)
(let ((l (list 1 2))) (eq (--drop-while nil l) l)) => nil)
(let ((l (list 1 2))) (setcar (-drop-while #'odd? l) 0) l) => '(1 0)
(let ((l (list 1 2))) (eq (--drop-while nil l) l)) => t)
(defexamples -select-by-indices
(-select-by-indices '(4 10 2 3 6) '("v" "e" "l" "o" "c" "i" "r" "a" "p" "t" "o" "r")) => '("c" "o" "l" "o" "r")
@ -634,7 +634,21 @@ value rather than consuming a list to produce a single value."
(defexamples -split-at
(-split-at 3 '(1 2 3 4 5)) => '((1 2 3) (4 5))
(-split-at 17 '(1 2 3 4 5)) => '((1 2 3 4 5) nil))
(-split-at 17 '(1 2 3 4 5)) => '((1 2 3 4 5) ())
(-split-at 0 '(1 2 3 4 5)) => '(() (1 2 3 4 5))
(-split-at -1 ()) => '(() ())
(-split-at 0 ()) => '(() ())
(-split-at 1 ()) => '(() ())
(-split-at -1 '(1)) => '(() (1))
(-split-at 0 '(1)) => '(() (1))
(-split-at 1 '(1)) => '((1) ())
(-split-at 2 '(1)) => '((1) ())
(-split-at -1 '(1 2)) => '(() (1 2))
(-split-at 1 '(1 2)) => '((1) (2))
(-split-at 2 '(1 2)) => '((1 2) ())
(-split-at 3 '(1 2)) => '((1 2) ())
(let* ((l (list 1 2)) (s (-split-at 1 l))) (eq (car s) l)) => nil
(let* ((l (list 1 2)) (s (-split-at 1 l))) (eq (cadr s) (cdr l))) => t)
(defexamples -split-with
(-split-with 'even? '(1 2 3 4)) => '(() (1 2 3 4))
@ -819,7 +833,17 @@ value rather than consuming a list to produce a single value."
(-rotate 3 '(1 2 3 4 5 6 7)) => '(5 6 7 1 2 3 4)
(-rotate -3 '(1 2 3 4 5 6 7)) => '(4 5 6 7 1 2 3)
(-rotate 16 '(1 2 3 4 5 6 7)) => '(6 7 1 2 3 4 5)
(-rotate -16 '(1 2 3 4 5 6 7)) => '(3 4 5 6 7 1 2))
(-rotate -16 '(1 2 3 4 5 6 7)) => '(3 4 5 6 7 1 2)
(-rotate -1 ()) => ()
(-rotate 0 ()) => ()
(-rotate 1 ()) => ()
(-rotate -1 '(1)) => '(1)
(-rotate 0 '(1)) => '(1)
(-rotate 1 '(1)) => '(1)
(-rotate -1 '(1 2)) => '(2 1)
(-rotate 0 '(1 2)) => '(1 2)
(-rotate 1 '(1 2)) => '(2 1)
(-rotate 2 '(1 2)) => '(1 2))
(defexamples -repeat
(-repeat 3 :a) => '(:a :a :a)
@ -888,8 +912,8 @@ value rather than consuming a list to produce a single value."
(-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-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))
(let ((l (list 1))) (eq l (-cycle l))) => nil)
(defexamples -pad
@ -985,10 +1009,11 @@ value rather than consuming a list to produce a single value."
(-list '(() 1)) => '(() 1))
(defexamples -fix
(-fix (lambda (l) (-non-nil (--mapcat (-split-at (/ (length it) 2) it) l))) '((1 2 3 4 5 6))) => '((1) (2) (3) (4) (5) (6))
(let ((data '(("starwars" "scifi")
("jedi" "starwars" "warrior"))))
(--fix (-uniq (--mapcat (cons it (cdr (assoc it data))) it)) '("jedi" "book"))) => '("jedi" "starwars" "warrior" "scifi" "book")))
(-fix (lambda (l) (-non-nil (--mapcat (-split-at (/ (length it) 2) it) l))) '((1 2 3))) => '((1) (2) (3))
(let ((l '((starwars scifi)
(jedi starwars warrior))))
(--fix (-uniq (--mapcat (cons it (cdr (assq it l))) it)) '(jedi book)))
=> '(jedi starwars warrior scifi book)))
(def-example-group "Tree operations"
"Functions pretending lists are trees."

Loading…
Cancel
Save