diff --git a/README.md b/README.md index 7562956..669c241 100644 --- a/README.md +++ b/README.md @@ -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) ``` diff --git a/dash.el b/dash.el index 0251288..5f6b8e5 100644 --- a/dash.el +++ b/dash.el @@ -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. diff --git a/dash.texi b/dash.texi index 1b0addf..a52633a 100644 --- a/dash.texi +++ b/dash.texi @@ -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 diff --git a/dev/examples.el b/dev/examples.el index 575684d..564d3ef 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -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."