diff --git a/README.md b/README.md index 2daa608..f987e4f 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ This is so much a work in progress that you should definitely not be using it ye * [!intersection](#intersection-list-list2) `(list list2)` * [!distinct](#distinct-list) `(list)` * [!contains?](#contains-list-element) `(list element)` +* [!some](#some-fn-list) `(fn list)` +* [!every?](#every-fn-list) `(fn list)` There are also anaphoric versions of these functions where that makes sense, prefixed with two bangs instead of one. @@ -205,6 +207,26 @@ or with `!compare-fn` if that's non-nil. (!contains? '(1 2 3) 4) ;; => nil ``` +### !some `(fn list)` + +Returns the first non-nil value of (`fn` x) for any x in `list`, else nil. + +```cl +(!some 'even? '(1 2 3)) ;; => t +(!some 'even? '(1 3 5)) ;; => nil +(!!some (= 0 (% it 2)) '(1 2 3)) ;; => t +``` + +### !every? `(fn list)` + +Returns t if (`fn` x) is non-nil for every x in `list`, else nil. + +```cl +(!every? 'even? '(1 2 3)) ;; => nil +(!every? 'even? '(2 4 6)) ;; => t +(!!every? (= 0 (% it 2)) '(2 4 6)) ;; => t +``` + ## Development diff --git a/bang.el b/bang.el index eb23382..8e01b94 100644 --- a/bang.el +++ b/bang.el @@ -168,6 +168,34 @@ or with `!compare-fn' if that's non-nil." (setq lst (cdr lst))) lst)))))) +(defmacro !!some (form list) + "Anaphoric form of `!some'." + `(let ((!--list ,list) + (!--any nil)) + (while (and !--list (not !--any)) + (let ((it (car !--list))) + (setq !--any ,form)) + (setq !--list (cdr !--list))) + !--any)) + +(defun !some (fn list) + "Returns the first non-nil value of (FN x) for any x in LIST, else nil." + (!!some (funcall fn it) list)) + +(defmacro !!every? (form list) + "Anaphoric form of `!every?'." + `(let ((!--list ,list) + (!--all t)) + (while (and !--all !--list) + (let ((it (car !--list))) + (setq !--all ,form)) + (setq !--list (cdr !--list))) + (not (null !--all)))) + +(defun !every? (fn list) + "Returns t if (FN x) is non-nil for every x in LIST, else nil." + (!!every? (funcall fn it) list)) + (defvar !compare-fn nil "Tests for equality use this function or `equal' if this is nil. It should only be set using dynamic scope with a let, like: diff --git a/examples.el b/examples.el index d25afa5..6593cc6 100644 --- a/examples.el +++ b/examples.el @@ -80,3 +80,13 @@ (!contains? '(1 2 3) 4) => nil (!contains? '() 1) => nil (!contains? '() '()) => nil) + +(defexamples !some + (!some 'even? '(1 2 3)) => t + (!some 'even? '(1 3 5)) => nil + (!!some (= 0 (% it 2)) '(1 2 3)) => t) + +(defexamples !every? + (!every? 'even? '(1 2 3)) => nil + (!every? 'even? '(2 4 6)) => t + (!!every? (= 0 (% it 2)) '(2 4 6)) => t)