diff --git a/README.md b/README.md index f7e9266..ff51a38 100644 --- a/README.md +++ b/README.md @@ -541,16 +541,19 @@ For similar operations, see also [`-keep`](#-keep-fn-list) and [`-filter`](#-fil #### -remove-first `(pred list)` -Return a new list with the first item matching `pred` removed. - -Alias: `-reject-first` - -See also: [`-remove`](#-remove-pred-list), [`-map-first`](#-map-first-pred-rep-list) - -```el -(-remove-first 'even? '(1 3 5 4 7 8 10)) ;; => '(1 3 5 7 8 10) -(-remove-first 'stringp '(1 2 "first" "second" "third")) ;; => '(1 2 "second" "third") -(--remove-first (> it 3) '(1 2 3 4 5 6 7 8 9 10)) ;; => '(1 2 3 5 6 7 8 9 10) +Remove the first item from `list` for which `pred` returns non-nil. +This is a non-destructive operation, but only the front of `list` +leading up to the removed item is a copy; the rest is `list``s +original tail. If no item is removed, then the result is a +complete copy. +Alias: `-reject-first`. +This function's anaphoric counterpart is `--remove-first`. +See also [`-map-first`](#-map-first-pred-rep-list), [`-remove-item`](#-remove-item-item-list), and [`-remove-last`](#-remove-last-pred-list). + +```el +(-remove-first #'natnump '(-2 -1 0 1 2)) ;; => '(-2 -1 1 2) +(-remove-first #'stringp '(1 2 "first" "second")) ;; => '(1 2 "second") +(--remove-first (> it 3) '(1 2 3 4 5 6)) ;; => '(1 2 3 5 6) ``` #### -remove-last `(pred list)` diff --git a/dash.el b/dash.el index da701ae..3c51812 100644 --- a/dash.el +++ b/dash.el @@ -456,24 +456,34 @@ For similar operations, see also `-keep' and `-filter'." (defalias '-reject '-remove) (defalias '--reject '--remove) -(defun -remove-first (pred list) - "Return a new list with the first item matching PRED removed. - -Alias: `-reject-first' - -See also: `-remove', `-map-first'" - (let (front) - (while (and list (not (funcall pred (car list)))) - (push (car list) front) - (!cdr list)) - (if list - (-concat (nreverse front) (cdr list)) - (nreverse front)))) - (defmacro --remove-first (form list) - "Anaphoric form of `-remove-first'." + "Remove the first item from LIST for which FORM evals to non-nil. +Each element of LIST in turn is bound to `it' and its index +within LIST to `it-index' before evaluating FORM. This is a +non-destructive operation, but only the front of LIST leading up +to the removed item is a copy; the rest is LIST's original tail. +If no item is removed, then the result is a complete copy. +This is the anaphoric counterpart to `-remove-first'." (declare (debug (form form))) - `(-remove-first (lambda (it) ,form) ,list)) + (let ((front (make-symbol "front")) + (tail (make-symbol "tail"))) + `(let ((,tail ,list) ,front) + (--each-while ,tail (not ,form) + (push (pop ,tail) ,front)) + (if ,tail + (nconc (nreverse ,front) (cdr ,tail)) + (nreverse ,front))))) + +(defun -remove-first (pred list) + "Remove the first item from LIST for which PRED returns non-nil. +This is a non-destructive operation, but only the front of LIST +leading up to the removed item is a copy; the rest is LIST's +original tail. If no item is removed, then the result is a +complete copy. +Alias: `-reject-first'. +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) (defalias '--reject-first '--remove-first) diff --git a/dash.texi b/dash.texi index ae9b03d..a98d7df 100644 --- a/dash.texi +++ b/dash.texi @@ -528,24 +528,27 @@ For similar operations, see also @code{-keep} (@pxref{-keep}) and @code{-filter} @anchor{-remove-first} @defun -remove-first (pred list) -Return a new list with the first item matching @var{pred} removed. - -Alias: @code{-reject-first} - -See also: @code{-remove} (@pxref{-remove}), @code{-map-first} (@pxref{-map-first}) +Remove the first item from @var{list} for which @var{pred} returns non-nil. +This is a non-destructive operation, but only the front of @var{list} +leading up to the removed item is a copy; the rest is @var{list}'s +original tail. If no item is removed, then the result is a +complete copy. +Alias: @code{-reject-first}. +This function's anaphoric counterpart is @code{--remove-first}. +See also @code{-map-first} (@pxref{-map-first}), @code{-remove-item} (@pxref{-remove-item}), and @code{-remove-last} (@pxref{-remove-last}). @example @group -(-remove-first 'even? '(1 3 5 4 7 8 10)) - @result{} '(1 3 5 7 8 10) +(-remove-first #'natnump '(-2 -1 0 1 2)) + @result{} '(-2 -1 1 2) @end group @group -(-remove-first 'stringp '(1 2 "first" "second" "third")) - @result{} '(1 2 "second" "third") +(-remove-first #'stringp '(1 2 "first" "second")) + @result{} '(1 2 "second") @end group @group -(--remove-first (> it 3) '(1 2 3 4 5 6 7 8 9 10)) - @result{} '(1 2 3 5 6 7 8 9 10) +(--remove-first (> it 3) '(1 2 3 4 5 6)) + @result{} '(1 2 3 5 6) @end group @end example @end defun diff --git a/dev/examples.el b/dev/examples.el index c2e6dbf..30e55c3 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -160,13 +160,16 @@ new list." (--remove nil '(1)) => '(1)) (defexamples -remove-first - (-remove-first 'even? '(1 3 5 4 7 8 10)) => '(1 3 5 7 8 10) - (-remove-first 'stringp '(1 2 "first" "second" "third")) => '(1 2 "second" "third") - (--remove-first (> it 3) '(1 2 3 4 5 6 7 8 9 10)) => '(1 2 3 5 6 7 8 9 10) - (-remove-first 'even? '(2 3 4)) => '(3 4) - (-remove-first 'even? '(3 5 7 4)) => '(3 5 7) - (-remove-first 'even? '(2)) => nil - (-remove-first 'even? '(1 3 5 7)) => '(1 3 5 7)) + (-remove-first #'natnump '(-2 -1 0 1 2)) => '(-2 -1 1 2) + (-remove-first #'stringp '(1 2 "first" "second")) => '(1 2 "second") + (--remove-first (> it 3) '(1 2 3 4 5 6)) => '(1 2 3 5 6) + (-remove-first #'natnump '(2 3 4)) => '(3 4) + (-remove-first #'natnump '(-3 -2 -1 4)) => '(-3 -2 -1) + (-remove-first #'natnump '(2)) => '() + (-remove-first #'natnump '()) => '() + (-remove-first #'null '(1 3 5 7)) => '(1 3 5 7) + (let ((l (list 1 2))) (setcar (-remove-first #'identity l) 0) l) => '(1 0) + (let ((l (list 1 2))) (setcar (-remove-first #'null l) 0) l) => '(1 2)) (defexamples -remove-last (-remove-last 'even? '(1 3 5 4 7 8 10 11)) => '(1 3 5 4 7 8 11)