branch: externals/dash commit 11a629639111cb919ca6d2942270fb1999d540de Author: Basil L. Contovounesios <conto...@tcd.ie> Commit: Basil L. Contovounesios <conto...@tcd.ie>
Prefer n-ary over unary/binary combinators This is a rewrite and extension of PR #72 to address issue #306. For discussion, especially wrt performance, see PRs #72 and #308. * dash.el (-on, -flip, -not, -orfn, -andfn): Return a variadic function. Declare as pure and side-effect-free. (-rotate-args): New combinator suggested by @vapniks in PR #72. (-const): Declare as pure and side-effect-free. * NEWS.md (2.19.0): Announce -rotate-args and variadic combinators. * dev/examples.el (-partition-after-pred): Fix oddp bug waiting to happen with negative dividends. (-cons*): Check that &rest args are safe to mutate. (-on, -flip, -const, -not, -orfn, -andfn): Extend tests. (-rotate-args): New test. * README.md: * dash.texi: Regenerate docs. --- NEWS.md | 4 ++ README.md | 113 ++++++++++++++++++++++++++-------------- dash.el | 126 ++++++++++++++++++++++++++++++++++---------- dash.texi | 138 +++++++++++++++++++++++++++++++++--------------- dev/examples.el | 159 ++++++++++++++++++++++++++++++++++++++++++++------------ 5 files changed, 399 insertions(+), 141 deletions(-) diff --git a/NEWS.md b/NEWS.md index efcf675..bd9700e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,6 +18,10 @@ See the end of the file for license conditions. #### New features +- The combinators `-on`, `-flip`, `-not`, `-andfn`, and `-orfn` now + return variadic functions that take any number of arguments (#308). +- New combinator `-rotate-args` similar to `-flip`, but for arbitrary + arglist rotations (suggested by @vapniks, #72). - New function `-every` and its anaphoric macro counterpart `--every`. They are like the existing `-every-p` and `--every-p`, respectively, but return the last non-`nil` result instead of just `t`. diff --git a/README.md b/README.md index c738bf9..00fc4d7 100644 --- a/README.md +++ b/README.md @@ -373,8 +373,9 @@ Functions that manipulate and compose other functions. * [`-juxt`](#-juxt-rest-fns) `(&rest fns)` * [`-compose`](#-compose-rest-fns) `(&rest fns)` * [`-applify`](#-applify-fn) `(fn)` -* [`-on`](#-on-operator-transformer) `(operator transformer)` -* [`-flip`](#-flip-func) `(func)` +* [`-on`](#-on-op-trans) `(op trans)` +* [`-flip`](#-flip-fn) `(fn)` +* [`-rotate-args`](#-rotate-args-n-fn) `(n fn)` * [`-const`](#-const-c) `(c)` * [`-cut`](#-cut-rest-params) `(&rest params)` * [`-not`](#-not-pred) `(pred)` @@ -1193,7 +1194,7 @@ Return the smallest value from `list` of numbers or markers. Take a comparison function `comparator` and a `list` and return the least element of the list by the comparison function. -See also combinator [`-on`](#-on-operator-transformer) which can transform the values before +See also combinator [`-on`](#-on-op-trans) which can transform the values before comparing them. ```el @@ -1217,7 +1218,7 @@ Return the largest value from `list` of numbers or markers. Take a comparison function `comparator` and a `list` and return the greatest element of the list by the comparison function. -See also combinator [`-on`](#-on-operator-transformer) which can transform the values before +See also combinator [`-on`](#-on-op-trans) which can transform the values before comparing them. ```el @@ -2892,30 +2893,56 @@ taking 1 argument which is a list of `n` arguments. (funcall (-applify #'<) '(3 6)) ;; => t ``` -#### -on `(operator transformer)` +#### -on `(op trans)` -Return a function of two arguments that first applies -`transformer` to each of them and then applies `operator` on the -results (in the same order). +Return a function that calls `trans` on each arg and `op` on the results. +The returned function takes a variable number of arguments, calls +the function `trans` on each one in turn, and then passes those +results as the list of arguments to `op`, in the same order. -In types: (b -> b -> c) -> (a -> b) -> a -> a -> c +For example, the following pairs of expressions are morally +equivalent: + + (funcall (-on #'+ #'1+) 1 2 3) = (+ (1+ 1) (1+ 2) (1+ 3)) + (funcall (-on #'+ #'1+)) = (+) ```el -(-sort (-on '< 'length) '((1 2 3) (1) (1 2))) ;; => ((1) (1 2) (1 2 3)) -(-min-by (-on '> 'length) '((1 2 3) (4) (1 2))) ;; => (4) -(-min-by (-on 'string-lessp 'number-to-string) '(2 100 22)) ;; => 22 +(-sort (-on #'< #'length) '((1 2 3) (1) (1 2))) ;; => ((1) (1 2) (1 2 3)) +(funcall (-on #'min #'string-to-number) "22" "2" "1" "12") ;; => 1 +(-min-by (-on #'> #'length) '((1 2 3) (4) (1 2))) ;; => (4) ``` -#### -flip `(func)` +#### -flip `(fn)` + +Return a function that calls `fn` with its arguments reversed. +The returned function takes the same number of arguments as `fn`. + +For example, the following two expressions are morally +equivalent: -Swap the order of arguments for binary function `func`. + (funcall (-flip #'-) 1 2) = (- 2 1) -In types: (a -> b -> c) -> b -> a -> c +See also: [`-rotate-args`](#-rotate-args-n-fn). ```el -(funcall (-flip '<) 2 1) ;; => t -(funcall (-flip '-) 3 8) ;; => 5 -(-sort (-flip '<) '(4 3 6 1)) ;; => (6 4 3 1) +(-sort (-flip #'<) '(4 3 6 1)) ;; => (6 4 3 1) +(funcall (-flip #'-) 3 2 1 10) ;; => 4 +(funcall (-flip #'1+) 1) ;; => 2 +``` + +#### -rotate-args `(n fn)` + +Return a function that calls `fn` with args rotated `n` places to the right. +The returned function takes the same number of arguments as `fn`, +rotates the list of arguments `n` places to the right (left if `n` is +negative) just like [`-rotate`](#-rotate-n-list), and applies `fn` to the result. + +See also: [`-flip`](#-flip-fn). + +```el +(funcall (-rotate-args -1 #'list) 1 2 3 4) ;; => (2 3 4 1) +(funcall (-rotate-args 1 #'-) 1 10 100) ;; => 89 +(funcall (-rotate-args 2 #'list) 3 4 5 1 2) ;; => (1 2 3 4 5) ``` #### -const `(c)` @@ -2926,8 +2953,8 @@ In types: a -> b -> a ```el (funcall (-const 2) 1 3 "foo") ;; => 2 -(-map (-const 1) '("a" "b" "c" "d")) ;; => (1 1 1 1) -(-sum (-map (-const 1) '("a" "b" "c" "d"))) ;; => 4 +(mapcar (-const 1) '("a" "b" "c" "d")) ;; => (1 1 1 1) +(-sum (mapcar (-const 1) '("a" "b" "c" "d"))) ;; => 4 ``` #### -cut `(&rest params)` @@ -2945,40 +2972,50 @@ See `srfi-26` for detailed description. #### -not `(pred)` -Take a unary predicate `pred` and return a unary predicate -that returns t if `pred` returns nil and nil if `pred` returns -non-nil. +Return a predicate that negates the result of `pred`. +The returned predicate passes its arguments to `pred`. If `pred` +returns nil, the result is non-nil; otherwise the result is nil. + +See also: [`-andfn`](#-andfn-rest-preds) and [`-orfn`](#-orfn-rest-preds). ```el -(funcall (-not 'even?) 5) ;; => t -(-filter (-not (-partial '< 4)) '(1 2 3 4 5 6 7 8)) ;; => (1 2 3 4) +(funcall (-not #'numberp) "5") ;; => t +(-sort (-not #'<) '(5 2 1 0 6)) ;; => (6 5 2 1 0) +(-filter (-not (-partial #'< 4)) '(1 2 3 4 5 6 7 8)) ;; => (1 2 3 4) ``` #### -orfn `(&rest preds)` -Take list of unary predicates `preds` and return a unary -predicate with argument x that returns non-nil if at least one of -the `preds` returns non-nil on x. +Return a predicate that returns the first non-nil result of `preds`. +The returned predicate takes a variable number of arguments, +passes them to each predicate in `preds` in turn until one of them +returns non-nil, and returns that non-nil result without calling +the remaining `preds`. If all `preds` return nil, or if no `preds` are +given, the returned predicate returns nil. -In types: [a -> Bool] -> a -> Bool +See also: [`-andfn`](#-andfn-rest-preds) and [`-not`](#-not-pred). ```el -(-filter (-orfn 'even? (-partial (-flip '<) 5)) '(1 2 3 4 5 6 7 8 9 10)) ;; => (1 2 3 4 6 8 10) -(funcall (-orfn 'stringp 'even?) "foo") ;; => t +(-filter (-orfn #'natnump #'booleanp) '(1 nil "a" -4 b c t)) ;; => (1 nil t) +(funcall (-orfn #'symbolp (-cut string-match-p "x" <>)) "axe") ;; => 1 +(funcall (-orfn #'= #'+) 1 1) ;; => t ``` #### -andfn `(&rest preds)` -Take list of unary predicates `preds` and return a unary -predicate with argument x that returns non-nil if all of the -`preds` returns non-nil on x. +Return a predicate that returns non-nil if all `preds` do so. +The returned predicate `p` takes a variable number of arguments and +passes them to each predicate in `preds` in turn. If any one of +`preds` returns nil, `p` also returns nil without calling the +remaining `preds`. If all `preds` return non-nil, `p` returns the last +such value. If no `preds` are given, `p` always returns non-nil. -In types: [a -> Bool] -> a -> Bool +See also: [`-orfn`](#-orfn-rest-preds) and [`-not`](#-not-pred). ```el -(funcall (-andfn (-cut < <> 10) 'even?) 6) ;; => t -(funcall (-andfn (-cut < <> 10) 'even?) 12) ;; => nil -(-filter (-andfn (-not 'even?) (-cut >= 5 <>)) '(1 2 3 4 5 6 7 8 9 10)) ;; => (1 3 5) +(-filter (-andfn #'numberp (-cut < <> 5)) '(a 1 b 6 c 2)) ;; => (1 2) +(mapcar (-andfn #'numberp #'1+) '(a 1 b 6)) ;; => (nil 2 nil 7) +(funcall (-andfn #'= #'+) 1 1) ;; => 2 ``` #### -iteratefn `(fn n)` diff --git a/dash.el b/dash.el index 6f97aca..a0be8f6 100644 --- a/dash.el +++ b/dash.el @@ -3113,24 +3113,73 @@ taking 1 argument which is a list of N arguments." (declare (pure t) (side-effect-free t)) (lambda (args) (apply fn args))) -(defun -on (operator transformer) - "Return a function of two arguments that first applies -TRANSFORMER to each of them and then applies OPERATOR on the -results (in the same order). +(defun -on (op trans) + "Return a function that calls TRANS on each arg and OP on the results. +The returned function takes a variable number of arguments, calls +the function TRANS on each one in turn, and then passes those +results as the list of arguments to OP, in the same order. -In types: (b -> b -> c) -> (a -> b) -> a -> a -> c" - (lambda (x y) (funcall operator (funcall transformer x) (funcall transformer y)))) +For example, the following pairs of expressions are morally +equivalent: -(defun -flip (func) - "Swap the order of arguments for binary function FUNC. - -In types: (a -> b -> c) -> b -> a -> c" - (lambda (x y) (funcall func y x))) + (funcall (-on #\\='+ #\\='1+) 1 2 3) = (+ (1+ 1) (1+ 2) (1+ 3)) + (funcall (-on #\\='+ #\\='1+)) = (+)" + (declare (pure t) (side-effect-free t)) + (lambda (&rest args) + ;; This unrolling seems to be a relatively cheap way to keep the + ;; overhead of `mapcar' + `apply' in check. + (cond ((cddr args) + (apply op (mapcar trans args))) + ((cdr args) + (funcall op (funcall trans (car args)) (funcall trans (cadr args)))) + (args + (funcall op (funcall trans (car args)))) + ((funcall op))))) + +(defun -flip (fn) + "Return a function that calls FN with its arguments reversed. +The returned function takes the same number of arguments as FN. + +For example, the following two expressions are morally +equivalent: + + (funcall (-flip #\\='-) 1 2) = (- 2 1) + +See also: `-rotate-args'." + (declare (pure t) (side-effect-free t)) + (lambda (&rest args) ;; Open-code for speed. + (cond ((cddr args) (apply fn (nreverse args))) + ((cdr args) (funcall fn (cadr args) (car args))) + (args (funcall fn (car args))) + ((funcall fn))))) + +(defun -rotate-args (n fn) + "Return a function that calls FN with args rotated N places to the right. +The returned function takes the same number of arguments as FN, +rotates the list of arguments N places to the right (left if N is +negative) just like `-rotate', and applies FN to the result. + +See also: `-flip'." + (declare (pure t) (side-effect-free t)) + (if (zerop n) + fn + (let ((even (= (% n 2) 0))) + (lambda (&rest args) + (cond ((cddr args) ;; Open-code for speed. + (apply fn (-rotate n args))) + ((cdr args) + (let ((fst (car args)) + (snd (cadr args))) + (funcall fn (if even fst snd) (if even snd fst)))) + (args + (funcall fn (car args))) + ((funcall fn))))))) (defun -const (c) "Return a function that returns C ignoring any additional arguments. In types: a -> b -> a" + (declare (pure t) (side-effect-free t)) (lambda (&rest _) c)) (defmacro -cut (&rest params) @@ -3147,30 +3196,51 @@ See SRFI-26 for detailed description." `(lambda ,args ,(let ((body (--map (if (eq it '<>) (pop args) it) params))) (if (eq (car params) '<>) - (cons 'funcall body) + (cons #'funcall body) body))))) (defun -not (pred) - "Take a unary predicate PRED and return a unary predicate -that returns t if PRED returns nil and nil if PRED returns -non-nil." - (lambda (x) (not (funcall pred x)))) + "Return a predicate that negates the result of PRED. +The returned predicate passes its arguments to PRED. If PRED +returns nil, the result is non-nil; otherwise the result is nil. -(defun -orfn (&rest preds) - "Take list of unary predicates PREDS and return a unary -predicate with argument x that returns non-nil if at least one of -the PREDS returns non-nil on x. +See also: `-andfn' and `-orfn'." + (declare (pure t) (side-effect-free t)) + (lambda (&rest args) (not (apply pred args)))) -In types: [a -> Bool] -> a -> Bool" - (lambda (x) (-any? (-cut funcall <> x) preds))) +(defun -orfn (&rest preds) + "Return a predicate that returns the first non-nil result of PREDS. +The returned predicate takes a variable number of arguments, +passes them to each predicate in PREDS in turn until one of them +returns non-nil, and returns that non-nil result without calling +the remaining PREDS. If all PREDS return nil, or if no PREDS are +given, the returned predicate returns nil. + +See also: `-andfn' and `-not'." + (declare (pure t) (side-effect-free t)) + ;; Open-code for speed. + (cond ((cdr preds) (lambda (&rest args) (--some (apply it args) preds))) + (preds (car preds)) + (#'ignore))) (defun -andfn (&rest preds) - "Take list of unary predicates PREDS and return a unary -predicate with argument x that returns non-nil if all of the -PREDS returns non-nil on x. - -In types: [a -> Bool] -> a -> Bool" - (lambda (x) (-all? (-cut funcall <> x) preds))) + "Return a predicate that returns non-nil if all PREDS do so. +The returned predicate P takes a variable number of arguments and +passes them to each predicate in PREDS in turn. If any one of +PREDS returns nil, P also returns nil without calling the +remaining PREDS. If all PREDS return non-nil, P returns the last +such value. If no PREDS are given, P always returns non-nil. + +See also: `-orfn' and `-not'." + (declare (pure t) (side-effect-free t)) + ;; Open-code for speed. + (cond ((cdr preds) (lambda (&rest args) (--every (apply it args) preds))) + (preds (car preds)) + ;; As a `pure' function, this runtime check may generate + ;; backward-incompatible bytecode for `(-andfn)' at compile-time, + ;; but I doubt that's a problem in practice (famous last words). + ((fboundp 'always) #'always) + ((lambda (&rest _) t)))) (defun -iteratefn (fn n) "Return a function FN composed N times with itself. diff --git a/dash.texi b/dash.texi index e7e0376..0d30090 100644 --- a/dash.texi +++ b/dash.texi @@ -4357,47 +4357,83 @@ taking 1 argument which is a list of @var{n} arguments. @end defun @anchor{-on} -@defun -on (operator transformer) -Return a function of two arguments that first applies -@var{transformer} to each of them and then applies @var{operator} on the -results (in the same order). +@defun -on (op trans) +Return a function that calls @var{trans} on each arg and @var{op} on the results. +The returned function takes a variable number of arguments, calls +the function @var{trans} on each one in turn, and then passes those +results as the list of arguments to @var{op}, in the same order. -In types: (b -> b -> c) -> (a -> b) -> a -> a -> c +For example, the following pairs of expressions are morally +equivalent: + + (funcall (-on #'+ #'1+) 1 2 3) = (+ (1+ 1) (1+ 2) (1+ 3)) + (funcall (-on #'+ #'1+)) = (+) @example @group -(-sort (-on '< 'length) '((1 2 3) (1) (1 2))) +(-sort (-on #'< #'length) '((1 2 3) (1) (1 2))) @result{} ((1) (1 2) (1 2 3)) @end group @group -(-min-by (-on '> 'length) '((1 2 3) (4) (1 2))) - @result{} (4) +(funcall (-on #'min #'string-to-number) "22" "2" "1" "12") + @result{} 1 @end group @group -(-min-by (-on 'string-lessp 'number-to-string) '(2 100 22)) - @result{} 22 +(-min-by (-on #'> #'length) '((1 2 3) (4) (1 2))) + @result{} (4) @end group @end example @end defun @anchor{-flip} -@defun -flip (func) -Swap the order of arguments for binary function @var{func}. +@defun -flip (fn) +Return a function that calls @var{fn} with its arguments reversed. +The returned function takes the same number of arguments as @var{fn}. + +For example, the following two expressions are morally +equivalent: -In types: (a -> b -> c) -> b -> a -> c + (funcall (-flip #'-) 1 2) = (- 2 1) + +See also: @code{-rotate-args} (@pxref{-rotate-args}). @example @group -(funcall (-flip '<) 2 1) - @result{} t +(-sort (-flip #'<) '(4 3 6 1)) + @result{} (6 4 3 1) @end group @group -(funcall (-flip '-) 3 8) - @result{} 5 +(funcall (-flip #'-) 3 2 1 10) + @result{} 4 @end group @group -(-sort (-flip '<) '(4 3 6 1)) - @result{} (6 4 3 1) +(funcall (-flip #'1+) 1) + @result{} 2 +@end group +@end example +@end defun + +@anchor{-rotate-args} +@defun -rotate-args (n fn) +Return a function that calls @var{fn} with args rotated @var{n} places to the right. +The returned function takes the same number of arguments as @var{fn}, +rotates the list of arguments @var{n} places to the right (left if @var{n} is +negative) just like @code{-rotate} (@pxref{-rotate}), and applies @var{fn} to the result. + +See also: @code{-flip} (@pxref{-flip}). + +@example +@group +(funcall (-rotate-args -1 #'list) 1 2 3 4) + @result{} (2 3 4 1) +@end group +@group +(funcall (-rotate-args 1 #'-) 1 10 100) + @result{} 89 +@end group +@group +(funcall (-rotate-args 2 #'list) 3 4 5 1 2) + @result{} (1 2 3 4 5) @end group @end example @end defun @@ -4414,11 +4450,11 @@ In types: a -> b -> a @result{} 2 @end group @group -(-map (-const 1) '("a" "b" "c" "d")) +(mapcar (-const 1) '("a" "b" "c" "d")) @result{} (1 1 1 1) @end group @group -(-sum (-map (-const 1) '("a" "b" "c" "d"))) +(-sum (mapcar (-const 1) '("a" "b" "c" "d"))) @result{} 4 @end group @end example @@ -4449,17 +4485,23 @@ See @var{srfi-26} for detailed description. @anchor{-not} @defun -not (pred) -Take a unary predicate @var{pred} and return a unary predicate -that returns t if @var{pred} returns nil and nil if @var{pred} returns -non-nil. +Return a predicate that negates the result of @var{pred}. +The returned predicate passes its arguments to @var{pred}. If @var{pred} +returns nil, the result is non-nil; otherwise the result is nil. + +See also: @code{-andfn} (@pxref{-andfn}) and @code{-orfn} (@pxref{-orfn}). @example @group -(funcall (-not 'even?) 5) +(funcall (-not #'numberp) "5") @result{} t @end group @group -(-filter (-not (-partial '< 4)) '(1 2 3 4 5 6 7 8)) +(-sort (-not #'<) '(5 2 1 0 6)) + @result{} (6 5 2 1 0) +@end group +@group +(-filter (-not (-partial #'< 4)) '(1 2 3 4 5 6 7 8)) @result{} (1 2 3 4) @end group @end example @@ -4467,19 +4509,26 @@ non-nil. @anchor{-orfn} @defun -orfn (&rest preds) -Take list of unary predicates @var{preds} and return a unary -predicate with argument x that returns non-nil if at least one of -the @var{preds} returns non-nil on x. +Return a predicate that returns the first non-nil result of @var{preds}. +The returned predicate takes a variable number of arguments, +passes them to each predicate in @var{preds} in turn until one of them +returns non-nil, and returns that non-nil result without calling +the remaining @var{preds}. If all @var{preds} return nil, or if no @var{preds} are +given, the returned predicate returns nil. -In types: [a -> Bool] -> a -> Bool +See also: @code{-andfn} (@pxref{-andfn}) and @code{-not} (@pxref{-not}). @example @group -(-filter (-orfn 'even? (-partial (-flip '<) 5)) '(1 2 3 4 5 6 7 8 9 10)) - @result{} (1 2 3 4 6 8 10) +(-filter (-orfn #'natnump #'booleanp) '(1 nil "a" -4 b c t)) + @result{} (1 nil t) +@end group +@group +(funcall (-orfn #'symbolp (-cut string-match-p "x" <>)) "axe") + @result{} 1 @end group @group -(funcall (-orfn 'stringp 'even?) "foo") +(funcall (-orfn #'= #'+) 1 1) @result{} t @end group @end example @@ -4487,24 +4536,27 @@ In types: [a -> Bool] -> a -> Bool @anchor{-andfn} @defun -andfn (&rest preds) -Take list of unary predicates @var{preds} and return a unary -predicate with argument x that returns non-nil if all of the -@var{preds} returns non-nil on x. +Return a predicate that returns non-nil if all @var{preds} do so. +The returned predicate @var{p} takes a variable number of arguments and +passes them to each predicate in @var{preds} in turn. If any one of +@var{preds} returns nil, @var{p} also returns nil without calling the +remaining @var{preds}. If all @var{preds} return non-nil, @var{p} returns the last +such value. If no @var{preds} are given, @var{p} always returns non-nil. -In types: [a -> Bool] -> a -> Bool +See also: @code{-orfn} (@pxref{-orfn}) and @code{-not} (@pxref{-not}). @example @group -(funcall (-andfn (-cut < <> 10) 'even?) 6) - @result{} t +(-filter (-andfn #'numberp (-cut < <> 5)) '(a 1 b 6 c 2)) + @result{} (1 2) @end group @group -(funcall (-andfn (-cut < <> 10) 'even?) 12) - @result{} nil +(mapcar (-andfn #'numberp #'1+) '(a 1 b 6)) + @result{} (nil 2 nil 7) @end group @group -(-filter (-andfn (-not 'even?) (-cut >= 5 <>)) '(1 2 3 4 5 6 7 8 9 10)) - @result{} (1 3 5) +(funcall (-andfn #'= #'+) 1 1) + @result{} 2 @end group @end example @end defun diff --git a/dev/examples.el b/dev/examples.el index d5d5210..0125da2 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -871,15 +871,15 @@ value rather than consuming a list to produce a single value." (-partition-after-pred #'booleanp '(t)) => '((t)) (-partition-after-pred #'booleanp '(0 t)) => '((0 t)) (--partition-after-pred (= (% it 2) 0) '()) => '() - (--partition-after-pred (= (% it 2) 1) '()) => '() + (--partition-after-pred (= (mod it 2) 1) '()) => '() (--partition-after-pred (= (% it 2) 0) '(0)) => '((0)) - (--partition-after-pred (= (% it 2) 1) '(0)) => '((0)) + (--partition-after-pred (= (mod it 2) 1) '(0)) => '((0)) (--partition-after-pred (= (% it 2) 0) '(0 1)) => '((0) (1)) - (--partition-after-pred (= (% it 2) 1) '(0 1)) => '((0 1)) + (--partition-after-pred (= (mod it 2) 1) '(0 1)) => '((0 1)) (--partition-after-pred (= (% it 2) 0) '(0 1 2)) => '((0) (1 2)) - (--partition-after-pred (= (% it 2) 1) '(0 1 2)) => '((0 1) (2)) + (--partition-after-pred (= (mod it 2) 1) '(0 1 2)) => '((0 1) (2)) (--partition-after-pred (= (% it 2) 0) '(0 1 2 3)) => '((0) (1 2) (3)) - (--partition-after-pred (= (% it 2) 1) '(0 1 2 3)) => '((0 1) (2 3)) + (--partition-after-pred (= (mod it 2) 1) '(0 1 2 3)) => '((0 1) (2 3)) (--partition-after-pred t '()) => () (--partition-after-pred t '(0)) => '((0)) (--partition-after-pred t '(0 1)) => '((0) (1)) @@ -1055,7 +1055,9 @@ related predicates." (-cons*) => () (-cons* ()) => () (-cons* 1 ()) => '(1) - (-cons* 1 '(2)) => '(1 2)) + (-cons* 1 '(2)) => '(1 2) + ;; Assert that &rest conses a fresh list in case that ever changes. + (let ((l (list 1 2))) (apply #'-cons* l) l) => '(1 2)) (defexamples -snoc (-snoc '(1 2 3) 4) => '(1 2 3 4) @@ -1769,30 +1771,83 @@ or readability." => '((1 (1)) (1 (2)) (5 (5)))) (defexamples -on - (-sort (-on '< 'length) '((1 2 3) (1) (1 2))) => '((1) (1 2) (1 2 3)) - (-min-by (-on '> 'length) '((1 2 3) (4) (1 2))) => '(4) - (-min-by (-on 'string-lessp 'number-to-string) '(2 100 22)) => 22 - (-max-by (-on '> 'car) '((2 2 3) (3) (1 2))) => '(3) - (-sort (-on 'string-lessp 'number-to-string) '(10 12 1 2 22)) => '(1 10 12 2 22) - (funcall (-on '+ '1+) 1 2) => 5 - (funcall (-on '+ 'identity) 1 2) => 3 - (funcall (-on '* 'length) '(1 2 3) '(4 5)) => 6 - (funcall (-on (-on '+ 'length) 'cdr) '(1 2 3) '(4 5)) => 3 - (funcall (-on '+ (lambda (x) (length (cdr x)))) '(1 2 3) '(4 5)) => 3 - (-sort (-on '< 'car) '((3 2 5) (2) (1 2))) => '((1 2) (2) (3 2 5)) - (-sort (-on '< (lambda (x) (length x))) '((1 2 3) (1) (1 2))) => '((1) (1 2) (1 2 3)) - (-sort (-on (-on '< 'car) 'cdr) '((0 3) (2 1) (4 2 8))) => '((2 1) (4 2 8) (0 3)) - (-sort (-on '< 'cadr) '((0 3) (2 1) (4 2 8))) => '((2 1) (4 2 8) (0 3))) + (-sort (-on #'< #'length) '((1 2 3) (1) (1 2))) => '((1) (1 2) (1 2 3)) + (funcall (-on #'min #'string-to-number) "22" "2" "1" "12") => 1 + (-min-by (-on #'> #'length) '((1 2 3) (4) (1 2))) => '(4) + (-min-by (-on #'string< #'number-to-string) '(2 100 22)) => 22 + (-max-by (-on #'> #'car) '((2 2 3) (3) (1 2))) => '(3) + (-sort (-on #'string< #'number-to-string) '(12 1 2 22)) => '(1 12 2 22) + (funcall (-on #'+ #'1+) 1 2) => 5 + (funcall (-on #'+ #'identity) 1 2) => 3 + (funcall (-on #'* #'length) '(1 2 3) '(4 5)) => 6 + (funcall (-on (-on #'+ #'length) #'cdr) '(1 2 3) '(4 5)) => 3 + (funcall (-on #'+ (lambda (x) (length (cdr x)))) '(1 2 3) '(4 5)) => 3 + (-sort (-on #'< #'car) '((3 2 5) (2) (1 2))) => '((1 2) (2) (3 2 5)) + (-sort (-on #'< (lambda (x) (length x))) '((1 2 3) (1) (1 2))) + => '((1) (1 2) (1 2 3)) + (-sort (-on (-on #'< #'car) #'cdr) '((0 3) (2 1) (4 2 8))) + => '((2 1) (4 2 8) (0 3)) + (-sort (-on #'< #'cadr) '((0 3) (2 1) (4 2 8))) => '((2 1) (4 2 8) (0 3)) + (funcall (-on #'not #'not) nil) => nil + (funcall (-on #'+ #'1+) 1 10 100 1000) => 1115 + (funcall (-on #'+ #'1+) 1 10 100) => 114 + (funcall (-on #'+ #'1+) 1 10) => 13 + (funcall (-on #'+ #'1+) 1) => 2 + (funcall (-on #'+ #'1+)) => 0 + (funcall (-on #'1+ #'1+) 0) => 2 + (funcall (-on #'+ #'*)) => 0 + (funcall (-on #'* #'+)) => 1) (defexamples -flip - (funcall (-flip '<) 2 1) => t - (funcall (-flip '-) 3 8) => 5 - (-sort (-flip '<) '(4 3 6 1)) => '(6 4 3 1)) + (-sort (-flip #'<) '(4 3 6 1)) => '(6 4 3 1) + (funcall (-flip #'-) 3 2 1 10) => 4 + (funcall (-flip #'1+) 1) => 2 + (funcall (-flip #'<) 2 1) => t + (funcall (-flip #'list) 1 2 3) => '(3 2 1) + (funcall (-flip #'list) 1 2) => '(2 1) + (funcall (-flip #'list) 1) => '(1) + (funcall (-flip #'list)) => '() + ;; Assert that &rest conses a fresh list in case that ever changes. + (let ((a (list 1 2 3 4))) (apply (-flip #'-) a) a) => '(1 2 3 4)) + + (defexamples -rotate-args + (funcall (-rotate-args -1 #'list) 1 2 3 4) => '(2 3 4 1) + (funcall (-rotate-args 1 #'-) 1 10 100) => 89 + (funcall (-rotate-args 2 #'list) 3 4 5 1 2) => '(1 2 3 4 5) + (funcall (-rotate-args -2 #'list) 1 2 3 4) => '(3 4 1 2) + (funcall (-rotate-args 0 #'list) 1 2 3 4) => '(1 2 3 4) + (funcall (-rotate-args 1 #'list) 1 2 3 4) => '(4 1 2 3) + (funcall (-rotate-args 2 #'list) 1 2 3 4) => '(3 4 1 2) + (funcall (-rotate-args -2 #'list) 1 2 3) => '(3 1 2) + (funcall (-rotate-args -1 #'list) 1 2 3) => '(2 3 1) + (funcall (-rotate-args 0 #'list) 1 2 3) => '(1 2 3) + (funcall (-rotate-args 1 #'list) 1 2 3) => '(3 1 2) + (funcall (-rotate-args 2 #'list) 1 2 3) => '(2 3 1) + (funcall (-rotate-args -2 #'list) 1 2) => '(1 2) + (funcall (-rotate-args -1 #'list) 1 2) => '(2 1) + (funcall (-rotate-args 0 #'list) 1 2) => '(1 2) + (funcall (-rotate-args 1 #'list) 1 2) => '(2 1) + (funcall (-rotate-args 2 #'list) 1 2) => '(1 2) + (funcall (-rotate-args -2 #'list) 1) => '(1) + (funcall (-rotate-args -1 #'list) 1) => '(1) + (funcall (-rotate-args 0 #'list) 1) => '(1) + (funcall (-rotate-args 1 #'list) 1) => '(1) + (funcall (-rotate-args 2 #'list) 1) => '(1) + (funcall (-rotate-args -2 #'list)) => '() + (funcall (-rotate-args -1 #'list)) => '() + (funcall (-rotate-args 0 #'list)) => '() + (funcall (-rotate-args 1 #'list)) => '() + (funcall (-rotate-args 2 #'list)) => '() + (let ((a (list 1 2 3))) (apply (-rotate-args 2 #'-) a) a) => '(1 2 3)) (defexamples -const (funcall (-const 2) 1 3 "foo") => 2 - (-map (-const 1) '("a" "b" "c" "d")) => '(1 1 1 1) - (-sum (-map (-const 1) '("a" "b" "c" "d"))) => 4) + (mapcar (-const 1) '("a" "b" "c" "d")) => '(1 1 1 1) + (-sum (mapcar (-const 1) '("a" "b" "c" "d"))) => 4 + (funcall (-const t)) => t + (funcall (-const nil)) => nil + (funcall (-const t) nil) => t + (funcall (-const nil) nil) => nil) (defexamples -cut (funcall (-cut list 1 <> 3 <> 5) 2 4) => '(1 2 3 4 5) @@ -1801,17 +1856,57 @@ or readability." (-filter (-cut < <> 5) '(1 3 5 7 9)) => '(1 3)) (defexamples -not - (funcall (-not 'even?) 5) => t - (-filter (-not (-partial '< 4)) '(1 2 3 4 5 6 7 8)) => '(1 2 3 4)) + (funcall (-not #'numberp) "5") => t + (-sort (-not #'<) '(5 2 1 0 6)) => '(6 5 2 1 0) + (-filter (-not (-partial #'< 4)) '(1 2 3 4 5 6 7 8)) => '(1 2 3 4) + ;; Variadic `<' was introduced in Emacs 24.4. + (funcall (-not (lambda (a b c) (and (< a b) (< b c)))) 1 2 3) => nil + (funcall (-not (lambda (a b c) (and (< a b) (< b c)))) 3 2 1) => t + (funcall (-not #'<) 1 2) => nil + (funcall (-not #'<) 2 1) => t + (funcall (-not #'+) 1) => nil + (funcall (-not #'+)) => nil) (defexamples -orfn - (-filter (-orfn 'even? (-partial (-flip '<) 5)) '(1 2 3 4 5 6 7 8 9 10)) => '(1 2 3 4 6 8 10) - (funcall (-orfn 'stringp 'even?) "foo") => t) + (-filter (-orfn #'natnump #'booleanp) '(1 nil "a" -4 b c t)) => '(1 nil t) + (funcall (-orfn #'symbolp (-cut string-match-p "x" <>)) "axe") => 1 + (funcall (-orfn #'= #'+) 1 1) => t + (funcall (-orfn #'+ #'null)) => 0 + (funcall (-orfn #'+ #'null) 1) => 1 + (funcall (-orfn #'+ #'null) 1 2) => 3 + (funcall (-orfn #'+ #'null) 1 2 3) => 6 + (funcall (-orfn #'ignore #'+)) => 0 + (funcall (-orfn #'ignore #'+) 1) => 1 + (funcall (-orfn #'ignore #'+) 1 2) => 3 + (funcall (-orfn #'ignore #'+) 1 2 3) => 6 + (-filter (-orfn #'symbolp) '(a b 1 nil t 2)) => '(a b nil t) + (-filter (-orfn #'null) '(a b 1 nil t 2)) => '(nil) + (-filter (-orfn) '(nil t)) => '() + (-orfn #'null) => #'null + (-orfn) => #'ignore) (defexamples -andfn - (funcall (-andfn (-cut < <> 10) 'even?) 6) => t - (funcall (-andfn (-cut < <> 10) 'even?) 12) => nil - (-filter (-andfn (-not 'even?) (-cut >= 5 <>)) '(1 2 3 4 5 6 7 8 9 10)) => '(1 3 5)) + (-filter (-andfn #'numberp (-cut < <> 5)) '(a 1 b 6 c 2)) => '(1 2) + (mapcar (-andfn #'numberp #'1+) '(a 1 b 6)) => '(nil 2 nil 7) + (funcall (-andfn #'= #'+) 1 1) => 2 + (funcall (-andfn #'ignore #'+)) => nil + (funcall (-andfn #'ignore #'+) 1) => nil + (funcall (-andfn #'ignore #'+) 1 2) => nil + (funcall (-andfn #'+ #'ignore)) => nil + (funcall (-andfn #'+ #'ignore) 1) => nil + (funcall (-andfn #'+ #'ignore) 1 2) => nil + (funcall (-andfn #'+ #'list)) => '() + (funcall (-andfn #'+ #'list) 1) => '(1) + (funcall (-andfn #'+ #'list) 1 2) => '(1 2) + (funcall (-andfn #'list #'+)) => nil + (funcall (-andfn #'list #'+) 1) => 1 + (funcall (-andfn #'list #'+) 1 2) => 3 + (funcall (-andfn #'* #'+)) => 0 + (funcall (-andfn #'+ #'*)) => 1 + (-andfn #'null) => #'null + (funcall (-andfn)) => t + (funcall (-andfn) nil) => t + (funcall (-andfn) t) => t) (defexamples -iteratefn (funcall (-iteratefn (lambda (x) (* x x)) 3) 2) => 256