pax_global_header00006660000000000000000000000064126116444730014522gustar00rootroot0000000000000052 comment=fec6f5480d0ce03ead0e6117ac77dc7e757e76f8 dash.el-2.12.1/000077500000000000000000000000001261164447300131235ustar00rootroot00000000000000dash.el-2.12.1/.gitignore000066400000000000000000000000771261164447300151170ustar00rootroot00000000000000/dash.elc /dash-functional.elc /dash-autoloads.el /dash-pkg.el dash.el-2.12.1/.travis.yml000066400000000000000000000006611261164447300152370ustar00rootroot00000000000000language: emacs-lisp before_install: # PPA for stable Emacs packages - sudo add-apt-repository -y ppa:cassou/emacs # PPA for Emacs nightlies - sudo add-apt-repository -y ppa:ubuntu-elisp/ppa # Update and install the Emacs for our environment - sudo apt-get update -qq - sudo apt-get install -qq -yy ${EMACS}-nox ${EMACS}-el env: - EMACS=emacs23 - EMACS=emacs24 - EMACS=emacs-snapshot script: ./run-travis-ci.sh dash.el-2.12.1/Cask000066400000000000000000000000311261164447300137210ustar00rootroot00000000000000(package-file "dash.el") dash.el-2.12.1/README.md000066400000000000000000002337061261164447300144150ustar00rootroot00000000000000# dash.el [![Build Status](https://secure.travis-ci.org/magnars/dash.el.png)](http://travis-ci.org/magnars/dash.el) A modern list api for Emacs. No 'cl required. ## Installation It's available on [marmalade](http://marmalade-repo.org/) and [Melpa](http://melpa.milkbox.net/): M-x package-install dash Or you can just dump `dash.el` in your load path somewhere. If you want the function combinators, then also: M-x package-install dash-functional ## Using in a package Add this to the big comment block at the top: ;; Package-Requires: ((dash "2.12.1")) To get function combinators: ;; Package-Requires: ((dash "2.12.1") (dash-functional "1.2.0") (emacs "24")) ## Upcoming breaking change! - For backward compatibility reasons `-zip` return a cons-cell instead of a list with two elements when called on two lists. This is a clunky API, and in an upcoming 3.0 release of Dash it will always return a list. If you rely on the cons-cell return value, use `-zip-pair` instead. ## Syntax highlighting of dash functions Font lock of dash functions in emacs lisp buffers is now optional. Include this in your emacs settings to get syntax highlighting: (eval-after-load "dash" '(dash-enable-font-lock)) ## Functions All functions and constructs in the library are prefixed with a dash (-). There are also anaphoric versions of functions where that makes sense, prefixed with two dashes instead of one. While `-map` takes a function to map over the list, you can also use the anaphoric form with double dashes - which will then be executed with `it` exposed as the list item. Here's an example: ```el (-map (lambda (n) (* n n)) '(1 2 3 4)) ;; normal version (--map (* it it) '(1 2 3 4)) ;; anaphoric version ``` of course the original can also be written like ```el (defun square (n) (* n n)) (-map 'square '(1 2 3 4)) ``` which demonstrates the usefulness of both versions. ### Maps Functions in this category take a transforming function, which is then applied sequentially to each or selected elements of the input list. The results are collected in order and returned as new list. * [-map](#-map-fn-list) `(fn list)` * [-map-when](#-map-when-pred-rep-list) `(pred rep list)` * [-map-first](#-map-first-pred-rep-list) `(pred rep list)` * [-map-last](#-map-last-pred-rep-list) `(pred rep list)` * [-map-indexed](#-map-indexed-fn-list) `(fn list)` * [-annotate](#-annotate-fn-list) `(fn list)` * [-splice](#-splice-pred-fun-list) `(pred fun list)` * [-splice-list](#-splice-list-pred-new-list-list) `(pred new-list list)` * [-mapcat](#-mapcat-fn-list) `(fn list)` * [-copy](#-copy-arg) `(arg)` ### Sublist selection Functions returning a sublist of the original list. * [-filter](#-filter-pred-list) `(pred list)` * [-remove](#-remove-pred-list) `(pred list)` * [-remove-first](#-remove-first-pred-list) `(pred list)` * [-remove-last](#-remove-last-pred-list) `(pred list)` * [-remove-item](#-remove-item-item-list) `(item list)` * [-non-nil](#-non-nil-list) `(list)` * [-slice](#-slice-list-from-optional-to-step) `(list from &optional to step)` * [-take](#-take-n-list) `(n list)` * [-drop](#-drop-n-list) `(n list)` * [-take-while](#-take-while-pred-list) `(pred list)` * [-drop-while](#-drop-while-pred-list) `(pred list)` * [-select-by-indices](#-select-by-indices-indices-list) `(indices list)` ### List to list Bag of various functions which modify input list. * [-keep](#-keep-fn-list) `(fn list)` * [-concat](#-concat-rest-lists) `(&rest lists)` * [-flatten](#-flatten-l) `(l)` * [-flatten-n](#-flatten-n-num-list) `(num list)` * [-replace](#-replace-old-new-list) `(old new list)` * [-replace-first](#-replace-first-old-new-list) `(old new list)` * [-replace-last](#-replace-last-old-new-list) `(old new list)` * [-insert-at](#-insert-at-n-x-list) `(n x list)` * [-replace-at](#-replace-at-n-x-list) `(n x list)` * [-update-at](#-update-at-n-func-list) `(n func list)` * [-remove-at](#-remove-at-n-list) `(n list)` * [-remove-at-indices](#-remove-at-indices-indices-list) `(indices list)` ### Reductions Functions reducing lists into single value. * [-reduce-from](#-reduce-from-fn-initial-value-list) `(fn initial-value list)` * [-reduce-r-from](#-reduce-r-from-fn-initial-value-list) `(fn initial-value list)` * [-reduce](#-reduce-fn-list) `(fn list)` * [-reduce-r](#-reduce-r-fn-list) `(fn list)` * [-count](#-count-pred-list) `(pred list)` * [-sum](#-sum-list) `(list)` * [-product](#-product-list) `(list)` * [-min](#-min-list) `(list)` * [-min-by](#-min-by-comparator-list) `(comparator list)` * [-max](#-max-list) `(list)` * [-max-by](#-max-by-comparator-list) `(comparator list)` ### Unfolding Operations dual to reductions, building lists from seed value rather than consuming a list to produce a single value. * [-iterate](#-iterate-fun-init-n) `(fun init n)` * [-unfold](#-unfold-fun-seed) `(fun seed)` ### Predicates * [-any?](#-any-pred-list) `(pred list)` * [-all?](#-all-pred-list) `(pred list)` * [-none?](#-none-pred-list) `(pred list)` * [-only-some?](#-only-some-pred-list) `(pred list)` * [-contains?](#-contains-list-element) `(list element)` * [-same-items?](#-same-items-list-list2) `(list list2)` * [-is-prefix?](#-is-prefix-prefix-list) `(prefix list)` * [-is-suffix?](#-is-suffix-suffix-list) `(suffix list)` * [-is-infix?](#-is-infix-infix-list) `(infix list)` ### Partitioning Functions partitioning the input list into a list of lists. * [-split-at](#-split-at-n-list) `(n list)` * [-split-with](#-split-with-pred-list) `(pred list)` * [-split-on](#-split-on-item-list) `(item list)` * [-split-when](#-split-when-fn-list) `(fn list)` * [-separate](#-separate-pred-list) `(pred list)` * [-partition](#-partition-n-list) `(n list)` * [-partition-all](#-partition-all-n-list) `(n list)` * [-partition-in-steps](#-partition-in-steps-n-step-list) `(n step list)` * [-partition-all-in-steps](#-partition-all-in-steps-n-step-list) `(n step list)` * [-partition-by](#-partition-by-fn-list) `(fn list)` * [-partition-by-header](#-partition-by-header-fn-list) `(fn list)` * [-group-by](#-group-by-fn-list) `(fn list)` ### Indexing Return indices of elements based on predicates, sort elements by indices etc. * [-elem-index](#-elem-index-elem-list) `(elem list)` * [-elem-indices](#-elem-indices-elem-list) `(elem list)` * [-find-index](#-find-index-pred-list) `(pred list)` * [-find-last-index](#-find-last-index-pred-list) `(pred list)` * [-find-indices](#-find-indices-pred-list) `(pred list)` * [-grade-up](#-grade-up-comparator-list) `(comparator list)` * [-grade-down](#-grade-down-comparator-list) `(comparator list)` ### Set operations Operations pretending lists are sets. * [-union](#-union-list-list2) `(list list2)` * [-difference](#-difference-list-list2) `(list list2)` * [-intersection](#-intersection-list-list2) `(list list2)` * [-distinct](#-distinct-list) `(list)` ### Other list operations Other list functions not fit to be classified elsewhere. * [-rotate](#-rotate-n-list) `(n list)` * [-repeat](#-repeat-n-x) `(n x)` * [-cons*](#-cons-rest-args) `(&rest args)` * [-snoc](#-snoc-list-elem-rest-elements) `(list elem &rest elements)` * [-interpose](#-interpose-sep-list) `(sep list)` * [-interleave](#-interleave-rest-lists) `(&rest lists)` * [-zip-with](#-zip-with-fn-list1-list2) `(fn list1 list2)` * [-zip](#-zip-rest-lists) `(&rest lists)` * [-zip-fill](#-zip-fill-fill-value-rest-lists) `(fill-value &rest lists)` * [-cycle](#-cycle-list) `(list)` * [-pad](#-pad-fill-value-rest-lists) `(fill-value &rest lists)` * [-table](#-table-fn-rest-lists) `(fn &rest lists)` * [-table-flat](#-table-flat-fn-rest-lists) `(fn &rest lists)` * [-first](#-first-pred-list) `(pred list)` * [-some](#-some-pred-list) `(pred list)` * [-last](#-last-pred-list) `(pred list)` * [-first-item](#-first-item-list) `(list)` * [-last-item](#-last-item-list) `(list)` * [-butlast](#-butlast-list) `(list)` * [-sort](#-sort-comparator-list) `(comparator list)` * [-list](#-list-rest-args) `(&rest args)` * [-fix](#-fix-fn-list) `(fn list)` ### Tree operations Functions pretending lists are trees. * [-tree-seq](#-tree-seq-branch-children-tree) `(branch children tree)` * [-tree-map](#-tree-map-fn-tree) `(fn tree)` * [-tree-map-nodes](#-tree-map-nodes-pred-fun-tree) `(pred fun tree)` * [-tree-reduce](#-tree-reduce-fn-tree) `(fn tree)` * [-tree-reduce-from](#-tree-reduce-from-fn-init-value-tree) `(fn init-value tree)` * [-tree-mapreduce](#-tree-mapreduce-fn-folder-tree) `(fn folder tree)` * [-tree-mapreduce-from](#-tree-mapreduce-from-fn-folder-init-value-tree) `(fn folder init-value tree)` * [-clone](#-clone-list) `(list)` ### Threading macros * [->](#--x-optional-form-rest-more) `(x &optional form &rest more)` * [->>](#--x-optional-form-rest-more) `(x &optional form &rest more)` * [-->](#---x-form-rest-more) `(x form &rest more)` * [-some->](#-some--x-optional-form-rest-more) `(x &optional form &rest more)` * [-some->>](#-some--x-optional-form-rest-more) `(x &optional form &rest more)` * [-some-->](#-some---x-optional-form-rest-more) `(x &optional form &rest more)` ### Binding Convenient versions of `let` and `let*` constructs combined with flow control. * [-when-let](#-when-let-var-val-rest-body) `(var-val &rest body)` * [-when-let*](#-when-let-vars-vals-rest-body) `(vars-vals &rest body)` * [-if-let](#-if-let-var-val-then-rest-else) `(var-val then &rest else)` * [-if-let*](#-if-let-vars-vals-then-rest-else) `(vars-vals then &rest else)` * [-let](#-let-varlist-rest-body) `(varlist &rest body)` * [-let*](#-let-varlist-rest-body) `(varlist &rest body)` * [-lambda](#-lambda-match-form-rest-body) `(match-form &rest body)` ### Side-effects Functions iterating over lists for side-effect only. * [-each](#-each-list-fn) `(list fn)` * [-each-while](#-each-while-list-pred-fn) `(list pred fn)` * [-dotimes](#-dotimes-num-fn) `(num fn)` ### Destructive operations * [!cons](#cons-car-cdr) `(car cdr)` * [!cdr](#cdr-list) `(list)` ### Function combinators These combinators require Emacs 24 for its lexical scope. So they are offered in a separate package: `dash-functional`. * [-partial](#-partial-fn-rest-args) `(fn &rest args)` * [-rpartial](#-rpartial-fn-rest-args) `(fn &rest args)` * [-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)` * [-const](#-const-c) `(c)` * [-cut](#-cut-rest-params) `(&rest params)` * [-not](#-not-pred) `(pred)` * [-orfn](#-orfn-rest-preds) `(&rest preds)` * [-andfn](#-andfn-rest-preds) `(&rest preds)` * [-iteratefn](#-iteratefn-fn-n) `(fn n)` * [-fixfn](#-fixfn-fn-optional-equal-test-halt-test) `(fn &optional equal-test halt-test)` * [-prodfn](#-prodfn-rest-fns) `(&rest fns)` ## Maps Functions in this category take a transforming function, which is then applied sequentially to each or selected elements of the input list. The results are collected in order and returned as new list. #### -map `(fn list)` Return a new list consisting of the result of applying `fn` to the items in `list`. ```el (-map (lambda (num) (* num num)) '(1 2 3 4)) ;; => '(1 4 9 16) (-map 'square '(1 2 3 4)) ;; => '(1 4 9 16) (--map (* it it) '(1 2 3 4)) ;; => '(1 4 9 16) ``` #### -map-when `(pred rep list)` Return a new list where the elements in `list` that does not match the `pred` function are unchanged, and where the elements in `list` that do match the `pred` function are mapped through the `rep` function. Alias: `-replace-where` See also: [`-update-at`](#-update-at-n-func-list) ```el (-map-when 'even? 'square '(1 2 3 4)) ;; => '(1 4 3 16) (--map-when (> it 2) (* it it) '(1 2 3 4)) ;; => '(1 2 9 16) (--map-when (= it 2) 17 '(1 2 3 4)) ;; => '(1 17 3 4) ``` #### -map-first `(pred rep list)` Replace first item in `list` satisfying `pred` with result of `rep` called on this item. See also: [`-map-when`](#-map-when-pred-rep-list), [`-replace-first`](#-replace-first-old-new-list) ```el (-map-first 'even? 'square '(1 2 3 4)) ;; => '(1 4 3 4) (--map-first (> it 2) (* it it) '(1 2 3 4)) ;; => '(1 2 9 4) (--map-first (= it 2) 17 '(1 2 3 2)) ;; => '(1 17 3 2) ``` #### -map-last `(pred rep list)` Replace first item in `list` satisfying `pred` with result of `rep` called on this item. See also: [`-map-when`](#-map-when-pred-rep-list), [`-replace-last`](#-replace-last-old-new-list) ```el (-map-last 'even? 'square '(1 2 3 4)) ;; => '(1 2 3 16) (--map-last (> it 2) (* it it) '(1 2 3 4)) ;; => '(1 2 3 16) (--map-last (= it 2) 17 '(1 2 3 2)) ;; => '(1 2 3 17) ``` #### -map-indexed `(fn list)` Return a new list consisting of the result of (`fn` index item) for each item in `list`. In the anaphoric form `--map-indexed`, the index is exposed as `it-index`. ```el (-map-indexed (lambda (index item) (- item index)) '(1 2 3 4)) ;; => '(1 1 1 1) (--map-indexed (- it it-index) '(1 2 3 4)) ;; => '(1 1 1 1) ``` #### -annotate `(fn list)` Return a list of cons cells where each cell is `fn` applied to each element of `list` paired with the unmodified element of `list`. ```el (-annotate '1+ '(1 2 3)) ;; => '((2 . 1) (3 . 2) (4 . 3)) (-annotate 'length '(("h" "e" "l" "l" "o") ("hello" "world"))) ;; => '((5 "h" "e" "l" "l" "o") (2 "hello" "world")) (--annotate (< 1 it) '(0 1 2 3)) ;; => '((nil . 0) (nil . 1) (t . 2) (t . 3)) ``` #### -splice `(pred fun list)` Splice lists generated by `fun` in place of elements matching `pred` in `list`. `fun` takes the element matching `pred` as input. This function can be used as replacement for `,@` in case you need to splice several lists at marked positions (for example with keywords). See also: [`-splice-list`](#-splice-list-pred-new-list-list), [`-insert-at`](#-insert-at-n-x-list) ```el (-splice 'even? (lambda (x) (list x x)) '(1 2 3 4)) ;; => '(1 2 2 3 4 4) (--splice 't (list it it) '(1 2 3 4)) ;; => '(1 1 2 2 3 3 4 4) (--splice (equal it :magic) '((list of) (magical) (code)) '((foo) (bar) :magic (baz))) ;; => '((foo) (bar) (list of) (magical) (code) (baz)) ``` #### -splice-list `(pred new-list list)` Splice `new-list` in place of elements matching `pred` in `list`. See also: [`-splice`](#-splice-pred-fun-list), [`-insert-at`](#-insert-at-n-x-list) ```el (-splice-list 'keywordp '(a b c) '(1 :foo 2)) ;; => '(1 a b c 2) (-splice-list 'keywordp nil '(1 :foo 2)) ;; => '(1 2) (--splice-list (keywordp it) '(a b c) '(1 :foo 2)) ;; => '(1 a b c 2) ``` #### -mapcat `(fn list)` Return the concatenation of the result of mapping `fn` over `list`. Thus function `fn` should return a list. ```el (-mapcat 'list '(1 2 3)) ;; => '(1 2 3) (-mapcat (lambda (item) (list 0 item)) '(1 2 3)) ;; => '(0 1 0 2 0 3) (--mapcat (list 0 it) '(1 2 3)) ;; => '(0 1 0 2 0 3) ``` #### -copy `(arg)` Create a shallow copy of `list`. ```el (-copy '(1 2 3)) ;; => '(1 2 3) (let ((a '(1 2 3))) (eq a (-copy a))) ;; => nil ``` ## Sublist selection Functions returning a sublist of the original list. #### -filter `(pred list)` Return a new list of the items in `list` for which `pred` returns a non-nil value. Alias: `-select` ```el (-filter (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) ;; => '(2 4) (-filter 'even? '(1 2 3 4)) ;; => '(2 4) (--filter (= 0 (% it 2)) '(1 2 3 4)) ;; => '(2 4) ``` #### -remove `(pred list)` Return a new list of the items in `list` for which `pred` returns nil. Alias: `-reject` ```el (-remove (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) ;; => '(1 3) (-remove 'even? '(1 2 3 4)) ;; => '(1 3) (--remove (= 0 (% it 2)) '(1 2 3 4)) ;; => '(1 3) ``` #### -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-last `(pred list)` Return a new list with the last item matching `pred` removed. Alias: `-reject-last` See also: [`-remove`](#-remove-pred-list), [`-map-last`](#-map-last-pred-rep-list) ```el (-remove-last 'even? '(1 3 5 4 7 8 10 11)) ;; => '(1 3 5 4 7 8 11) (-remove-last 'stringp '(1 2 "last" "second" "third")) ;; => '(1 2 "last" "second") (--remove-last (> it 3) '(1 2 3 4 5 6 7 8 9 10)) ;; => '(1 2 3 4 5 6 7 8 9) ``` #### -remove-item `(item list)` Remove all occurences of `item` from `list`. Comparison is done with `equal`. ```el (-remove-item 3 '(1 2 3 2 3 4 5 3)) ;; => '(1 2 2 4 5) (-remove-item 'foo '(foo bar baz foo)) ;; => '(bar baz) (-remove-item "bob" '("alice" "bob" "eve" "bob" "dave")) ;; => '("alice" "eve" "dave") ``` #### -non-nil `(list)` Return all non-nil elements of `list`. ```el (-non-nil '(1 nil 2 nil nil 3 4 nil 5 nil)) ;; => '(1 2 3 4 5) ``` #### -slice `(list from &optional to step)` Return copy of `list`, starting from index `from` to index `to`. `from` or `to` may be negative. These values are then interpreted modulo the length of the list. If `step` is a number, only each STEPth item in the resulting section is returned. Defaults to 1. ```el (-slice '(1 2 3 4 5) 1) ;; => '(2 3 4 5) (-slice '(1 2 3 4 5) 0 3) ;; => '(1 2 3) (-slice '(1 2 3 4 5 6 7 8 9) 1 -1 2) ;; => '(2 4 6 8) ``` #### -take `(n list)` Return a new list of the first `n` items in `list`, or all items if there are fewer than `n`. ```el (-take 3 '(1 2 3 4 5)) ;; => '(1 2 3) (-take 17 '(1 2 3 4 5)) ;; => '(1 2 3 4 5) ``` #### -drop `(n list)` Return the tail of `list` without the first `n` items. ```el (-drop 3 '(1 2 3 4 5)) ;; => '(4 5) (-drop 17 '(1 2 3 4 5)) ;; => '() ``` #### -take-while `(pred list)` Return a new list of successive items from `list` while (`pred` item) returns a non-nil value. ```el (-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) ``` #### -drop-while `(pred list)` Return the tail of `list` starting from the first item for which (`pred` item) returns nil. ```el (-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) ``` #### -select-by-indices `(indices list)` Return a list whose elements are elements from `list` selected as `(nth i list)` for all i from `indices`. ```el (-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") (-select-by-indices '(2 1 0) '("a" "b" "c")) ;; => '("c" "b" "a") (-select-by-indices '(0 1 2 0 1 3 3 1) '("f" "a" "r" "l")) ;; => '("f" "a" "r" "f" "a" "l" "l" "a") ``` ## List to list Bag of various functions which modify input list. #### -keep `(fn list)` Return a new list of the non-nil results of applying `fn` to the items in `list`. If you want to select the original items satisfying a predicate use [`-filter`](#-filter-pred-list). ```el (-keep 'cdr '((1 2 3) (4 5) (6))) ;; => '((2 3) (5)) (-keep (lambda (num) (when (> num 3) (* 10 num))) '(1 2 3 4 5 6)) ;; => '(40 50 60) (--keep (when (> it 3) (* 10 it)) '(1 2 3 4 5 6)) ;; => '(40 50 60) ``` #### -concat `(&rest lists)` Return a new list with the concatenation of the elements in the supplied `lists`. ```el (-concat '(1)) ;; => '(1) (-concat '(1) '(2)) ;; => '(1 2) (-concat '(1) '(2 3) '(4)) ;; => '(1 2 3 4) ``` #### -flatten `(l)` Take a nested list `l` and return its contents as a single, flat list. Note that because `nil` represents a list of zero elements (an empty list), any mention of nil in `l` will disappear after flattening. If you need to preserve nils, consider [`-flatten-n`](#-flatten-n-num-list) or map them to some unique symbol and then map them back. Conses of two atoms are considered "terminals", that is, they aren't flattened further. See also: [`-flatten-n`](#-flatten-n-num-list) ```el (-flatten '((1))) ;; => '(1) (-flatten '((1 (2 3) (((4 (5))))))) ;; => '(1 2 3 4 5) (-flatten '(1 2 (3 . 4))) ;; => '(1 2 (3 . 4)) ``` #### -flatten-n `(num list)` Flatten `num` levels of a nested `list`. See also: [`-flatten`](#-flatten-l) ```el (-flatten-n 1 '((1 2) ((3 4) ((5 6))))) ;; => '(1 2 (3 4) ((5 6))) (-flatten-n 2 '((1 2) ((3 4) ((5 6))))) ;; => '(1 2 3 4 (5 6)) (-flatten-n 3 '((1 2) ((3 4) ((5 6))))) ;; => '(1 2 3 4 5 6) ``` #### -replace `(old new list)` Replace all `old` items in `list` with `new`. Elements are compared using `equal`. See also: [`-replace-at`](#-replace-at-n-x-list) ```el (-replace 1 "1" '(1 2 3 4 3 2 1)) ;; => '("1" 2 3 4 3 2 "1") (-replace "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) ;; => '("a" "nice" "bar" "sentence" "about" "bar") (-replace 1 2 nil) ;; => nil ``` #### -replace-first `(old new list)` Replace the first occurence of `old` with `new` in `list`. Elements are compared using `equal`. See also: [`-map-first`](#-map-first-pred-rep-list) ```el (-replace-first 1 "1" '(1 2 3 4 3 2 1)) ;; => '("1" 2 3 4 3 2 1) (-replace-first "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) ;; => '("a" "nice" "bar" "sentence" "about" "foo") (-replace-first 1 2 nil) ;; => nil ``` #### -replace-last `(old new list)` Replace the last occurence of `old` with `new` in `list`. Elements are compared using `equal`. See also: [`-map-last`](#-map-last-pred-rep-list) ```el (-replace-last 1 "1" '(1 2 3 4 3 2 1)) ;; => '(1 2 3 4 3 2 "1") (-replace-last "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) ;; => '("a" "nice" "foo" "sentence" "about" "bar") (-replace-last 1 2 nil) ;; => nil ``` #### -insert-at `(n x list)` Return a list with `x` inserted into `list` at position `n`. See also: [`-splice`](#-splice-pred-fun-list), [`-splice-list`](#-splice-list-pred-new-list-list) ```el (-insert-at 1 'x '(a b c)) ;; => '(a x b c) (-insert-at 12 'x '(a b c)) ;; => '(a b c x) ``` #### -replace-at `(n x list)` Return a list with element at Nth position in `list` replaced with `x`. See also: [`-replace`](#-replace-old-new-list) ```el (-replace-at 0 9 '(0 1 2 3 4 5)) ;; => '(9 1 2 3 4 5) (-replace-at 1 9 '(0 1 2 3 4 5)) ;; => '(0 9 2 3 4 5) (-replace-at 4 9 '(0 1 2 3 4 5)) ;; => '(0 1 2 3 9 5) ``` #### -update-at `(n func list)` Return a list with element at Nth position in `list` replaced with `(func (nth n list))`. See also: [`-map-when`](#-map-when-pred-rep-list) ```el (-update-at 0 (lambda (x) (+ x 9)) '(0 1 2 3 4 5)) ;; => '(9 1 2 3 4 5) (-update-at 1 (lambda (x) (+ x 8)) '(0 1 2 3 4 5)) ;; => '(0 9 2 3 4 5) (--update-at 2 (length it) '("foo" "bar" "baz" "quux")) ;; => '("foo" "bar" 3 "quux") ``` #### -remove-at `(n list)` Return a list with element at Nth position in `list` removed. See also: [`-remove-at-indices`](#-remove-at-indices-indices-list), [`-remove`](#-remove-pred-list) ```el (-remove-at 0 '("0" "1" "2" "3" "4" "5")) ;; => '("1" "2" "3" "4" "5") (-remove-at 1 '("0" "1" "2" "3" "4" "5")) ;; => '("0" "2" "3" "4" "5") (-remove-at 2 '("0" "1" "2" "3" "4" "5")) ;; => '("0" "1" "3" "4" "5") ``` #### -remove-at-indices `(indices list)` Return a list whose elements are elements from `list` without elements selected as `(nth i list)` for all i from `indices`. See also: [`-remove-at`](#-remove-at-n-list), [`-remove`](#-remove-pred-list) ```el (-remove-at-indices '(0) '("0" "1" "2" "3" "4" "5")) ;; => '("1" "2" "3" "4" "5") (-remove-at-indices '(0 2 4) '("0" "1" "2" "3" "4" "5")) ;; => '("1" "3" "5") (-remove-at-indices '(0 5) '("0" "1" "2" "3" "4" "5")) ;; => '("1" "2" "3" "4") ``` ## Reductions Functions reducing lists into single value. #### -reduce-from `(fn initial-value list)` Return the result of applying `fn` to `initial-value` and the first item in `list`, then applying `fn` to that result and the 2nd item, etc. If `list` contains no items, return `initial-value` and `fn` is not called. In the anaphoric form `--reduce-from`, the accumulated value is exposed as `acc`. See also: [`-reduce`](#-reduce-fn-list), [`-reduce-r`](#-reduce-r-fn-list) ```el (-reduce-from '- 10 '(1 2 3)) ;; => 4 (-reduce-from (lambda (memo item) (concat "(" memo " - " (int-to-string item) ")")) "10" '(1 2 3)) ;; => "(((10 - 1) - 2) - 3)" (--reduce-from (concat acc " " it) "START" '("a" "b" "c")) ;; => "START a b c" ``` #### -reduce-r-from `(fn initial-value list)` Replace conses with `fn`, nil with `initial-value` and evaluate the resulting expression. If `list` is empty, `initial-value` is returned and `fn` is not called. Note: this function works the same as [`-reduce-from`](#-reduce-from-fn-initial-value-list) but the operation associates from right instead of from left. See also: [`-reduce-r`](#-reduce-r-fn-list), [`-reduce`](#-reduce-fn-list) ```el (-reduce-r-from '- 10 '(1 2 3)) ;; => -8 (-reduce-r-from (lambda (item memo) (concat "(" (int-to-string item) " - " memo ")")) "10" '(1 2 3)) ;; => "(1 - (2 - (3 - 10)))" (--reduce-r-from (concat it " " acc) "END" '("a" "b" "c")) ;; => "a b c END" ``` #### -reduce `(fn list)` Return the result of applying `fn` to the first 2 items in `list`, then applying `fn` to that result and the 3rd item, etc. If `list` contains no items, `fn` must accept no arguments as well, and reduce return the result of calling `fn` with no arguments. If `list` has only 1 item, it is returned and `fn` is not called. In the anaphoric form `--reduce`, the accumulated value is exposed as `acc`. See also: [`-reduce-from`](#-reduce-from-fn-initial-value-list), [`-reduce-r`](#-reduce-r-fn-list) ```el (-reduce '- '(1 2 3 4)) ;; => -8 (-reduce (lambda (memo item) (format "%s-%s" memo item)) '(1 2 3)) ;; => "1-2-3" (--reduce (format "%s-%s" acc it) '(1 2 3)) ;; => "1-2-3" ``` #### -reduce-r `(fn list)` Replace conses with `fn` and evaluate the resulting expression. The final nil is ignored. If `list` contains no items, `fn` must accept no arguments as well, and reduce return the result of calling `fn` with no arguments. If `list` has only 1 item, it is returned and `fn` is not called. The first argument of `fn` is the new item, the second is the accumulated value. Note: this function works the same as [`-reduce`](#-reduce-fn-list) but the operation associates from right instead of from left. See also: [`-reduce-r-from`](#-reduce-r-from-fn-initial-value-list), [`-reduce`](#-reduce-fn-list) ```el (-reduce-r '- '(1 2 3 4)) ;; => -2 (-reduce-r (lambda (item memo) (format "%s-%s" memo item)) '(1 2 3)) ;; => "3-2-1" (--reduce-r (format "%s-%s" acc it) '(1 2 3)) ;; => "3-2-1" ``` #### -count `(pred list)` Counts the number of items in `list` where (`pred` item) is non-nil. ```el (-count 'even? '(1 2 3 4 5)) ;; => 2 (--count (< it 4) '(1 2 3 4)) ;; => 3 ``` #### -sum `(list)` Return the sum of `list`. ```el (-sum '()) ;; => 0 (-sum '(1)) ;; => 1 (-sum '(1 2 3 4)) ;; => 10 ``` #### -product `(list)` Return the product of `list`. ```el (-product '()) ;; => 1 (-product '(1)) ;; => 1 (-product '(1 2 3 4)) ;; => 24 ``` #### -min `(list)` Return the smallest value from `list` of numbers or markers. ```el (-min '(0)) ;; => 0 (-min '(3 2 1)) ;; => 1 (-min '(1 2 3)) ;; => 1 ``` #### -min-by `(comparator list)` 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 comparing them. ```el (-min-by '> '(4 3 6 1)) ;; => 1 (--min-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) ;; => '(1 2 3) (--min-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) ;; => '(2) ``` #### -max `(list)` Return the largest value from `list` of numbers or markers. ```el (-max '(0)) ;; => 0 (-max '(3 2 1)) ;; => 3 (-max '(1 2 3)) ;; => 3 ``` #### -max-by `(comparator list)` 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 comparing them. ```el (-max-by '> '(4 3 6 1)) ;; => 6 (--max-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) ;; => '(3 2) (--max-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) ;; => '(1 2 3) ``` ## Unfolding Operations dual to reductions, building lists from seed value rather than consuming a list to produce a single value. #### -iterate `(fun init n)` Return a list of iterated applications of `fun` to `init`. This means a list of form: (init (fun init) (fun (fun init)) ...) `n` is the length of the returned list. ```el (-iterate '1+ 1 10) ;; => '(1 2 3 4 5 6 7 8 9 10) (-iterate (lambda (x) (+ x x)) 2 5) ;; => '(2 4 8 16 32) (--iterate (* it it) 2 5) ;; => '(2 4 16 256 65536) ``` #### -unfold `(fun seed)` Build a list from `seed` using `fun`. This is "dual" operation to [`-reduce-r`](#-reduce-r-fn-list): while -reduce-r consumes a list to produce a single value, [`-unfold`](#-unfold-fun-seed) takes a seed value and builds a (potentially infinite!) list. `fun` should return `nil` to stop the generating process, or a cons (`a` . `b`), where `a` will be prepended to the result and `b` is the new seed. ```el (-unfold (lambda (x) (unless (= x 0) (cons x (1- x)))) 10) ;; => '(10 9 8 7 6 5 4 3 2 1) (--unfold (when it (cons it (cdr it))) '(1 2 3 4)) ;; => '((1 2 3 4) (2 3 4) (3 4) (4)) (--unfold (when it (cons it (butlast it))) '(1 2 3 4)) ;; => '((1 2 3 4) (1 2 3) (1 2) (1)) ``` ## Predicates #### -any? `(pred list)` Return t if (`pred` x) is non-nil for any x in `list`, else nil. Alias: `-any-p`, `-some?`, `-some-p` ```el (-any? 'even? '(1 2 3)) ;; => t (-any? 'even? '(1 3 5)) ;; => nil (--any? (= 0 (% it 2)) '(1 2 3)) ;; => t ``` #### -all? `(pred list)` Return t if (`pred` x) is non-nil for all x in `list`, else nil. Alias: `-all-p`, `-every?`, `-every-p` ```el (-all? 'even? '(1 2 3)) ;; => nil (-all? 'even? '(2 4 6)) ;; => t (--all? (= 0 (% it 2)) '(2 4 6)) ;; => t ``` #### -none? `(pred list)` Return t if (`pred` x) is nil for all x in `list`, else nil. Alias: `-none-p` ```el (-none? 'even? '(1 2 3)) ;; => nil (-none? 'even? '(1 3 5)) ;; => t (--none? (= 0 (% it 2)) '(1 2 3)) ;; => nil ``` #### -only-some? `(pred list)` Return `t` if at least one item of `list` matches `pred` and at least one item of `list` does not match `pred`. Return `nil` both if all items match the predicate or if none of the items match the predicate. Alias: `-only-some-p` ```el (-only-some? 'even? '(1 2 3)) ;; => t (-only-some? 'even? '(1 3 5)) ;; => nil (-only-some? 'even? '(2 4 6)) ;; => nil ``` #### -contains? `(list element)` Return non-nil if `list` contains `element`. The test for equality is done with `equal`, or with `-compare-fn` if that's non-nil. Alias: `-contains-p` ```el (-contains? '(1 2 3) 1) ;; => t (-contains? '(1 2 3) 2) ;; => t (-contains? '(1 2 3) 4) ;; => nil ``` #### -same-items? `(list list2)` Return true if `list` and `list2` has the same items. The order of the elements in the lists does not matter. Alias: `-same-items-p` ```el (-same-items? '(1 2 3) '(1 2 3)) ;; => t (-same-items? '(1 2 3) '(3 2 1)) ;; => t (-same-items? '(1 2 3) '(1 2 3 4)) ;; => nil ``` #### -is-prefix? `(prefix list)` Return non-nil if `prefix` is prefix of `list`. Alias: `-is-prefix-p` ```el (-is-prefix? '(1 2 3) '(1 2 3 4 5)) ;; => t (-is-prefix? '(1 2 3 4 5) '(1 2 3)) ;; => nil (-is-prefix? '(1 3) '(1 2 3 4 5)) ;; => nil ``` #### -is-suffix? `(suffix list)` Return non-nil if `suffix` is suffix of `list`. Alias: `-is-suffix-p` ```el (-is-suffix? '(3 4 5) '(1 2 3 4 5)) ;; => t (-is-suffix? '(1 2 3 4 5) '(3 4 5)) ;; => nil (-is-suffix? '(3 5) '(1 2 3 4 5)) ;; => nil ``` #### -is-infix? `(infix list)` Return non-nil if `infix` is infix of `list`. This operation runs in `o`(n^2) time Alias: `-is-infix-p` ```el (-is-infix? '(1 2 3) '(1 2 3 4 5)) ;; => t (-is-infix? '(2 3 4) '(1 2 3 4 5)) ;; => t (-is-infix? '(3 4 5) '(1 2 3 4 5)) ;; => t ``` ## Partitioning 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. ```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-with `(pred list)` Return a list of ((-take-while `pred` `list`) (-drop-while `pred` `list`)), in no more than one pass through the list. ```el (-split-with 'even? '(1 2 3 4)) ;; => '(nil (1 2 3 4)) (-split-with 'even? '(2 4 5 6)) ;; => '((2 4) (5 6)) (--split-with (< it 4) '(1 2 3 4 3 2 1)) ;; => '((1 2 3) (4 3 2 1)) ``` #### -split-on `(item list)` Split the `list` each time `item` is found. Unlike [`-partition-by`](#-partition-by-fn-list), the `item` is discarded from the results. Empty lists are also removed from the result. Comparison is done by `equal`. See also [`-split-when`](#-split-when-fn-list) ```el (-split-on '| '(Nil | Leaf a | Node [Tree a])) ;; => '((Nil) (Leaf a) (Node [Tree a])) (-split-on ':endgroup '("a" "b" :endgroup "c" :endgroup "d" "e")) ;; => '(("a" "b") ("c") ("d" "e")) (-split-on ':endgroup '("a" "b" :endgroup :endgroup "d" "e")) ;; => '(("a" "b") ("d" "e")) ``` #### -split-when `(fn list)` Split the `list` on each element where `fn` returns non-nil. Unlike [`-partition-by`](#-partition-by-fn-list), the "matched" element is discarded from the results. Empty lists are also removed from the result. This function can be thought of as a generalization of `split-string`. ```el (-split-when 'even? '(1 2 3 4 5 6)) ;; => '((1) (3) (5)) (-split-when 'even? '(1 2 3 4 6 8 9)) ;; => '((1) (3) (9)) (--split-when (memq it '(&optional &rest)) '(a b &optional c d &rest args)) ;; => '((a b) (c d) (args)) ``` #### -separate `(pred list)` Return a list of ((-filter `pred` `list`) (-remove `pred` `list`)), in one pass through the list. ```el (-separate (lambda (num) (= 0 (% num 2))) '(1 2 3 4 5 6 7)) ;; => '((2 4 6) (1 3 5 7)) (--separate (< it 5) '(3 7 5 9 3 2 1 4 6)) ;; => '((3 3 2 1 4) (7 5 9 6)) (-separate 'cdr '((1 2) (1) (1 2 3) (4))) ;; => '(((1 2) (1 2 3)) ((1) (4))) ``` #### -partition `(n list)` Return a new list with the items in `list` grouped into `n-`sized sublists. If there are not enough items to make the last group `n-`sized, those items are discarded. ```el (-partition 2 '(1 2 3 4 5 6)) ;; => '((1 2) (3 4) (5 6)) (-partition 2 '(1 2 3 4 5 6 7)) ;; => '((1 2) (3 4) (5 6)) (-partition 3 '(1 2 3 4 5 6 7)) ;; => '((1 2 3) (4 5 6)) ``` #### -partition-all `(n list)` Return a new list with the items in `list` grouped into `n-`sized sublists. The last group may contain less than `n` items. ```el (-partition-all 2 '(1 2 3 4 5 6)) ;; => '((1 2) (3 4) (5 6)) (-partition-all 2 '(1 2 3 4 5 6 7)) ;; => '((1 2) (3 4) (5 6) (7)) (-partition-all 3 '(1 2 3 4 5 6 7)) ;; => '((1 2 3) (4 5 6) (7)) ``` #### -partition-in-steps `(n step list)` Return a new list with the items in `list` grouped into `n-`sized sublists at offsets `step` apart. If there are not enough items to make the last group `n-`sized, those items are discarded. ```el (-partition-in-steps 2 1 '(1 2 3 4)) ;; => '((1 2) (2 3) (3 4)) (-partition-in-steps 3 2 '(1 2 3 4)) ;; => '((1 2 3)) (-partition-in-steps 3 2 '(1 2 3 4 5)) ;; => '((1 2 3) (3 4 5)) ``` #### -partition-all-in-steps `(n step list)` Return a new list with the items in `list` grouped into `n-`sized sublists at offsets `step` apart. The last groups may contain less than `n` items. ```el (-partition-all-in-steps 2 1 '(1 2 3 4)) ;; => '((1 2) (2 3) (3 4) (4)) (-partition-all-in-steps 3 2 '(1 2 3 4)) ;; => '((1 2 3) (3 4)) (-partition-all-in-steps 3 2 '(1 2 3 4 5)) ;; => '((1 2 3) (3 4 5) (5)) ``` #### -partition-by `(fn list)` Apply `fn` to each item in `list`, splitting it each time `fn` returns a new value. ```el (-partition-by 'even? '()) ;; => '() (-partition-by 'even? '(1 1 2 2 2 3 4 6 8)) ;; => '((1 1) (2 2 2) (3) (4 6 8)) (--partition-by (< it 3) '(1 2 3 4 3 2 1)) ;; => '((1 2) (3 4 3) (2 1)) ``` #### -partition-by-header `(fn list)` Apply `fn` to the first item in `list`. That is the header value. Apply `fn` to each item in `list`, splitting it each time `fn` returns the header value, but only after seeing at least one other value (the body). ```el (--partition-by-header (= it 1) '(1 2 3 1 2 1 2 3 4)) ;; => '((1 2 3) (1 2) (1 2 3 4)) (--partition-by-header (> it 0) '(1 2 0 1 0 1 2 3 0)) ;; => '((1 2 0) (1 0) (1 2 3 0)) (-partition-by-header 'even? '(2 1 1 1 4 1 3 5 6 6 1)) ;; => '((2 1 1 1) (4 1 3 5) (6 6 1)) ``` #### -group-by `(fn list)` Separate `list` into an alist whose keys are `fn` applied to the elements of `list`. Keys are compared by `equal`. ```el (-group-by 'even? '()) ;; => '() (-group-by 'even? '(1 1 2 2 2 3 4 6 8)) ;; => '((nil 1 1 3) (t 2 2 2 4 6 8)) (--group-by (car (split-string it "/")) '("a/b" "c/d" "a/e")) ;; => '(("a" "a/b" "a/e") ("c" "c/d")) ``` ## Indexing Return indices of elements based on predicates, sort elements by indices etc. #### -elem-index `(elem list)` Return the index of the first element in the given `list` which is equal to the query element `elem`, or nil if there is no such element. ```el (-elem-index 2 '(6 7 8 2 3 4)) ;; => 3 (-elem-index "bar" '("foo" "bar" "baz")) ;; => 1 (-elem-index '(1 2) '((3) (5 6) (1 2) nil)) ;; => 2 ``` #### -elem-indices `(elem list)` Return the indices of all elements in `list` equal to the query element `elem`, in ascending order. ```el (-elem-indices 2 '(6 7 8 2 3 4 2 1)) ;; => '(3 6) (-elem-indices "bar" '("foo" "bar" "baz")) ;; => '(1) (-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) ;; => '(1 3) ``` #### -find-index `(pred list)` Take a predicate `pred` and a `list` and return the index of the first element in the list satisfying the predicate, or nil if there is no such element. ```el (-find-index 'even? '(2 4 1 6 3 3 5 8)) ;; => 0 (--find-index (< 5 it) '(2 4 1 6 3 3 5 8)) ;; => 3 (-find-index (-partial 'string-lessp "baz") '("bar" "foo" "baz")) ;; => 1 ``` #### -find-last-index `(pred list)` Take a predicate `pred` and a `list` and return the index of the last element in the list satisfying the predicate, or nil if there is no such element. ```el (-find-last-index 'even? '(2 4 1 6 3 3 5 8)) ;; => 7 (--find-last-index (< 5 it) '(2 7 1 6 3 8 5 2)) ;; => 5 (-find-last-index (-partial 'string-lessp "baz") '("q" "foo" "baz")) ;; => 1 ``` #### -find-indices `(pred list)` Return the indices of all elements in `list` satisfying the predicate `pred`, in ascending order. ```el (-find-indices 'even? '(2 4 1 6 3 3 5 8)) ;; => '(0 1 3 7) (--find-indices (< 5 it) '(2 4 1 6 3 3 5 8)) ;; => '(3 7) (-find-indices (-partial 'string-lessp "baz") '("bar" "foo" "baz")) ;; => '(1) ``` #### -grade-up `(comparator list)` Grade elements of `list` using `comparator` relation, yielding a permutation vector such that applying this permutation to `list` sorts it in ascending order. ```el (-grade-up '< '(3 1 4 2 1 3 3)) ;; => '(1 4 3 0 5 6 2) (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-up '< l) l)) ;; => '(1 1 2 3 3 3 4) ``` #### -grade-down `(comparator list)` Grade elements of `list` using `comparator` relation, yielding a permutation vector such that applying this permutation to `list` sorts it in descending order. ```el (-grade-down '< '(3 1 4 2 1 3 3)) ;; => '(2 0 5 6 3 1 4) (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-down '< l) l)) ;; => '(4 3 3 3 2 1 1) ``` ## Set operations Operations pretending lists are sets. #### -union `(list list2)` Return a new list containing the elements of `list1` and elements of `list2` that are not in `list1`. The test for equality is done with `equal`, or with `-compare-fn` if that's non-nil. ```el (-union '(1 2 3) '(3 4 5)) ;; => '(1 2 3 4 5) (-union '(1 2 3 4) '()) ;; => '(1 2 3 4) (-union '(1 1 2 2) '(3 2 1)) ;; => '(1 1 2 2 3) ``` #### -difference `(list list2)` Return a new list with only the members of `list` that are not in `list2`. The test for equality is done with `equal`, or with `-compare-fn` if that's non-nil. ```el (-difference '() '()) ;; => '() (-difference '(1 2 3) '(4 5 6)) ;; => '(1 2 3) (-difference '(1 2 3 4) '(3 4 5 6)) ;; => '(1 2) ``` #### -intersection `(list list2)` Return a new list containing only the elements that are members of both `list` and `list2`. The test for equality is done with `equal`, or with `-compare-fn` if that's non-nil. ```el (-intersection '() '()) ;; => '() (-intersection '(1 2 3) '(4 5 6)) ;; => '() (-intersection '(1 2 3 4) '(3 4 5 6)) ;; => '(3 4) ``` #### -distinct `(list)` Return a new list with all duplicates removed. The test for equality is done with `equal`, or with `-compare-fn` if that's non-nil. Alias: `-uniq` ```el (-distinct '()) ;; => '() (-distinct '(1 2 2 4)) ;; => '(1 2 4) ``` ## Other list operations Other list functions not fit to be classified elsewhere. #### -rotate `(n list)` Rotate `list` `n` places to the right. With `n` negative, rotate to the left. The time complexity is `o`(n). ```el (-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) ``` #### -repeat `(n x)` Return a list with `x` repeated `n` times. Return nil if `n` is less than 1. ```el (-repeat 3 :a) ;; => '(:a :a :a) (-repeat 1 :a) ;; => '(:a) (-repeat 0 :a) ;; => nil ``` #### -cons* `(&rest args)` Make a new list from the elements of `args`. The last 2 members of `args` are used as the final cons of the result so if the final member of `args` is not a list the result is a dotted list. ```el (-cons* 1 2) ;; => '(1 . 2) (-cons* 1 2 3) ;; => '(1 2 . 3) (-cons* 1) ;; => 1 ``` #### -snoc `(list elem &rest elements)` Append `elem` to the end of the list. This is like `cons`, but operates on the end of list. If `elements` is non nil, append these to the list as well. ```el (-snoc '(1 2 3) 4) ;; => '(1 2 3 4) (-snoc '(1 2 3) 4 5 6) ;; => '(1 2 3 4 5 6) (-snoc '(1 2 3) '(4 5 6)) ;; => '(1 2 3 (4 5 6)) ``` #### -interpose `(sep list)` Return a new list of all elements in `list` separated by `sep`. ```el (-interpose "-" '()) ;; => '() (-interpose "-" '("a")) ;; => '("a") (-interpose "-" '("a" "b" "c")) ;; => '("a" "-" "b" "-" "c") ``` #### -interleave `(&rest lists)` Return a new list of the first item in each list, then the second etc. ```el (-interleave '(1 2) '("a" "b")) ;; => '(1 "a" 2 "b") (-interleave '(1 2) '("a" "b") '("A" "B")) ;; => '(1 "a" "A" 2 "b" "B") (-interleave '(1 2 3) '("a" "b")) ;; => '(1 "a" 2 "b") ``` #### -zip-with `(fn list1 list2)` Zip the two lists `list1` and `list2` using a function `fn`. This function is applied pairwise taking as first argument element of `list1` and as second argument element of `list2` at corresponding position. The anaphoric form `--zip-with` binds the elements from `list1` as `it`, and the elements from `list2` as `other`. ```el (-zip-with '+ '(1 2 3) '(4 5 6)) ;; => '(5 7 9) (-zip-with 'cons '(1 2 3) '(4 5 6)) ;; => '((1 . 4) (2 . 5) (3 . 6)) (--zip-with (concat it " and " other) '("Batman" "Jekyll") '("Robin" "Hyde")) ;; => '("Batman and Robin" "Jekyll and Hyde") ``` #### -zip `(&rest lists)` Zip `lists` together. Group the head of each list, followed by the second elements of each list, and so on. The lengths of the returned groupings are equal to the length of the shortest input list. If two lists are provided as arguments, return the groupings as a list of cons cells. Otherwise, return the groupings as a list of lists. Please note! This distinction is being removed in an upcoming 2.0 release of Dash. If you rely on this behavior, use -zip-pair instead. ```el (-zip '(1 2 3) '(4 5 6)) ;; => '((1 . 4) (2 . 5) (3 . 6)) (-zip '(1 2 3) '(4 5 6 7)) ;; => '((1 . 4) (2 . 5) (3 . 6)) (-zip '(1 2 3 4) '(4 5 6)) ;; => '((1 . 4) (2 . 5) (3 . 6)) ``` #### -zip-fill `(fill-value &rest lists)` Zip `lists`, with `fill-value` padded onto the shorter lists. The lengths of the returned groupings are equal to the length of the longest input list. ```el (-zip-fill 0 '(1 2 3 4 5) '(6 7 8 9)) ;; => '((1 . 6) (2 . 7) (3 . 8) (4 . 9) (5 . 0)) ``` #### -cycle `(list)` Return an infinite copy of `list` that will cycle through the elements and repeat from the beginning. ```el (-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)) ``` #### -pad `(fill-value &rest lists)` Appends `fill-value` to the end of each list in `lists` such that they will all have the same length. ```el (-pad 0 '()) ;; => '(nil) (-pad 0 '(1)) ;; => '((1)) (-pad 0 '(1 2 3) '(4 5)) ;; => '((1 2 3) (4 5 0)) ``` #### -table `(fn &rest lists)` Compute outer product of `lists` using function `fn`. The function `fn` should have the same arity as the number of supplied lists. The outer product is computed by applying fn to all possible combinations created by taking one element from each list in order. The dimension of the result is (length lists). See also: [`-table-flat`](#-table-flat-fn-rest-lists) ```el (-table '* '(1 2 3) '(1 2 3)) ;; => '((1 2 3) (2 4 6) (3 6 9)) (-table (lambda (a b) (-sum (-zip-with '* a b))) '((1 2) (3 4)) '((1 3) (2 4))) ;; => '((7 15) (10 22)) (apply '-table 'list (-repeat 3 '(1 2))) ;; => '((((1 1 1) (2 1 1)) ((1 2 1) (2 2 1))) (((1 1 2) (2 1 2)) ((1 2 2) (2 2 2)))) ``` #### -table-flat `(fn &rest lists)` Compute flat outer product of `lists` using function `fn`. The function `fn` should have the same arity as the number of supplied lists. The outer product is computed by applying fn to all possible combinations created by taking one element from each list in order. The results are flattened, ignoring the tensor structure of the result. This is equivalent to calling: (-flatten-n (1- (length lists)) (-table fn lists)) but the implementation here is much more efficient. See also: [`-flatten-n`](#-flatten-n-num-list), [`-table`](#-table-fn-rest-lists) ```el (-table-flat 'list '(1 2 3) '(a b c)) ;; => '((1 a) (2 a) (3 a) (1 b) (2 b) (3 b) (1 c) (2 c) (3 c)) (-table-flat '* '(1 2 3) '(1 2 3)) ;; => '(1 2 3 2 4 6 3 6 9) (apply '-table-flat 'list (-repeat 3 '(1 2))) ;; => '((1 1 1) (2 1 1) (1 2 1) (2 2 1) (1 1 2) (2 1 2) (1 2 2) (2 2 2)) ``` #### -first `(pred list)` Return the first x in `list` where (`pred` x) is non-nil, else nil. To get the first item in the list no questions asked, use `car`. Alias: `-find` ```el (-first 'even? '(1 2 3)) ;; => 2 (-first 'even? '(1 3 5)) ;; => nil (--first (> it 2) '(1 2 3)) ;; => 3 ``` #### -some `(pred list)` Return (`pred` x) for the first `list` item where (`pred` x) is non-nil, else nil. Alias: `-any` ```el (-some 'even? '(1 2 3)) ;; => t (--some (member 'foo it) '((foo bar) (baz))) ;; => '(foo bar) (--some (plist-get it :bar) '((:foo 1 :bar 2) (:baz 3))) ;; => 2 ``` #### -last `(pred list)` Return the last x in `list` where (`pred` x) is non-nil, else nil. ```el (-last 'even? '(1 2 3 4 5 6 3 3 3)) ;; => 6 (-last 'even? '(1 3 7 5 9)) ;; => nil (--last (> (length it) 3) '("a" "looong" "word" "and" "short" "one")) ;; => "short" ``` #### -first-item `(list)` Return the first item of `list`, or nil on an empty list. ```el (-first-item '(1 2 3)) ;; => 1 (-first-item nil) ;; => nil ``` #### -last-item `(list)` Return the last item of `list`, or nil on an empty list. ```el (-last-item '(1 2 3)) ;; => 3 (-last-item nil) ;; => nil ``` #### -butlast `(list)` Return a list of all items in list except for the last. ```el (-butlast '(1 2 3)) ;; => '(1 2) (-butlast '(1 2)) ;; => '(1) (-butlast '(1)) ;; => nil ``` #### -sort `(comparator list)` Sort `list`, stably, comparing elements using `comparator`. Return the sorted list. `list` is `not` modified by side effects. `comparator` is called with two elements of `list`, and should return non-nil if the first element should sort before the second. ```el (-sort '< '(3 1 2)) ;; => '(1 2 3) (-sort '> '(3 1 2)) ;; => '(3 2 1) (--sort (< it other) '(3 1 2)) ;; => '(1 2 3) ``` #### -list `(&rest args)` Return a list with `args`. If first item of `args` is already a list, simply return `args`. If not, return a list with `args` as elements. ```el (-list 1) ;; => '(1) (-list 1 2 3) ;; => '(1 2 3) (-list '(1 2 3)) ;; => '(1 2 3) ``` #### -fix `(fn list)` 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") ``` ## Tree operations Functions pretending lists are trees. #### -tree-seq `(branch children tree)` Return a sequence of the nodes in `tree`, in depth-first search order. `branch` is a predicate of one argument that returns non-nil if the passed argument is a branch, that is, a node that can have children. `children` is a function of one argument that returns the children of the passed branch node. Non-branch nodes are simply copied. ```el (-tree-seq 'listp 'identity '(1 (2 3) 4 (5 (6 7)))) ;; => '((1 (2 3) 4 (5 (6 7))) 1 (2 3) 2 3 4 (5 (6 7)) 5 (6 7) 6 7) (-tree-seq 'listp 'reverse '(1 (2 3) 4 (5 (6 7)))) ;; => '((1 (2 3) 4 (5 (6 7))) (5 (6 7)) (6 7) 7 6 5 4 (2 3) 3 2 1) (--tree-seq (vectorp it) (append it nil) [1 [2 3] 4 [5 [6 7]]]) ;; => '([1 [2 3] 4 [5 [6 7]]] 1 [2 3] 2 3 4 [5 [6 7]] 5 [6 7] 6 7) ``` #### -tree-map `(fn tree)` Apply `fn` to each element of `tree` while preserving the tree structure. ```el (-tree-map '1+ '(1 (2 3) (4 (5 6) 7))) ;; => '(2 (3 4) (5 (6 7) 8)) (-tree-map '(lambda (x) (cons x (expt 2 x))) '(1 (2 3) 4)) ;; => '((1 . 2) ((2 . 4) (3 . 8)) (4 . 16)) (--tree-map (length it) '("" ("

" "text" "

") "")) ;; => '(6 (3 4 4) 7) ``` #### -tree-map-nodes `(pred fun tree)` Call `fun` on each node of `tree` that satisfies `pred`. If `pred` returns nil, continue descending down this node. If `pred` returns non-nil, apply `fun` to this node and do not descend further. ```el (-tree-map-nodes 'vectorp (lambda (x) (-sum (append x nil))) '(1 [2 3] 4 (5 [6 7] 8))) ;; => '(1 5 4 (5 13 8)) (-tree-map-nodes 'keywordp (lambda (x) (symbol-name x)) '(1 :foo 4 ((5 6 :bar) :baz 8))) ;; => '(1 ":foo" 4 ((5 6 ":bar") ":baz" 8)) (--tree-map-nodes (eq (car-safe it) 'add-mode) (-concat it (list :mode 'emacs-lisp-mode)) '(with-mode emacs-lisp-mode (foo bar) (add-mode a b) (baz (add-mode c d)))) ;; => '(with-mode emacs-lisp-mode (foo bar) (add-mode a b :mode emacs-lisp-mode) (baz (add-mode c d :mode emacs-lisp-mode))) ``` #### -tree-reduce `(fn tree)` Use `fn` to reduce elements of list `tree`. If elements of `tree` are lists themselves, apply the reduction recursively. `fn` is first applied to first element of the list and second element, then on this result and third element from the list etc. See [`-reduce-r`](#-reduce-r-fn-list) for how exactly are lists of zero or one element handled. ```el (-tree-reduce '+ '(1 (2 3) (4 5))) ;; => 15 (-tree-reduce 'concat '("strings" (" on" " various") ((" levels")))) ;; => "strings on various levels" (--tree-reduce (cond ((stringp it) (concat it " " acc)) (t (let ((sn (symbol-name it))) (concat "<" sn ">" acc "")))) '(body (p "some words") (div "more" (b "bold") "words"))) ;; => "

some words

more bold words
" ``` #### -tree-reduce-from `(fn init-value tree)` Use `fn` to reduce elements of list `tree`. If elements of `tree` are lists themselves, apply the reduction recursively. `fn` is first applied to `init-value` and first element of the list, then on this result and second element from the list etc. The initial value is ignored on cons pairs as they always contain two elements. ```el (-tree-reduce-from '+ 1 '(1 (1 1) ((1)))) ;; => 8 (--tree-reduce-from (-concat acc (list it)) nil '(1 (2 3 (4 5)) (6 7))) ;; => '((7 6) ((5 4) 3 2) 1) ``` #### -tree-mapreduce `(fn folder tree)` Apply `fn` to each element of `tree`, and make a list of the results. If elements of `tree` are lists themselves, apply `fn` recursively to elements of these nested lists. Then reduce the resulting lists using `folder` and initial value `init-value`. See [`-reduce-r-from`](#-reduce-r-from-fn-initial-value-list). This is the same as calling [`-tree-reduce`](#-tree-reduce-fn-tree) after [`-tree-map`](#-tree-map-fn-tree) but is twice as fast as it only traverse the structure once. ```el (-tree-mapreduce 'list 'append '(1 (2 (3 4) (5 6)) (7 (8 9)))) ;; => '(1 2 3 4 5 6 7 8 9) (--tree-mapreduce 1 (+ it acc) '(1 (2 (4 9) (2 1)) (7 (4 3)))) ;; => 9 (--tree-mapreduce 0 (max acc (1+ it)) '(1 (2 (4 9) (2 1)) (7 (4 3)))) ;; => 3 ``` #### -tree-mapreduce-from `(fn folder init-value tree)` Apply `fn` to each element of `tree`, and make a list of the results. If elements of `tree` are lists themselves, apply `fn` recursively to elements of these nested lists. Then reduce the resulting lists using `folder` and initial value `init-value`. See [`-reduce-r-from`](#-reduce-r-from-fn-initial-value-list). This is the same as calling [`-tree-reduce-from`](#-tree-reduce-from-fn-init-value-tree) after [`-tree-map`](#-tree-map-fn-tree) but is twice as fast as it only traverse the structure once. ```el (-tree-mapreduce-from 'identity '* 1 '(1 (2 (3 4) (5 6)) (7 (8 9)))) ;; => 362880 (--tree-mapreduce-from (+ it it) (cons it acc) nil '(1 (2 (4 9) (2 1)) (7 (4 3)))) ;; => '(2 (4 (8 18) (4 2)) (14 (8 6))) (concat "{" (--tree-mapreduce-from (cond ((-cons-pair? it) (concat (symbol-name (car it)) " -> " (symbol-name (cdr it)))) (t (concat (symbol-name it) " : {"))) (concat it (unless (or (equal acc "}") (equal (substring it (1- (length it))) "{")) ", ") acc) "}" '((elips-mode (foo (bar . booze)) (baz . qux)) (c-mode (foo . bla) (bum . bam))))) ;; => "{elips-mode : {foo : {bar -> booze}, baz -> qux}, c-mode : {foo -> bla, bum -> bam}}" ``` #### -clone `(list)` Create a deep copy of `list`. The new list has the same elements and structure but all cons are replaced with new ones. This is useful when you need to clone a structure such as plist or alist. ```el (let* ((a '(1 2 3)) (b (-clone a))) (nreverse a) b) ;; => '(1 2 3) ``` ## Threading macros #### -> `(x &optional form &rest more)` Thread the expr through the forms. Insert `x` as the second item in the first form, making a list of it if it is not a list already. If there are more forms, insert the first form as the second item in second form, etc. ```el (-> '(2 3 5)) ;; => '(2 3 5) (-> '(2 3 5) (append '(8 13))) ;; => '(2 3 5 8 13) (-> '(2 3 5) (append '(8 13)) (-slice 1 -1)) ;; => '(3 5 8) ``` #### ->> `(x &optional form &rest more)` Thread the expr through the forms. Insert `x` as the last item in the first form, making a list of it if it is not a list already. If there are more forms, insert the first form as the last item in second form, etc. ```el (->> '(1 2 3) (-map 'square)) ;; => '(1 4 9) (->> '(1 2 3) (-map 'square) (-remove 'even?)) ;; => '(1 9) (->> '(1 2 3) (-map 'square) (-reduce '+)) ;; => 14 ``` #### --> `(x form &rest more)` Thread the expr through the forms. Insert `x` at the position signified by the token `it` in the first form. If there are more forms, insert the first form at the position signified by `it` in in second form, etc. ```el (--> "def" (concat "abc" it "ghi")) ;; => "abcdefghi" (--> "def" (concat "abc" it "ghi") (upcase it)) ;; => "ABCDEFGHI" (--> "def" (concat "abc" it "ghi") upcase) ;; => "ABCDEFGHI" ``` #### -some-> `(x &optional form &rest more)` When expr is non-nil, thread it through the first form (via [`->`](#--x-optional-form-rest-more)), and when that result is non-nil, through the next form, etc. ```el (-some-> '(2 3 5)) ;; => '(2 3 5) (-some-> 5 square) ;; => 25 (-some-> 5 even? square) ;; => nil ``` #### -some->> `(x &optional form &rest more)` When expr is non-nil, thread it through the first form (via [`->>`](#--x-optional-form-rest-more)), and when that result is non-nil, through the next form, etc. ```el (-some->> '(1 2 3) (-map 'square)) ;; => '(1 4 9) (-some->> '(1 3 5) (-last 'even?) (+ 100)) ;; => nil (-some->> '(2 4 6) (-last 'even?) (+ 100)) ;; => 106 ``` #### -some--> `(x &optional form &rest more)` When expr in non-nil, thread it through the first form (via [`-->`](#---x-form-rest-more)), and when that result is non-nil, through the next form, etc. ```el (-some--> "def" (concat "abc" it "ghi")) ;; => "abcdefghi" (-some--> nil (concat "abc" it "ghi")) ;; => nil (-some--> '(1 3 5) (-filter 'even? it) (append it it) (-map 'square it)) ;; => nil ``` ## Binding Convenient versions of `let` and `let*` constructs combined with flow control. #### -when-let `(var-val &rest body)` If `val` evaluates to non-nil, bind it to `var` and execute body. `var-val` should be a (`var` `val`) pair. Note: binding is done according to [`-let`](#-let-varlist-rest-body). ```el (-when-let (match-index (string-match "d" "abcd")) (+ match-index 2)) ;; => 5 (-when-let ((&plist :foo foo) (list :foo "foo")) foo) ;; => "foo" (-when-let ((&plist :foo foo) (list :bar "bar")) foo) ;; => nil ``` #### -when-let* `(vars-vals &rest body)` If all `vals` evaluate to true, bind them to their corresponding `vars` and execute body. `vars-vals` should be a list of (`var` `val`) pairs. Note: binding is done according to [`-let*`](#-let-varlist-rest-body). `vals` are evaluated sequentially, and evaluation stops after the first nil `val` is encountered. ```el (-when-let* ((x 5) (y 3) (z (+ y 4))) (+ x y z)) ;; => 15 (-when-let* ((x 5) (y nil) (z 7)) (+ x y z)) ;; => nil ``` #### -if-let `(var-val then &rest else)` If `val` evaluates to non-nil, bind it to `var` and do `then`, otherwise do `else`. `var-val` should be a (`var` `val`) pair. Note: binding is done according to [`-let`](#-let-varlist-rest-body). ```el (-if-let (match-index (string-match "d" "abc")) (+ match-index 3) 7) ;; => 7 (--if-let (even? 4) it nil) ;; => t ``` #### -if-let* `(vars-vals then &rest else)` If all `vals` evaluate to true, bind them to their corresponding `vars` and do `then`, otherwise do `else`. `vars-vals` should be a list of (`var` `val`) pairs. Note: binding is done according to [`-let*`](#-let-varlist-rest-body). `vals` are evaluated sequentially, and evaluation stops after the first nil `val` is encountered. ```el (-if-let* ((x 5) (y 3) (z 7)) (+ x y z) "foo") ;; => 15 (-if-let* ((x 5) (y nil) (z 7)) (+ x y z) "foo") ;; => "foo" (-if-let* (((_ _ x) '(nil nil 7))) x) ;; => 7 ``` #### -let `(varlist &rest body)` Bind variables according to `varlist` then eval `body`. `varlist` is a list of lists of the form (`pattern` `source`). Each `pattern` is matched against the `source` "structurally". `source` is only evaluated once for each `pattern`. Each `pattern` is matched recursively, and can therefore contain sub-patterns which are matched against corresponding sub-expressions of `source`. All the SOURCEs are evalled before any symbols are bound (i.e. "in parallel"). If `varlist` only contains one (`pattern` `source`) element, you can optionally specify it using a vector and discarding the outer-most parens. Thus (-let ((`pattern` `source`)) ..) becomes (-let [`pattern` `source`] ..). [`-let`](#-let-varlist-rest-body) uses a convention of not binding places (symbols) starting with _ whenever it's possible. You can use this to skip over entries you don't care about. However, this is not *always* possible (as a result of implementation) and these symbols might get bound to undefined values. Following is the overview of supported patterns. Remember that patterns can be matched recursively, so every a, b, aK in the following can be a matching construct and not necessarily a symbol/variable. Symbol: a - bind the `source` to `a`. This is just like regular `let`. Conses and lists: (a) - bind `car` of cons/list to `a` (a . b) - bind car of cons to `a` and `cdr` to `b` (a b) - bind car of list to `a` and `cadr` to `b` (a1 a2 a3 ...) - bind 0th car of list to `a1`, 1st to `a2`, 2nd to `a3` ... (a1 a2 a3 ... aN . rest) - as above, but bind the Nth cdr to `rest`. Vectors: [a] - bind 0th element of a non-list sequence to `a` (works with vectors, strings, bit arrays...) [a1 a2 a3 ...] - bind 0th element of non-list sequence to `a0`, 1st to `a1`, 2nd to `a2`, ... If the `pattern` is shorter than `source`, the values at places not in `pattern` are ignored. If the `pattern` is longer than `source`, an `error` is thrown. [a1 a2 a3 ... &rest rest] - as above, but bind the rest of the sequence to `rest`. This is conceptually the same as improper list matching (a1 a2 ... aN . rest) Key/value stores: (&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the `source` plist to aK. If the value is not found, aK is nil. (&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the `source` alist to aK. If the value is not found, aK is nil. (&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the `source` hash table to aK. If the value is not found, aK is nil. Further, special keyword &keys supports "inline" matching of plist-like key-value pairs, similarly to &keys keyword of `cl-defun`. (a1 a2 ... aN &keys key1 b1 ... keyN bK) This binds `n` values from the list to a1 ... aN, then interprets the cdr as a plist (see key/value matching above). You can name the source using the syntax `symbol` &as `pattern`. This syntax works with lists (proper or improper), vectors and all types of maps. (list &as a b c) (list 1 2 3) binds `a` to 1, `b` to 2, `c` to 3 and `list` to (1 2 3). Similarly: (bounds &as beg . end) (cons 1 2) binds `beg` to 1, `end` to 2 and `bounds` to (1 . 2). (items &as first . rest) (list 1 2 3) binds `first` to 1, `rest` to (2 3) and `items` to (1 2 3) [vect &as _ b c] [1 2 3] binds `b` to 2, `c` to 3 and `vect` to [1 2 3] (_ avoids binding as usual). (plist &as &plist :b b) (list :a 1 :b 2 :c 3) binds `b` to 2 and `plist` to (:a 1 :b 2 :c 3). Same for &alist and &hash. This is especially useful when we want to capture the result of a computation and destructure at the same time. Consider the form (function-returning-complex-structure) returning a list of two vectors with two items each. We want to capture this entire result and pass it to another computation, but at the same time we want to get the second item from each vector. We can achieve it with pattern (result &as [_ a] [_ b]) (function-returning-complex-structure) Note: Clojure programmers may know this feature as the ":as binding". The difference is that we put the &as at the front because we need to support improper list binding. ```el (-let (([a (b c) d] [1 (2 3) 4])) (list a b c d)) ;; => '(1 2 3 4) (-let [(a b c . d) (list 1 2 3 4 5 6)] (list a b c d)) ;; => '(1 2 3 (4 5 6)) (-let [(&plist :foo foo :bar bar) (list :baz 3 :foo 1 :qux 4 :bar 2)] (list foo bar)) ;; => '(1 2) ``` #### -let* `(varlist &rest body)` Bind variables according to `varlist` then eval `body`. `varlist` is a list of lists of the form (`pattern` `source`). Each `pattern` is matched against the `source` structurally. `source` is only evaluated once for each `pattern`. Each `source` can refer to the symbols already bound by this `varlist`. This is useful if you want to destructure `source` recursively but also want to name the intermediate structures. See [`-let`](#-let-varlist-rest-body) for the list of all possible patterns. ```el (-let* (((a . b) (cons 1 2)) ((c . d) (cons 3 4))) (list a b c d)) ;; => '(1 2 3 4) (-let* (((a . b) (cons 1 (cons 2 3))) ((c . d) b)) (list a b c d)) ;; => '(1 (2 . 3) 2 3) (-let* (((&alist "foo" foo "bar" bar) (list (cons "foo" 1) (cons "bar" (list 'a 'b 'c)))) ((a b c) bar)) (list foo a b c bar)) ;; => '(1 a b c (a b c)) ``` #### -lambda `(match-form &rest body)` Return a lambda which destructures its input as `match-form` and executes `body`. Note that you have to enclose the `match-form` in a pair of parens, such that: (-lambda (x) body) (-lambda (x y ...) body) has the usual semantics of `lambda`. Furthermore, these get translated into normal lambda, so there is no performance penalty. See [`-let`](#-let-varlist-rest-body) for the description of destructuring mechanism. ```el (-map (-lambda ((x y)) (+ x y)) '((1 2) (3 4) (5 6))) ;; => '(3 7 11) (-map (-lambda ([x y]) (+ x y)) '([1 2] [3 4] [5 6])) ;; => '(3 7 11) (funcall (-lambda ((_ . a) (_ . b)) (-concat a b)) '(1 2 3) '(4 5 6)) ;; => '(2 3 5 6) ``` ## Side-effects Functions iterating over lists for side-effect only. #### -each `(list fn)` Call `fn` with every item in `list`. Return nil, used for side-effects only. ```el (let (s) (-each '(1 2 3) (lambda (item) (setq s (cons item s))))) ;; => nil (let (s) (-each '(1 2 3) (lambda (item) (setq s (cons item s)))) s) ;; => '(3 2 1) (let (s) (--each '(1 2 3) (setq s (cons it s))) s) ;; => '(3 2 1) ``` #### -each-while `(list pred fn)` Call `fn` with every item in `list` while (`pred` item) is non-nil. Return nil, used for side-effects only. ```el (let (s) (-each-while '(2 4 5 6) 'even? (lambda (item) (!cons item s))) s) ;; => '(4 2) (let (s) (--each-while '(1 2 3 4) (< it 3) (!cons it s)) s) ;; => '(2 1) ``` #### -dotimes `(num fn)` Repeatedly calls `fn` (presumably for side-effects) passing in integers from 0 through `num-1`. ```el (let (s) (-dotimes 3 (lambda (n) (!cons n s))) s) ;; => '(2 1 0) (let (s) (--dotimes 5 (!cons it s)) s) ;; => '(4 3 2 1 0) ``` ## Destructive operations #### !cons `(car cdr)` Destructive: Set `cdr` to the cons of `car` and `cdr`. ```el (let (l) (!cons 5 l) l) ;; => '(5) (let ((l '(3))) (!cons 5 l) l) ;; => '(5 3) ``` #### !cdr `(list)` Destructive: Set `list` to the cdr of `list`. ```el (let ((l '(3))) (!cdr l) l) ;; => '() (let ((l '(3 5))) (!cdr l) l) ;; => '(5) ``` ## Function combinators These combinators require Emacs 24 for its lexical scope. So they are offered in a separate package: `dash-functional`. #### -partial `(fn &rest args)` Takes a function `fn` and fewer than the normal arguments to `fn`, and returns a fn that takes a variable number of additional `args`. When called, the returned function calls `fn` with `args` first and then additional args. ```el (funcall (-partial '- 5) 3) ;; => 2 (funcall (-partial '+ 5 2) 3) ;; => 10 ``` #### -rpartial `(fn &rest args)` Takes a function `fn` and fewer than the normal arguments to `fn`, and returns a fn that takes a variable number of additional `args`. When called, the returned function calls `fn` with the additional args first and then `args`. ```el (funcall (-rpartial '- 5) 8) ;; => 3 (funcall (-rpartial '- 5 2) 10) ;; => 3 ``` #### -juxt `(&rest fns)` Takes a list of functions and returns a fn that is the juxtaposition of those fns. The returned fn takes a variable number of args, and returns a list containing the result of applying each fn to the args (left-to-right). ```el (funcall (-juxt '+ '-) 3 5) ;; => '(8 -2) (-map (-juxt 'identity 'square) '(1 2 3)) ;; => '((1 1) (2 4) (3 9)) ``` #### -compose `(&rest fns)` Takes a list of functions and returns a fn that is the composition of those fns. The returned fn takes a variable number of arguments, and returns the result of applying each fn to the result of applying the previous fn to the arguments (right-to-left). ```el (funcall (-compose 'square '+) 2 3) ;; => (square (+ 2 3)) (funcall (-compose 'identity 'square) 3) ;; => (square 3) (funcall (-compose 'square 'identity) 3) ;; => (square 3) ``` #### -applify `(fn)` Changes an n-arity function `fn` to a 1-arity function that expects a list with n items as arguments ```el (-map (-applify '+) '((1 1 1) (1 2 3) (5 5 5))) ;; => '(3 6 15) (-map (-applify (lambda (a b c) (\` ((\, a) ((\, b) ((\, c))))))) '((1 1 1) (1 2 3) (5 5 5))) ;; => '((1 (1 (1))) (1 (2 (3))) (5 (5 (5)))) (funcall (-applify '<) '(3 6)) ;; => t ``` #### -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). In types: (b -> b -> c) -> (a -> b) -> a -> a -> c ```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 'int-to-string) '(2 100 22)) ;; => 22 ``` #### -flip `(func)` Swap the order of arguments for binary function `func`. In types: (a -> b -> c) -> b -> a -> c ```el (funcall (-flip '<) 2 1) ;; => t (funcall (-flip '-) 3 8) ;; => 5 (-sort (-flip '<) '(4 3 6 1)) ;; => '(6 4 3 1) ``` #### -const `(c)` Return a function that returns `c` ignoring any additional arguments. 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 ``` #### -cut `(&rest params)` Take n-ary function and n arguments and specialize some of them. Arguments denoted by <> will be left unspecialized. See `srfi-26` for detailed description. ```el (funcall (-cut list 1 <> 3 <> 5) 2 4) ;; => '(1 2 3 4 5) (-map (-cut funcall <> 5) '(1+ 1- (lambda (x) (/ 1.0 x)))) ;; => '(6 4 0.2) (-filter (-cut < <> 5) '(1 3 5 7 9)) ;; => '(1 3) ``` #### -not `(pred)` Take an unary predicates `pred` and return an unary predicate that returns t if `pred` returns nil and nil if `pred` returns non-nil. ```el (funcall (-not 'even?) 5) ;; => t (-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 an unary predicate with argument x that returns non-nil if at least one of the `preds` returns non-nil on x. In types: [a -> Bool] -> a -> Bool ```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 ``` #### -andfn `(&rest preds)` Take list of unary predicates `preds` and return an 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 ```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) ``` #### -iteratefn `(fn n)` Return a function `fn` composed `n` times with itself. `fn` is a unary function. If you need to use a function of higher arity, use [`-applify`](#-applify-fn) first to turn it into an unary function. With n = 0, this acts as identity function. In types: (a -> a) -> Int -> a -> a. This function satisfies the following law: (funcall (-iteratefn fn n) init) = (-last-item (-iterate fn init (1+ n))). ```el (funcall (-iteratefn (lambda (x) (* x x)) 3) 2) ;; => 256 (funcall (-iteratefn '1+ 3) 1) ;; => 4 (funcall (-iteratefn 'cdr 3) '(1 2 3 4 5)) ;; => '(4 5) ``` #### -fixfn `(fn &optional equal-test halt-test)` Return a function that computes the (least) fixpoint of `fn`. `fn` must be a unary function. The returned lambda takes a single argument, `x`, the initial value for the fixpoint iteration. The iteration halts when either of the following conditions is satisified: 1. Iteration converges to the fixpoint, with equality being tested using `equal-test`. If `equal-test` is not specified, `equal` is used. For functions over the floating point numbers, it may be necessary to provide an appropriate appoximate comparsion test. 2. `halt-test` returns a non-nil value. `halt-test` defaults to a simple counter that returns t after `-fixfn-max-iterations`, to guard against infinite iteration. Otherwise, `halt-test` must be a function that accepts a single argument, the current value of `x`, and returns non-nil as long as iteration should continue. In this way, a more sophisticated convergence test may be supplied by the caller. The return value of the lambda is either the fixpoint or, if iteration halted before converging, a cons with car `halted` and cdr the final output from `halt-test`. In types: (a -> a) -> a -> a. ```el (funcall (-fixfn 'cos 'approx-equal) 0.7) ;; ~> 0.7390851332151607 (funcall (-fixfn (lambda (x) (expt (+ x 10) 0.25))) 2.0) ;; => 1.8555845286409378 (funcall (-fixfn 'sin 'approx-equal) 0.1) ;; => '(halted . t) ``` #### -prodfn `(&rest fns)` Take a list of n functions and return a function that takes a list of length n, applying i-th function to i-th element of the input list. Returns a list of length n. In types (for n=2): ((a -> b), (c -> d)) -> (a, c) -> (b, d) This function satisfies the following laws: (-compose (-prodfn f g ...) (-prodfn f' g' ...)) = (-prodfn (-compose f f') (-compose g g') ...) (-prodfn f g ...) = (-juxt (-compose f (-partial 'nth 0)) (-compose g (-partial 'nth 1)) ...) (-compose (-prodfn f g ...) (-juxt f' g' ...)) = (-juxt (-compose f f') (-compose g g') ...) (-compose (-partial 'nth n) (-prod f1 f2 ...)) = (-compose fn (-partial 'nth n)) ```el (funcall (-prodfn '1+ '1- 'int-to-string) '(1 2 3)) ;; => '(2 1 "3") (-map (-prodfn '1+ '1-) '((1 2) (3 4) (5 6) (7 8))) ;; => '((2 1) (4 3) (6 5) (8 7)) (apply '+ (funcall (-prodfn 'length 'string-to-int) '((1 2 3) "15"))) ;; => 18 ``` ## Contribute Yes, please do. Pure functions in the list manipulation realm only, please. There's a suite of tests in `dev/examples.el`, so remember to add tests for your function, or I might break it later. You'll find the repo at: https://github.com/magnars/dash.el Run the tests with ./run-tests.sh Create the docs with ./create-docs.sh I highly recommend that you install these as a pre-commit hook, so that the tests are always running and the docs are always in sync: cp pre-commit.sh .git/hooks/pre-commit Oh, and don't edit `README.md` directly, it is auto-generated. Change `readme-template.md` or `examples-to-docs.el` instead. ## Changelist - Added lexical binding pragma to dash.el ### From 2.11 to 2.12 - Add GNU ELPA support. (Phillip Lord) - Add `-some->`, `-some->>`, and `-some-->` macros. (Cam Saul) - `-is-suffix?` no longer destroys input list. - Faster hashtable implementation for `-union`. - Improvements to docstrings and examples ### From 2.10 to 2.11 - Lots of clean up wrt byte compilation, debug macros and tests ### From 2.9 to 2.10 - Add `-let` destructuring to `-if-let` and `-when-let` (Fredrik Bergroth) ### From 2.8 to 2.9 - Add `-let`, `-let*` and `-lambda` with destructuring - Add `-tree-seq` and `-tree-map-nodes` - Add `-non-nil` - Add `-fix` - Add `-fixfn` (dash-functional 1.2) - Add `-copy` (Wilfred Hughes) ### From 2.7 to 2.8 - Add `-butlast` ### From 2.6 to 2.7 - `-zip` now supports more than two lists (Steve Lamb) - Add `-cycle` , `-pad` , `-annotate` , `-zip-fill` (Steve Lamb) - Add `-table`, `-table-flat` (finite cartesian product) - Add `-flatten-n` - `-slice` now supports "step" argument - Add functional combinators `-iteratefn`, `-prodfn` - Add `-replace`, `-splice`, `-splice-list` which generalize `-replace-at` and `-insert-at` - Add `-compose`, `-iteratefn` and `-prodfn` (dash-functional 1.1) ### From 2.5 to 2.6 - Add `-is-prefix-p`, `-is-suffix-p`, `-is-infix-p` (Matus Goljer) - Add `-iterate`, `-unfold` (Matus Goljer) - Add `-split-on`, `-split-when` (Matus Goljer) - Add `-find-last-index` (Matus Goljer) - Add `-list` (Johan Andersson) ### From 2.4 to 2.5 - Add `-same-items?` (Johan Andersson) - A few bugfixes ### From 2.3 to 2.4 - Add `-snoc` (Matus Goljer) - Add `-replace-at`, `-update-at`, `-remove-at`, and `-remove-at-indices` (Matus Goljer) ### From 2.2 to 2.3 - Add tree operations (Matus Goljer) - Make font-lock optional ### From 2.1 to 2.2 - Add `-compose` (Christina Whyte) ### From 2.0 to 2.1 - Add indexing operations (Matus Goljer) ### From 1.8 to 2.0 - Split out `dash-functional.el` (Matus Goljer) - Add `-andfn`, `-orfn`, `-not`, `-cut`, `-const`, `-flip` and `-on`. (Matus Goljer) - Fix `-min`, `-max`, `-min-by` and `-max-by` (Matus Goljer) ### From 1.7 to 1.8 - Add `-first-item` and `-last-item` (Wilfred Hughes) ### From 1.6 to 1.7 - Add `-rotate` (Matus Goljer) ### From 1.5 to 1.6 - Add `-min`, `-max`, `-min-by` and `-max-by` (Johan Andersson) ### From 1.4 to 1.5 - Add `-sum` and `-product` (Johan Andersson) ### From 1.3 to 1.4 - Add `-sort` - Add `-reduce-r` (Matus Goljer) - Add `-reduce-r-from` (Matus Goljer) ### From 1.2 to 1.3 - Add `-partition-in-steps` - Add `-partition-all-in-steps` ### From 1.1 to 1.2 - Add `-last` (Matus Goljer) - Add `-insert-at` (Emanuel Evans) - Add `-when-let` and `-if-let` (Emanuel Evans) - Add `-when-let*` and `-if-let*` (Emanuel Evans) - Some bugfixes ## Contributors - [Matus Goljer](https://github.com/Fuco1) contributed lots of features and functions. - [Takafumi Arakaki](https://github.com/tkf) contributed `-group-by`. - [tali713](https://github.com/tali713) is the author of `-applify`. - [Víctor M. Valenzuela](https://github.com/vemv) contributed `-repeat`. - [Nic Ferrier](https://github.com/nicferrier) contributed `-cons*`. - [Wilfred Hughes](https://github.com/Wilfred) contributed `-slice`, `-first-item` and `-last-item`. - [Emanuel Evans](https://github.com/shosti) contributed `-if-let`, `-when-let` and `-insert-at`. - [Johan Andersson](https://github.com/rejeep) contributed `-sum`, `-product` and `-same-items?` - [Christina Whyte](https://github.com/kurisuwhyte) contributed `-compose` - [Steve Lamb](https://github.com/steventlamb) contributed `-cycle`, `-pad`, `-annotate`, `-zip-fill` and an n-ary version of `-zip`. - [Fredrik Bergroth](https://github.com/fbergroth) made the `-if-let` family use `-let` destructuring and improved script for generating documentation. - [Mark Oteiza](https://github.com/holomorph) contributed the script to create an info manual. - [Vasilij Schneidermann](https://github.com/wasamasa) contributed `-some`. - [William West](https://github.com/occidens) made `-fixfn` more robust at handling floats. - [Cam Saül](https://github.com/cammsaul) contributed `-some->`, `-some->>`, and `-some-->`. Thanks! ## License Copyright (C) 2012-2014 Magnar Sveen Authors: Magnar Sveen Keywords: lists This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . dash.el-2.12.1/create-docs.sh000077500000000000000000000006051261164447300156540ustar00rootroot00000000000000#!/usr/bin/env bash if [ -z "$EMACS" ] ; then EMACS="emacs" fi if [ -z "$MAKEINFO" ] ; then MAKEINFO="makeinfo" fi $EMACS -batch -l dash.el -l dash-functional.el -l dev/examples-to-docs.el -l dev/examples.el -f create-docs-file $EMACS -batch -l dash.el -l dash-functional.el -l dev/examples-to-info.el -l dev/examples.el -f create-info-file $MAKEINFO --fill-column=70 dash.texi dash.el-2.12.1/dash-functional.el000066400000000000000000000174071261164447300165350ustar00rootroot00000000000000;;; dash-functional.el --- Collection of useful combinators for Emacs Lisp -*- lexical-binding: t -*- ;; Copyright (C) 2013-2014 Free Software Foundation, Inc. ;; Authors: Matus Goljer ;; Magnar Sveen ;; Version: 1.2.0 ;; Package-Requires: ((dash "2.0.0") (emacs "24")) ;; Keywords: lisp functions combinators ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Collection of useful combinators for Emacs Lisp ;; ;; See documentation on https://github.com/magnars/dash.el#functions ;;; Code: (require 'dash) (defun -partial (fn &rest args) "Takes a function FN and fewer than the normal arguments to FN, and returns a fn that takes a variable number of additional ARGS. When called, the returned function calls FN with ARGS first and then additional args." (apply 'apply-partially fn args)) (defun -rpartial (fn &rest args) "Takes a function FN and fewer than the normal arguments to FN, and returns a fn that takes a variable number of additional ARGS. When called, the returned function calls FN with the additional args first and then ARGS." (lambda (&rest args-before) (apply fn (append args-before args)))) (defun -juxt (&rest fns) "Takes a list of functions and returns a fn that is the juxtaposition of those fns. The returned fn takes a variable number of args, and returns a list containing the result of applying each fn to the args (left-to-right)." (lambda (&rest args) (mapcar (lambda (x) (apply x args)) fns))) (defun -compose (&rest fns) "Takes a list of functions and returns a fn that is the composition of those fns. The returned fn takes a variable number of arguments, and returns the result of applying each fn to the result of applying the previous fn to the arguments (right-to-left)." (lambda (&rest args) (car (-reduce-r-from (lambda (fn xs) (list (apply fn xs))) args fns)))) (defun -applify (fn) "Changes an n-arity function FN to a 1-arity function that expects a list with n items as arguments" (apply-partially 'apply fn)) (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). In types: (b -> b -> c) -> (a -> b) -> a -> a -> c" (lambda (x y) (funcall operator (funcall transformer x) (funcall transformer y)))) (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))) (defun -const (c) "Return a function that returns C ignoring any additional arguments. In types: a -> b -> a" (lambda (&rest _) c)) (defmacro -cut (&rest params) "Take n-ary function and n arguments and specialize some of them. Arguments denoted by <> will be left unspecialized. See SRFI-26 for detailed description." (let* ((i 0) (args (mapcar (lambda (_) (setq i (1+ i)) (make-symbol (format "D%d" i))) (-filter (-partial 'eq '<>) params)))) `(lambda ,args ,(--map (if (eq it '<>) (pop args) it) params)))) (defun -not (pred) "Take an unary predicates PRED and return an unary predicate that returns t if PRED returns nil and nil if PRED returns non-nil." (lambda (x) (not (funcall pred x)))) (defun -orfn (&rest preds) "Take list of unary predicates PREDS and return an unary predicate with argument x that returns non-nil if at least one of the PREDS returns non-nil on x. In types: [a -> Bool] -> a -> Bool" (lambda (x) (-any? (-cut funcall <> x) preds))) (defun -andfn (&rest preds) "Take list of unary predicates PREDS and return an 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))) (defun -iteratefn (fn n) "Return a function FN composed N times with itself. FN is a unary function. If you need to use a function of higher arity, use `-applify' first to turn it into an unary function. With n = 0, this acts as identity function. In types: (a -> a) -> Int -> a -> a. This function satisfies the following law: (funcall (-iteratefn fn n) init) = (-last-item (-iterate fn init (1+ n)))." (lambda (x) (--dotimes n (setq x (funcall fn x))) x)) (defun -counter (&optional beg end inc) "Return a closure that counts from BEG to END, with increment INC. The closure will return the next value in the counting sequence each time it is called, and nil after END is reached. BEG defaults to 0, INC defaults to 1, and if END is nil, the counter will increment indefinitely. The closure accepts any number of arguments, which are discarded." (let ((inc (or inc 1)) (n (or beg 0))) (lambda (&rest _) (when (or (not end) (< n end)) (prog1 n (setq n (+ n inc))))))) (defvar -fixfn-max-iterations 1000 "The default maximum number of iterations performed by `-fixfn' unless otherwise specified.") (defun -fixfn (fn &optional equal-test halt-test) "Return a function that computes the (least) fixpoint of FN. FN must be a unary function. The returned lambda takes a single argument, X, the initial value for the fixpoint iteration. The iteration halts when either of the following conditions is satisified: 1. Iteration converges to the fixpoint, with equality being tested using EQUAL-TEST. If EQUAL-TEST is not specified, `equal' is used. For functions over the floating point numbers, it may be necessary to provide an appropriate appoximate comparsion test. 2. HALT-TEST returns a non-nil value. HALT-TEST defaults to a simple counter that returns t after `-fixfn-max-iterations', to guard against infinite iteration. Otherwise, HALT-TEST must be a function that accepts a single argument, the current value of X, and returns non-nil as long as iteration should continue. In this way, a more sophisticated convergence test may be supplied by the caller. The return value of the lambda is either the fixpoint or, if iteration halted before converging, a cons with car `halted' and cdr the final output from HALT-TEST. In types: (a -> a) -> a -> a." (let ((eqfn (or equal-test 'equal)) (haltfn (or halt-test (-not (-counter 0 -fixfn-max-iterations))))) (lambda (x) (let ((re (funcall fn x)) (halt? (funcall haltfn x))) (while (and (not halt?) (not (funcall eqfn x re))) (setq x re re (funcall fn re) halt? (funcall haltfn re))) (if halt? (cons 'halted halt?) re))))) (defun -prodfn (&rest fns) "Take a list of n functions and return a function that takes a list of length n, applying i-th function to i-th element of the input list. Returns a list of length n. In types (for n=2): ((a -> b), (c -> d)) -> (a, c) -> (b, d) This function satisfies the following laws: (-compose (-prodfn f g ...) (-prodfn f' g' ...)) = (-prodfn (-compose f f') (-compose g g') ...) (-prodfn f g ...) = (-juxt (-compose f (-partial 'nth 0)) (-compose g (-partial 'nth 1)) ...) (-compose (-prodfn f g ...) (-juxt f' g' ...)) = (-juxt (-compose f f') (-compose g g') ...) (-compose (-partial 'nth n) (-prod f1 f2 ...)) = (-compose fn (-partial 'nth n))" (lambda (x) (-zip-with 'funcall fns x))) (provide 'dash-functional) ;;; dash-functional.el ends here dash.el-2.12.1/dash-template.texi000066400000000000000000000232371261164447300165550ustar00rootroot00000000000000\input texinfo @c -*- texinfo -*- @c %**start of header @setfilename dash.info @settitle dash @documentencoding UTF-8 @documentlanguage en @syncodeindex fn cp @dircategory Emacs @direntry * Dash: (dash.info). A modern list library for GNU Emacs @end direntry @c %**end of header @copying This manual is for @code{dash.el} version 2.12.1. Copyright © 2012-2015 Magnar Sveen @quotation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see @uref{http://www.gnu.org/licenses/}. @end quotation @end copying @finalout @titlepage @title Dash @author Magnar Sveen @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top dash @insertcopying @end ifnottex @menu * Installation:: * Functions:: * Development:: * Index:: @detailmenu --- The Detailed Node Listing --- Installation * Using in a package:: * Syntax highlighting of dash functions:: Functions @c [[ function-nodes ]] Development * Contribute:: How to contribute * Changes:: List of significant changes by version * Contributors:: List of contributors @end detailmenu @end menu @node Installation @chapter Installation It's available on @uref{http://marmalade-repo.org/,marmalade} and @uref{http://melpa.milkbox.net/,Melpa}; use @code{M-x package-install}: @table @kbd @item M-x package-install @key{RET} dash Install the dash library. @end table @table @kbd @item M-x package-install @key{RET} dash-functional Optional, if you want the function combinators. @end table Alternatively, you can just dump @verb{~dash.el~} or @verb{~dash-functional.el~} in your load path somewhere. @menu * Using in a package:: * Syntax highlighting of dash functions:: @end menu @node Using in a package @section Using in a package Add this to the big comment block at the top: @lisp ;; Package-Requires: ((dash "2.12.1")) @end lisp @noindent To get function combinators: @lisp ;; Package-Requires: ((dash "2.12.1") (dash-functional "1.2.0") (emacs "24")) @end lisp @node Syntax highlighting of dash functions @section Syntax highlighting of dash functions Font lock of dash functions in emacs lisp buffers is now optional. Include this in your emacs settings to get syntax highlighting: @lisp (eval-after-load "dash" '(dash-enable-font-lock)) @end lisp @node Functions @chapter Functions This chapter contains reference documentation for the dash @abbr{application programming interface,API}. All functions and constructs in the library are prefixed with a dash (-). There are also anaphoric versions of functions where that makes sense, prefixed with two dashes instead of one. For instance, while @code{-map} takes a function to map over the list, one can also use the anaphoric form with double dashes - which will then be executed with @code{it} exposed as the list item. Here's an example: @lisp (-map (lambda (n) (* n n)) '(1 2 3 4)) ;; normal version (--map (* it it) '(1 2 3 4)) ;; anaphoric version @end lisp @noindent Of course, the original can also be written like @lisp (defun square (n) (* n n)) (-map 'square '(1 2 3 4)) @end lisp @noindent which demonstrates the usefulness of both versions. @menu @c [[ function-nodes ]] @end menu @c [[ function-docs ]] @node Development @chapter Development The dash repository is hosted on GitHub: @uref{https://github.com/magnars/dash.el} @menu * Contribute:: How to contribute * Changes:: List of significant changes by version * Contributors:: List of contributors @end menu @node Contribute @section Contribute Yes, please do. Pure functions in the list manipulation realm only, please. There's a suite of tests in @verb{~dev/examples.el~}, so remember to add tests for your function, or it might get broken later. Run the tests with @code{./run-tests.sh}. Create the docs with @code{./create-docs.sh}. I highly recommend that you install these as a pre-commit hook, so that the tests are always running and the docs are always in sync: @verbatim cp pre-commit.sh .git/hooks/pre-commit @end verbatim Oh, and don't edit @file{README.md} directly, it is auto-generated. Change @file{readme-template.md} or @file{examples-to-docs.el} instead. The same goes for the info manual. @node Changes @section Changes @noindent Changes in 2.10: @itemize @item Add @code{-let} destructuring to @code{-if-let} and @code{-when-let} (Fredrik Bergroth) @end itemize @noindent Changes in 2.9: @itemize @item Add @code{-let}, @code{-let*} and @code{-lambda} with destructuring @item Add @code{-tree-seq} and @code{-tree-map-nodes} @item Add @code{-non-nil} @item Add @code{-fix} @item Add @code{-fixfn} (dash-functional 1.2) @item Add @code{-copy} (Wilfred Hughes) @end itemize @noindent Changes in 2.8: @itemize @item Add @code{-butlast} @end itemize @noindent Changes in 2.7: @itemize @item @code{-zip} now supports more than two lists (Steve Lamb) @item Add @code{-cycle}, @code{-pad}, @code{-annotate}, @code{-zip-fill} (Steve Lamb) @item Add @code{-table}, @code{-table-flat} (finite cartesian product) @item Add @code{-flatten-n} @item @code{-slice} now supports "step" argument @item Add functional combinators @code{-iteratefn}, @code{-prodfn} @item Add @code{-replace}, @code{-splice}, @code{-splice-list} which generalize @code{-replace-at} and @code{-insert-at} @item Add @code{-compose}, @code{-iteratefn} and @code{-prodfn} (dash-functional 1.1) @end itemize @noindent Changes in 2.6: @itemize @item Add @code{-is-prefix-p}, @code{-is-suffix-p}, @code{-is-infix-p} (Matus Goljer) @item Add @code{-iterate}, @code{-unfold} (Matus Goljer) @item Add @code{-split-on}, @code{-split-when} (Matus Goljer) @item Add @code{-find-last-index} (Matus Goljer) @item Add @code{-list} (Johan Andersson) @end itemize @noindent Changes in 2.5: @itemize @item Add @code{-same-items?} (Johan Andersson) @item A few bugfixes @end itemize @noindent Changes in 2.4: @itemize @item Add @code{-snoc} (Matus Goljer) @item Add @code{-replace-at}, @code{-update-at}, @code{-remove-at}, and @code{-remove-at-indices} (Matus Goljer) @end itemize @noindent Changes in 2.3: @itemize @item Add tree operations (Matus Goljer) @item Make font-lock optional @end itemize @noindent Changes in 2.2: @itemize @item Add @code{-compose} (Christina Whyte) @end itemize @noindent Changes in 2.1: @itemize @item Add indexing operations (Matus Goljer) @end itemize @noindent Changes in 2.0: @itemize @item Split out @code{dash-functional.el} (Matus Goljer) @item Add @code{-andfn}, @code{-orfn}, @code{-not}, @code{-cut}, @code{-const}, @code{-flip} and @code{-on}. (Matus Goljer) @item Fix @code{-min}, @code{-max}, @code{-min-by} and @code{-max-by} (Matus Goljer) @end itemize @noindent Changes in 1.8: @itemize @item Add @code{-first-item} and @code{-last-item} (Wilfred Hughes) @end itemize @noindent Changes in 1.7: @itemize @item Add @code{-rotate} (Matus Goljer) @end itemize @noindent Changes in 1.6: @itemize @item Add @code{-min}, @code{-max}, @code{-min-by} and @code{-max-by} (Johan Andersson) @end itemize @noindent Changes in 1.5: @itemize @item Add @code{-sum} and @code{-product} (Johan Andersson) @end itemize @noindent Changes in 1.4: @itemize @item Add @code{-sort} @item Add @code{-reduce-r} (Matus Goljer) @item Add @code{-reduce-r-from} (Matus Goljer) @end itemize @noindent Changes in 1.3: @itemize @item Add @code{-partition-in-steps} @item Add @code{-partition-all-in-steps} @end itemize @noindent Changes in 1.2: @itemize @item Add @code{-last} (Matus Goljer) @item Add @code{-insert-at} (Emanuel Evans) @item Add @code{-when-let} and @code{-if-let} (Emanuel Evans) @item Add @code{-when-let*} and @code{-if-let*} (Emanuel Evans) @item Some bugfixes @end itemize @node Contributors @section Contributors @itemize @item @uref{https://github.com/Fuco1,Matus Goljer} contributed lots of features and functions. @item @uref{https://github.com/tkf,Takafumi Arakaki} contributed @code{-group-by}. @item @uref{https://github.com/tali713,tali713} is the author of @code{-applify}. @item @uref{https://github.com/vemv,Víctor M. Valenzuela} contributed @code{-repeat}. @item @uref{https://github.com/nicferrier,Nic Ferrier} contributed @code{-cons*}. @item @uref{https://github.com/Wilfred,Wilfred Hughes} contributed @code{-slice}, @code{-first-item} and @code{-last-item}. @item @uref{https://github.com/shosti,Emanuel Evans} contributed @code{-if-let}, @code{-when-let} and @code{-insert-at}. @item @uref{https://github.com/rejeep,Johan Andersson} contributed @code{-sum}, @code{-product} and @code{-same-items?} @item @uref{https://github.com/kurisuwhyte,Christina Whyte} contributed @code{-compose} @item @uref{https://github.com/steventlamb,Steve Lamb} contributed @code{-cycle}, @code{-pad}, @code{-annotate}, @code{-zip-fill} and an n-ary version of @code{-zip}. @item @uref{https://github.com/fbergroth,Fredrik Bergroth} made the @code{-if-let} family use @code{-let} destructuring and improved script for generating documentation. @item @uref{https://github.com/holomorph,Mark Oteiza} contributed the script to create an info manual. @item @uref{https://github.com/wasamasa,Vasilij Schneidermann} contributed @code{-some}. @item @uref{https://github.com/occidens,William West} made @code{-fixfn} more robust at handling floats. @end itemize Thanks! @node Index @unnumbered Index @printindex cp @bye dash.el-2.12.1/dash.el000066400000000000000000002411741261164447300143750ustar00rootroot00000000000000;;; dash.el --- A modern list library for Emacs -*- lexical-binding: t -*- ;; Copyright (C) 2012-2015 Free Software Foundation, Inc. ;; Author: Magnar Sveen ;; Version: 2.12.1 ;; Keywords: lists ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; A modern list api for Emacs. ;; ;; See documentation on https://github.com/magnars/dash.el#functions ;; ;; **Please note** The lexical binding in this file is not utilised at the ;; moment. We will take full advantage of lexical binding in an upcoming 3.0 ;; release of Dash. In the meantime, we've added the pragma to avoid a bug that ;; you can read more about in https://github.com/magnars/dash.el/issues/130. ;; ;;; Code: (defgroup dash () "Customize group for dash.el" :group 'lisp :prefix "dash-") (defun dash--enable-fontlock (symbol value) (when value (dash-enable-font-lock)) (set-default symbol value)) (defcustom dash-enable-fontlock nil "If non-nil, enable fontification of dash functions, macros and special values." :type 'boolean :set 'dash--enable-fontlock :group 'dash) (defmacro !cons (car cdr) "Destructive: Set CDR to the cons of CAR and CDR." `(setq ,cdr (cons ,car ,cdr))) (defmacro !cdr (list) "Destructive: Set LIST to the cdr of LIST." `(setq ,list (cdr ,list))) (defmacro --each (list &rest body) "Anaphoric form of `-each'." (declare (debug (form body)) (indent 1)) (let ((l (make-symbol "list"))) `(let ((,l ,list) (it-index 0)) (while ,l (let ((it (car ,l))) ,@body) (setq it-index (1+ it-index)) (!cdr ,l))))) (defun -each (list fn) "Call FN with every item in LIST. Return nil, used for side-effects only." (--each list (funcall fn it))) (put '-each 'lisp-indent-function 1) (defmacro --each-while (list pred &rest body) "Anaphoric form of `-each-while'." (declare (debug (form form body)) (indent 2)) (let ((l (make-symbol "list")) (c (make-symbol "continue"))) `(let ((,l ,list) (,c t) (it-index 0)) (while (and ,l ,c) (let ((it (car ,l))) (if (not ,pred) (setq ,c nil) ,@body)) (setq it-index (1+ it-index)) (!cdr ,l))))) (defun -each-while (list pred fn) "Call FN with every item in LIST while (PRED item) is non-nil. Return nil, used for side-effects only." (--each-while list (funcall pred it) (funcall fn it))) (put '-each-while 'lisp-indent-function 2) (defmacro --dotimes (num &rest body) "Repeatedly executes BODY (presumably for side-effects) with `it` bound to integers from 0 through NUM-1." (declare (debug (form body)) (indent 1)) (let ((n (make-symbol "num"))) `(let ((,n ,num) (it 0)) (while (< it ,n) ,@body (setq it (1+ it)))))) (defun -dotimes (num fn) "Repeatedly calls FN (presumably for side-effects) passing in integers from 0 through NUM-1." (--dotimes num (funcall fn it))) (put '-dotimes 'lisp-indent-function 1) (defun -map (fn list) "Return a new list consisting of the result of applying FN to the items in LIST." (mapcar fn list)) (defmacro --map (form list) "Anaphoric form of `-map'." (declare (debug (form form))) `(mapcar (lambda (it) ,form) ,list)) (defmacro --reduce-from (form initial-value list) "Anaphoric form of `-reduce-from'." (declare (debug (form form form))) `(let ((acc ,initial-value)) (--each ,list (setq acc ,form)) acc)) (defun -reduce-from (fn initial-value list) "Return the result of applying FN to INITIAL-VALUE and the first item in LIST, then applying FN to that result and the 2nd item, etc. If LIST contains no items, return INITIAL-VALUE and FN is not called. In the anaphoric form `--reduce-from', the accumulated value is exposed as `acc`. See also: `-reduce', `-reduce-r'" (--reduce-from (funcall fn acc it) initial-value list)) (defmacro --reduce (form list) "Anaphoric form of `-reduce'." (declare (debug (form form))) (let ((lv (make-symbol "list-value"))) `(let ((,lv ,list)) (if ,lv (--reduce-from ,form (car ,lv) (cdr ,lv)) (let (acc it) ,form))))) (defun -reduce (fn list) "Return the result of applying FN to the first 2 items in LIST, then applying FN to that result and the 3rd item, etc. If LIST contains no items, FN must accept no arguments as well, and reduce return the result of calling FN with no arguments. If LIST has only 1 item, it is returned and FN is not called. In the anaphoric form `--reduce', the accumulated value is exposed as `acc`. See also: `-reduce-from', `-reduce-r'" (if list (-reduce-from fn (car list) (cdr list)) (funcall fn))) (defun -reduce-r-from (fn initial-value list) "Replace conses with FN, nil with INITIAL-VALUE and evaluate the resulting expression. If LIST is empty, INITIAL-VALUE is returned and FN is not called. Note: this function works the same as `-reduce-from' but the operation associates from right instead of from left. See also: `-reduce-r', `-reduce'" (if (not list) initial-value (funcall fn (car list) (-reduce-r-from fn initial-value (cdr list))))) (defmacro --reduce-r-from (form initial-value list) "Anaphoric version of `-reduce-r-from'." (declare (debug (form form form))) `(-reduce-r-from (lambda (&optional it acc) ,form) ,initial-value ,list)) (defun -reduce-r (fn list) "Replace conses with FN and evaluate the resulting expression. The final nil is ignored. If LIST contains no items, FN must accept no arguments as well, and reduce return the result of calling FN with no arguments. If LIST has only 1 item, it is returned and FN is not called. The first argument of FN is the new item, the second is the accumulated value. Note: this function works the same as `-reduce' but the operation associates from right instead of from left. See also: `-reduce-r-from', `-reduce'" (cond ((not list) (funcall fn)) ((not (cdr list)) (car list)) (t (funcall fn (car list) (-reduce-r fn (cdr list)))))) (defmacro --reduce-r (form list) "Anaphoric version of `-reduce-r'." (declare (debug (form form))) `(-reduce-r (lambda (&optional it acc) ,form) ,list)) (defmacro --filter (form list) "Anaphoric form of `-filter'." (declare (debug (form form))) (let ((r (make-symbol "result"))) `(let (,r) (--each ,list (when ,form (!cons it ,r))) (nreverse ,r)))) (defun -filter (pred list) "Return a new list of the items in LIST for which PRED returns a non-nil value. Alias: `-select'" (--filter (funcall pred it) list)) (defalias '-select '-filter) (defalias '--select '--filter) (defmacro --remove (form list) "Anaphoric form of `-remove'." (declare (debug (form form))) `(--filter (not ,form) ,list)) (defun -remove (pred list) "Return a new list of the items in LIST for which PRED returns nil. Alias: `-reject'" (--remove (funcall pred it) list)) (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'." (declare (debug (form form))) `(-remove-first (lambda (it) ,form) ,list)) (defalias '-reject-first '-remove-first) (defalias '--reject-first '--remove-first) (defun -remove-last (pred list) "Return a new list with the last item matching PRED removed. Alias: `-reject-last' See also: `-remove', `-map-last'" (nreverse (-remove-first pred (nreverse list)))) (defmacro --remove-last (form list) "Anaphoric form of `-remove-last'." (declare (debug (form form))) `(-remove-last (lambda (it) ,form) ,list)) (defalias '-reject-last '-remove-last) (defalias '--reject-last '--remove-last) (defun -remove-item (item list) "Remove all occurences of ITEM from LIST. Comparison is done with `equal'." (--remove (equal it item) list)) (defmacro --keep (form list) "Anaphoric form of `-keep'." (declare (debug (form form))) (let ((r (make-symbol "result")) (m (make-symbol "mapped"))) `(let (,r) (--each ,list (let ((,m ,form)) (when ,m (!cons ,m ,r)))) (nreverse ,r)))) (defun -keep (fn list) "Return a new list of the non-nil results of applying FN to the items in LIST. If you want to select the original items satisfying a predicate use `-filter'." (--keep (funcall fn it) list)) (defun -non-nil (list) "Return all non-nil elements of LIST." (-remove 'null list)) (defmacro --map-indexed (form list) "Anaphoric form of `-map-indexed'." (declare (debug (form form))) (let ((r (make-symbol "result"))) `(let (,r) (--each ,list (!cons ,form ,r)) (nreverse ,r)))) (defun -map-indexed (fn list) "Return a new list consisting of the result of (FN index item) for each item in LIST. In the anaphoric form `--map-indexed', the index is exposed as `it-index`." (--map-indexed (funcall fn it-index it) list)) (defmacro --map-when (pred rep list) "Anaphoric form of `-map-when'." (declare (debug (form form form))) (let ((r (make-symbol "result"))) `(let (,r) (--each ,list (!cons (if ,pred ,rep it) ,r)) (nreverse ,r)))) (defun -map-when (pred rep list) "Return a new list where the elements in LIST that does not match the PRED function are unchanged, and where the elements in LIST that do match the PRED function are mapped through the REP function. Alias: `-replace-where' See also: `-update-at'" (--map-when (funcall pred it) (funcall rep it) list)) (defalias '-replace-where '-map-when) (defalias '--replace-where '--map-when) (defun -map-first (pred rep list) "Replace first item in LIST satisfying PRED with result of REP called on this item. See also: `-map-when', `-replace-first'" (let (front) (while (and list (not (funcall pred (car list)))) (push (car list) front) (!cdr list)) (if list (-concat (nreverse front) (cons (funcall rep (car list)) (cdr list))) (nreverse front)))) (defmacro --map-first (pred rep list) "Anaphoric form of `-map-first'." `(-map-first (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) (defun -map-last (pred rep list) "Replace first item in LIST satisfying PRED with result of REP called on this item. See also: `-map-when', `-replace-last'" (nreverse (-map-first pred rep (nreverse list)))) (defmacro --map-last (pred rep list) "Anaphoric form of `-map-last'." `(-map-last (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) (defun -replace (old new list) "Replace all OLD items in LIST with NEW. Elements are compared using `equal'. See also: `-replace-at'" (--map-when (equal it old) new list)) (defun -replace-first (old new list) "Replace the first occurence of OLD with NEW in LIST. Elements are compared using `equal'. See also: `-map-first'" (--map-first (equal old it) new list)) (defun -replace-last (old new list) "Replace the last occurence of OLD with NEW in LIST. Elements are compared using `equal'. See also: `-map-last'" (--map-last (equal old it) new list)) (defmacro --mapcat (form list) "Anaphoric form of `-mapcat'." (declare (debug (form form))) `(apply 'append (--map ,form ,list))) (defun -mapcat (fn list) "Return the concatenation of the result of mapping FN over LIST. Thus function FN should return a list." (--mapcat (funcall fn it) list)) (defun -flatten (l) "Take a nested list L and return its contents as a single, flat list. Note that because `nil' represents a list of zero elements (an empty list), any mention of nil in L will disappear after flattening. If you need to preserve nils, consider `-flatten-n' or map them to some unique symbol and then map them back. Conses of two atoms are considered \"terminals\", that is, they aren't flattened further. See also: `-flatten-n'" (if (and (listp l) (listp (cdr l))) (-mapcat '-flatten l) (list l))) (defmacro --iterate (form init n) "Anaphoric version of `-iterate'." (declare (debug (form form form))) `(-iterate (lambda (it) ,form) ,init ,n)) (defun -flatten-n (num list) "Flatten NUM levels of a nested LIST. See also: `-flatten'" (-last-item (--iterate (--mapcat (-list it) it) list (1+ num)))) (defun -concat (&rest lists) "Return a new list with the concatenation of the elements in the supplied LISTS." (apply 'append lists)) (defalias '-copy 'copy-sequence "Create a shallow copy of LIST.") (defun -splice (pred fun list) "Splice lists generated by FUN in place of elements matching PRED in LIST. FUN takes the element matching PRED as input. This function can be used as replacement for `,@' in case you need to splice several lists at marked positions (for example with keywords). See also: `-splice-list', `-insert-at'" (let (r) (--each list (if (funcall pred it) (let ((new (funcall fun it))) (--each new (!cons it r))) (!cons it r))) (nreverse r))) (defmacro --splice (pred form list) "Anaphoric form of `-splice'." `(-splice (lambda (it) ,pred) (lambda (it) ,form) ,list)) (defun -splice-list (pred new-list list) "Splice NEW-LIST in place of elements matching PRED in LIST. See also: `-splice', `-insert-at'" (-splice pred (lambda (_) new-list) list)) (defmacro --splice-list (pred new-list list) "Anaphoric form of `-splice-list'." `(-splice-list (lambda (it) ,pred) ,new-list ,list)) (defun -cons* (&rest args) "Make a new list from the elements of ARGS. The last 2 members of ARGS are used as the final cons of the result so if the final member of ARGS is not a list the result is a dotted list." (-reduce-r 'cons args)) (defun -snoc (list elem &rest elements) "Append ELEM to the end of the list. This is like `cons', but operates on the end of list. If ELEMENTS is non nil, append these to the list as well." (-concat list (list elem) elements)) (defmacro --first (form list) "Anaphoric form of `-first'." (declare (debug (form form))) (let ((n (make-symbol "needle"))) `(let (,n) (--each-while ,list (not ,n) (when ,form (setq ,n it))) ,n))) (defun -first (pred list) "Return the first x in LIST where (PRED x) is non-nil, else nil. To get the first item in the list no questions asked, use `car'. Alias: `-find'" (--first (funcall pred it) list)) (defalias '-find '-first) (defalias '--find '--first) (defmacro --some (form list) "Anaphoric form of `-some'." (declare (debug (form form))) (let ((n (make-symbol "needle"))) `(let (,n) (--each-while ,list (not ,n) (setq ,n ,form)) ,n))) (defun -some (pred list) "Return (PRED x) for the first LIST item where (PRED x) is non-nil, else nil. Alias: `-any'" (--some (funcall pred it) list)) (defalias '-any '-some) (defalias '--any '--some) (defmacro --last (form list) "Anaphoric form of `-last'." (declare (debug (form form))) (let ((n (make-symbol "needle"))) `(let (,n) (--each ,list (when ,form (setq ,n it))) ,n))) (defun -last (pred list) "Return the last x in LIST where (PRED x) is non-nil, else nil." (--last (funcall pred it) list)) (defalias '-first-item 'car "Return the first item of LIST, or nil on an empty list.") (defun -last-item (list) "Return the last item of LIST, or nil on an empty list." (car (last list))) (defun -butlast (list) "Return a list of all items in list except for the last." (let (result) (while (cdr list) (!cons (car list) result) (!cdr list)) (nreverse result))) (defmacro --count (pred list) "Anaphoric form of `-count'." (declare (debug (form form))) (let ((r (make-symbol "result"))) `(let ((,r 0)) (--each ,list (when ,pred (setq ,r (1+ ,r)))) ,r))) (defun -count (pred list) "Counts the number of items in LIST where (PRED item) is non-nil." (--count (funcall pred it) list)) (defun ---truthy? (val) (not (null val))) (defmacro --any? (form list) "Anaphoric form of `-any?'." (declare (debug (form form))) `(---truthy? (--first ,form ,list))) (defun -any? (pred list) "Return t if (PRED x) is non-nil for any x in LIST, else nil. Alias: `-any-p', `-some?', `-some-p'" (--any? (funcall pred it) list)) (defalias '-some? '-any?) (defalias '--some? '--any?) (defalias '-any-p '-any?) (defalias '--any-p '--any?) (defalias '-some-p '-any?) (defalias '--some-p '--any?) (defmacro --all? (form list) "Anaphoric form of `-all?'." (declare (debug (form form))) (let ((a (make-symbol "all"))) `(let ((,a t)) (--each-while ,list ,a (setq ,a ,form)) (---truthy? ,a)))) (defun -all? (pred list) "Return t if (PRED x) is non-nil for all x in LIST, else nil. Alias: `-all-p', `-every?', `-every-p'" (--all? (funcall pred it) list)) (defalias '-every? '-all?) (defalias '--every? '--all?) (defalias '-all-p '-all?) (defalias '--all-p '--all?) (defalias '-every-p '-all?) (defalias '--every-p '--all?) (defmacro --none? (form list) "Anaphoric form of `-none?'." (declare (debug (form form))) `(--all? (not ,form) ,list)) (defun -none? (pred list) "Return t if (PRED x) is nil for all x in LIST, else nil. Alias: `-none-p'" (--none? (funcall pred it) list)) (defalias '-none-p '-none?) (defalias '--none-p '--none?) (defmacro --only-some? (form list) "Anaphoric form of `-only-some?'." (declare (debug (form form))) (let ((y (make-symbol "yes")) (n (make-symbol "no"))) `(let (,y ,n) (--each-while ,list (not (and ,y ,n)) (if ,form (setq ,y t) (setq ,n t))) (---truthy? (and ,y ,n))))) (defun -only-some? (pred list) "Return `t` if at least one item of LIST matches PRED and at least one item of LIST does not match PRED. Return `nil` both if all items match the predicate or if none of the items match the predicate. Alias: `-only-some-p'" (--only-some? (funcall pred it) list)) (defalias '-only-some-p '-only-some?) (defalias '--only-some-p '--only-some?) (defun -slice (list from &optional to step) "Return copy of LIST, starting from index FROM to index TO. FROM or TO may be negative. These values are then interpreted modulo the length of the list. If STEP is a number, only each STEPth item in the resulting section is returned. Defaults to 1." (let ((length (length list)) (new-list nil)) ;; to defaults to the end of the list (setq to (or to length)) (setq step (or step 1)) ;; handle negative indices (when (< from 0) (setq from (mod from length))) (when (< to 0) (setq to (mod to length))) ;; iterate through the list, keeping the elements we want (--each-while list (< it-index to) (when (and (>= it-index from) (= (mod (- from it-index) step) 0)) (push it new-list))) (nreverse new-list))) (defun -take (n list) "Return a new list of the first N items in LIST, or all items if there are fewer than N." (let (result) (--dotimes n (when list (!cons (car list) result) (!cdr list))) (nreverse result))) (defalias '-drop 'nthcdr "Return the tail of LIST without the first N items.") (defmacro --take-while (form list) "Anaphoric form of `-take-while'." (declare (debug (form form))) (let ((r (make-symbol "result"))) `(let (,r) (--each-while ,list ,form (!cons it ,r)) (nreverse ,r)))) (defun -take-while (pred list) "Return a new list of successive items from LIST while (PRED item) returns a non-nil value." (--take-while (funcall pred it) list)) (defmacro --drop-while (form list) "Anaphoric form of `-drop-while'." (declare (debug (form form))) (let ((l (make-symbol "list"))) `(let ((,l ,list)) (while (and ,l (let ((it (car ,l))) ,form)) (!cdr ,l)) ,l))) (defun -drop-while (pred list) "Return the tail of LIST starting from the first item for which (PRED item) returns nil." (--drop-while (funcall pred it) list)) (defun -split-at (n list) "Return a list of ((-take N LIST) (-drop N LIST)), in no more than one pass through the list." (let (result) (--dotimes n (when list (!cons (car list) result) (!cdr list))) (list (nreverse result) list))) (defun -rotate (n list) "Rotate LIST N places to the right. With N negative, rotate to the left. The time complexity is O(n)." (if (> n 0) (append (last list n) (butlast list n)) (append (-drop (- n) list) (-take (- n) list)))) (defun -insert-at (n x list) "Return a list with X inserted into LIST at position N. See also: `-splice', `-splice-list'" (let ((split-list (-split-at n list))) (nconc (car split-list) (cons x (cadr split-list))))) (defun -replace-at (n x list) "Return a list with element at Nth position in LIST replaced with X. See also: `-replace'" (let ((split-list (-split-at n list))) (nconc (car split-list) (cons x (cdr (cadr split-list)))))) (defun -update-at (n func list) "Return a list with element at Nth position in LIST replaced with `(func (nth n list))`. See also: `-map-when'" (let ((split-list (-split-at n list))) (nconc (car split-list) (cons (funcall func (car (cadr split-list))) (cdr (cadr split-list)))))) (defmacro --update-at (n form list) "Anaphoric version of `-update-at'." (declare (debug (form form form))) `(-update-at ,n (lambda (it) ,form) ,list)) (defun -remove-at (n list) "Return a list with element at Nth position in LIST removed. See also: `-remove-at-indices', `-remove'" (-remove-at-indices (list n) list)) (defun -remove-at-indices (indices list) "Return a list whose elements are elements from LIST without elements selected as `(nth i list)` for all i from INDICES. See also: `-remove-at', `-remove'" (let* ((indices (-sort '< indices)) (diffs (cons (car indices) (-map '1- (-zip-with '- (cdr indices) indices)))) r) (--each diffs (let ((split (-split-at it list))) (!cons (car split) r) (setq list (cdr (cadr split))))) (!cons list r) (apply '-concat (nreverse r)))) (defmacro --split-with (pred list) "Anaphoric form of `-split-with'." (declare (debug (form form))) (let ((l (make-symbol "list")) (r (make-symbol "result")) (c (make-symbol "continue"))) `(let ((,l ,list) (,r nil) (,c t)) (while (and ,l ,c) (let ((it (car ,l))) (if (not ,pred) (setq ,c nil) (!cons it ,r) (!cdr ,l)))) (list (nreverse ,r) ,l)))) (defun -split-with (pred list) "Return a list of ((-take-while PRED LIST) (-drop-while PRED LIST)), in no more than one pass through the list." (--split-with (funcall pred it) list)) (defmacro -split-on (item list) "Split the LIST each time ITEM is found. Unlike `-partition-by', the ITEM is discarded from the results. Empty lists are also removed from the result. Comparison is done by `equal'. See also `-split-when'" (declare (debug (form form))) `(-split-when (lambda (it) (equal it ,item)) ,list)) (defmacro --split-when (form list) "Anaphoric version of `-split-when'." (declare (debug (form form))) `(-split-when (lambda (it) ,form) ,list)) (defun -split-when (fn list) "Split the LIST on each element where FN returns non-nil. Unlike `-partition-by', the \"matched\" element is discarded from the results. Empty lists are also removed from the result. This function can be thought of as a generalization of `split-string'." (let (r s) (while list (if (not (funcall fn (car list))) (push (car list) s) (when s (push (nreverse s) r)) (setq s nil)) (!cdr list)) (when s (push (nreverse s) r)) (nreverse r))) (defmacro --separate (form list) "Anaphoric form of `-separate'." (declare (debug (form form))) (let ((y (make-symbol "yes")) (n (make-symbol "no"))) `(let (,y ,n) (--each ,list (if ,form (!cons it ,y) (!cons it ,n))) (list (nreverse ,y) (nreverse ,n))))) (defun -separate (pred list) "Return a list of ((-filter PRED LIST) (-remove PRED LIST)), in one pass through the list." (--separate (funcall pred it) list)) (defun ---partition-all-in-steps-reversed (n step list) "Private: Used by -partition-all-in-steps and -partition-in-steps." (when (< step 1) (error "Step must be a positive number, or you're looking at some juicy infinite loops.")) (let ((result nil)) (while list (!cons (-take n list) result) (setq list (-drop step list))) result)) (defun -partition-all-in-steps (n step list) "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. The last groups may contain less than N items." (nreverse (---partition-all-in-steps-reversed n step list))) (defun -partition-in-steps (n step list) "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. If there are not enough items to make the last group N-sized, those items are discarded." (let ((result (---partition-all-in-steps-reversed n step list))) (while (and result (< (length (car result)) n)) (!cdr result)) (nreverse result))) (defun -partition-all (n list) "Return a new list with the items in LIST grouped into N-sized sublists. The last group may contain less than N items." (-partition-all-in-steps n n list)) (defun -partition (n list) "Return a new list with the items in LIST grouped into N-sized sublists. If there are not enough items to make the last group N-sized, those items are discarded." (-partition-in-steps n n list)) (defmacro --partition-by (form list) "Anaphoric form of `-partition-by'." (declare (debug (form form))) (let ((r (make-symbol "result")) (s (make-symbol "sublist")) (v (make-symbol "value")) (n (make-symbol "new-value")) (l (make-symbol "list"))) `(let ((,l ,list)) (when ,l (let* ((,r nil) (it (car ,l)) (,s (list it)) (,v ,form) (,l (cdr ,l))) (while ,l (let* ((it (car ,l)) (,n ,form)) (unless (equal ,v ,n) (!cons (nreverse ,s) ,r) (setq ,s nil) (setq ,v ,n)) (!cons it ,s) (!cdr ,l))) (!cons (nreverse ,s) ,r) (nreverse ,r)))))) (defun -partition-by (fn list) "Apply FN to each item in LIST, splitting it each time FN returns a new value." (--partition-by (funcall fn it) list)) (defmacro --partition-by-header (form list) "Anaphoric form of `-partition-by-header'." (declare (debug (form form))) (let ((r (make-symbol "result")) (s (make-symbol "sublist")) (h (make-symbol "header-value")) (b (make-symbol "seen-body?")) (n (make-symbol "new-value")) (l (make-symbol "list"))) `(let ((,l ,list)) (when ,l (let* ((,r nil) (it (car ,l)) (,s (list it)) (,h ,form) (,b nil) (,l (cdr ,l))) (while ,l (let* ((it (car ,l)) (,n ,form)) (if (equal ,h ,n) (when ,b (!cons (nreverse ,s) ,r) (setq ,s nil) (setq ,b nil)) (setq ,b t)) (!cons it ,s) (!cdr ,l))) (!cons (nreverse ,s) ,r) (nreverse ,r)))))) (defun -partition-by-header (fn list) "Apply FN to the first item in LIST. That is the header value. Apply FN to each item in LIST, splitting it each time FN returns the header value, but only after seeing at least one other value (the body)." (--partition-by-header (funcall fn it) list)) (defmacro --group-by (form list) "Anaphoric form of `-group-by'." (declare (debug t)) (let ((n (make-symbol "n")) (k (make-symbol "k")) (grp (make-symbol "grp"))) `(nreverse (-map (lambda (,n) (cons (car ,n) (nreverse (cdr ,n)))) (--reduce-from (let* ((,k (,@form)) (,grp (assoc ,k acc))) (if ,grp (setcdr ,grp (cons it (cdr ,grp))) (push (list ,k it) acc)) acc) nil ,list))))) (defun -group-by (fn list) "Separate LIST into an alist whose keys are FN applied to the elements of LIST. Keys are compared by `equal'." (--group-by (funcall fn it) list)) (defun -interpose (sep list) "Return a new list of all elements in LIST separated by SEP." (let (result) (when list (!cons (car list) result) (!cdr list)) (while list (setq result (cons (car list) (cons sep result))) (!cdr list)) (nreverse result))) (defun -interleave (&rest lists) "Return a new list of the first item in each list, then the second etc." (let (result) (while (-none? 'null lists) (--each lists (!cons (car it) result)) (setq lists (-map 'cdr lists))) (nreverse result))) (defmacro --zip-with (form list1 list2) "Anaphoric form of `-zip-with'. The elements in list1 is bound as `it`, the elements in list2 as `other`." (declare (debug (form form form))) (let ((r (make-symbol "result")) (l1 (make-symbol "list1")) (l2 (make-symbol "list2"))) `(let ((,r nil) (,l1 ,list1) (,l2 ,list2)) (while (and ,l1 ,l2) (let ((it (car ,l1)) (other (car ,l2))) (!cons ,form ,r) (!cdr ,l1) (!cdr ,l2))) (nreverse ,r)))) (defun -zip-with (fn list1 list2) "Zip the two lists LIST1 and LIST2 using a function FN. This function is applied pairwise taking as first argument element of LIST1 and as second argument element of LIST2 at corresponding position. The anaphoric form `--zip-with' binds the elements from LIST1 as `it`, and the elements from LIST2 as `other`." (--zip-with (funcall fn it other) list1 list2)) (defun -zip (&rest lists) "Zip LISTS together. Group the head of each list, followed by the second elements of each list, and so on. The lengths of the returned groupings are equal to the length of the shortest input list. If two lists are provided as arguments, return the groupings as a list of cons cells. Otherwise, return the groupings as a list of lists. Please note! This distinction is being removed in an upcoming 2.0 release of Dash. If you rely on this behavior, use -zip-pair instead." (let (results) (while (-none? 'null lists) (setq results (cons (mapcar 'car lists) results)) (setq lists (mapcar 'cdr lists))) (setq results (nreverse results)) (if (= (length lists) 2) ;; to support backward compatability, return ;; a cons cell if two lists were provided (--map (cons (car it) (cadr it)) results) results))) (defalias '-zip-pair '-zip) (defun -zip-fill (fill-value &rest lists) "Zip LISTS, with FILL-VALUE padded onto the shorter lists. The lengths of the returned groupings are equal to the length of the longest input list." (apply '-zip (apply '-pad (cons fill-value lists)))) (defun -cycle (list) "Return an infinite copy of LIST that will cycle through the elements and repeat from the beginning." (let ((newlist (-map 'identity list))) (nconc newlist newlist))) (defun -pad (fill-value &rest lists) "Appends FILL-VALUE to the end of each list in LISTS such that they will all have the same length." (let* ((annotations (-annotate 'length lists)) (n (-max (-map 'car annotations)))) (--map (append (cdr it) (-repeat (- n (car it)) fill-value)) annotations))) (defun -annotate (fn list) "Return a list of cons cells where each cell is FN applied to each element of LIST paired with the unmodified element of LIST." (-zip (-map fn list) list)) (defmacro --annotate (form list) "Anaphoric version of `-annotate'." (declare (debug (form form))) `(-annotate (lambda (it) ,form) ,list)) (defun dash--table-carry (lists restore-lists &optional re) "Helper for `-table' and `-table-flat'. If a list overflows, carry to the right and reset the list." (while (not (or (car lists) (equal lists '(nil)))) (setcar lists (car restore-lists)) (pop (cadr lists)) (!cdr lists) (!cdr restore-lists) (when re (push (nreverse (car re)) (cadr re)) (setcar re nil) (!cdr re)))) (defun -table (fn &rest lists) "Compute outer product of LISTS using function FN. The function FN should have the same arity as the number of supplied lists. The outer product is computed by applying fn to all possible combinations created by taking one element from each list in order. The dimension of the result is (length lists). See also: `-table-flat'" (let ((restore-lists (copy-sequence lists)) (last-list (last lists)) (re (make-list (length lists) nil))) (while (car last-list) (let ((item (apply fn (-map 'car lists)))) (push item (car re)) (setcar lists (cdar lists)) ;; silence byte compiler (dash--table-carry lists restore-lists re))) (nreverse (car (last re))))) (defun -table-flat (fn &rest lists) "Compute flat outer product of LISTS using function FN. The function FN should have the same arity as the number of supplied lists. The outer product is computed by applying fn to all possible combinations created by taking one element from each list in order. The results are flattened, ignoring the tensor structure of the result. This is equivalent to calling: (-flatten-n (1- (length lists)) (-table fn lists)) but the implementation here is much more efficient. See also: `-flatten-n', `-table'" (when lists ;Just in case. (let* ((list1 (pop lists)) (restore-lists (copy-sequence lists)) (last-list (last lists)) re) (while (car last-list) (let ((tail (-map #'car lists))) (dolist (head list1) (push (apply fn head tail) re))) (pop (car lists)) (dash--table-carry lists restore-lists)) (nreverse re)))) (defun -partial (fn &rest args) "Take a function FN and fewer than the normal arguments to FN, and return a fn that takes a variable number of additional ARGS. When called, the returned function calls FN with ARGS first and then additional args." (apply 'apply-partially fn args)) (defun -elem-index (elem list) "Return the index of the first element in the given LIST which is equal to the query element ELEM, or nil if there is no such element." (car (-elem-indices elem list))) (defun -elem-indices (elem list) "Return the indices of all elements in LIST equal to the query element ELEM, in ascending order." (-find-indices (-partial 'equal elem) list)) (defun -find-indices (pred list) "Return the indices of all elements in LIST satisfying the predicate PRED, in ascending order." (apply 'append (--map-indexed (when (funcall pred it) (list it-index)) list))) (defmacro --find-indices (form list) "Anaphoric version of `-find-indices'." (declare (debug (form form))) `(-find-indices (lambda (it) ,form) ,list)) (defun -find-index (pred list) "Take a predicate PRED and a LIST and return the index of the first element in the list satisfying the predicate, or nil if there is no such element." (car (-find-indices pred list))) (defmacro --find-index (form list) "Anaphoric version of `-find-index'." (declare (debug (form form))) `(-find-index (lambda (it) ,form) ,list)) (defun -find-last-index (pred list) "Take a predicate PRED and a LIST and return the index of the last element in the list satisfying the predicate, or nil if there is no such element." (-last-item (-find-indices pred list))) (defmacro --find-last-index (form list) "Anaphoric version of `-find-last-index'." `(-find-last-index (lambda (it) ,form) ,list)) (defun -select-by-indices (indices list) "Return a list whose elements are elements from LIST selected as `(nth i list)` for all i from INDICES." (let (r) (--each indices (!cons (nth it list) r)) (nreverse r))) (defmacro -> (x &optional form &rest more) "Thread the expr through the forms. Insert X as the second item in the first form, making a list of it if it is not a list already. If there are more forms, insert the first form as the second item in second form, etc." (cond ((null form) x) ((null more) (if (listp form) `(,(car form) ,x ,@(cdr form)) (list form x))) (:else `(-> (-> ,x ,form) ,@more)))) (defmacro ->> (x &optional form &rest more) "Thread the expr through the forms. Insert X as the last item in the first form, making a list of it if it is not a list already. If there are more forms, insert the first form as the last item in second form, etc." (cond ((null form) x) ((null more) (if (listp form) `(,@form ,x) (list form x))) (:else `(->> (->> ,x ,form) ,@more)))) (defmacro --> (x form &rest more) "Thread the expr through the forms. Insert X at the position signified by the token `it' in the first form. If there are more forms, insert the first form at the position signified by `it' in in second form, etc." (if (null more) (if (listp form) (--map-when (eq it 'it) x form) (list form x)) `(--> (--> ,x ,form) ,@more))) (defmacro -some-> (x &optional form &rest more) "When expr is non-nil, thread it through the first form (via `->'), and when that result is non-nil, through the next form, etc." (if (null form) x (let ((result (make-symbol "result"))) `(-some-> (-when-let (,result ,x) (-> ,result ,form)) ,@more)))) (defmacro -some->> (x &optional form &rest more) "When expr is non-nil, thread it through the first form (via `->>'), and when that result is non-nil, through the next form, etc." (if (null form) x (let ((result (make-symbol "result"))) `(-some->> (-when-let (,result ,x) (->> ,result ,form)) ,@more)))) (defmacro -some--> (x &optional form &rest more) "When expr in non-nil, thread it through the first form (via `-->'), and when that result is non-nil, through the next form, etc." (if (null form) x (let ((result (make-symbol "result"))) `(-some--> (-when-let (,result ,x) (--> ,result ,form)) ,@more)))) (defun -grade-up (comparator list) "Grade elements of LIST using COMPARATOR relation, yielding a permutation vector such that applying this permutation to LIST sorts it in ascending order." ;; ugly hack to "fix" lack of lexical scope (let ((comp `(lambda (it other) (funcall ',comparator (car it) (car other))))) (->> (--map-indexed (cons it it-index) list) (-sort comp) (-map 'cdr)))) (defun -grade-down (comparator list) "Grade elements of LIST using COMPARATOR relation, yielding a permutation vector such that applying this permutation to LIST sorts it in descending order." ;; ugly hack to "fix" lack of lexical scope (let ((comp `(lambda (it other) (funcall ',comparator (car other) (car it))))) (->> (--map-indexed (cons it it-index) list) (-sort comp) (-map 'cdr)))) (defvar dash--source-counter 0 "Monotonic counter for generated symbols.") (defun dash--match-make-source-symbol () "Generate a new dash-source symbol. All returned symbols are guaranteed to be unique." (prog1 (make-symbol (format "--dash-source-%d--" dash--source-counter)) (setq dash--source-counter (1+ dash--source-counter)))) (defun dash--match-ignore-place-p (symbol) "Return non-nil if SYMBOL is a symbol and starts with _." (and (symbolp symbol) (eq (aref (symbol-name symbol) 0) ?_))) (defun dash--match-cons-skip-cdr (skip-cdr source) "Helper function generating idiomatic shifting code." (cond ((= skip-cdr 0) `(pop ,source)) (t `(prog1 ,(dash--match-cons-get-car skip-cdr source) (setq ,source ,(dash--match-cons-get-cdr (1+ skip-cdr) source)))))) (defun dash--match-cons-get-car (skip-cdr source) "Helper function generating idiomatic code to get nth car." (cond ((= skip-cdr 0) `(car ,source)) ((= skip-cdr 1) `(cadr ,source)) (t `(nth ,skip-cdr ,source)))) (defun dash--match-cons-get-cdr (skip-cdr source) "Helper function generating idiomatic code to get nth cdr." (cond ((= skip-cdr 0) source) ((= skip-cdr 1) `(cdr ,source)) (t `(nthcdr ,skip-cdr ,source)))) (defun dash--match-cons (match-form source) "Setup a cons matching environment and call the real matcher." (let ((s (dash--match-make-source-symbol)) (n 0) (m match-form)) (while (and (consp m) (dash--match-ignore-place-p (car m))) (setq n (1+ n)) (!cdr m)) (cond ;; when we only have one pattern in the list, we don't have to ;; create a temporary binding (--dash-source--) for the source ;; and just use the input directly ((and (consp m) (not (cdr m))) (dash--match (car m) (dash--match-cons-get-car n source))) ;; handle other special types ((> n 0) (dash--match m (dash--match-cons-get-cdr n source))) ;; this is the only entry-point for dash--match-cons-1, that's ;; why we can't simply use the above branch, it would produce ;; infinite recursion (t (cons (list s source) (dash--match-cons-1 match-form s)))))) (defun dash--match-cons-1 (match-form source &optional props) "Match MATCH-FORM against SOURCE. MATCH-FORM is a proper or improper list. Each element of MATCH-FORM is either a symbol, which gets bound to the respective value in source or another match form which gets destructured recursively. If the cdr of last cons cell in the list is `nil', matching stops there. SOURCE is a proper or improper list." (let ((skip-cdr (or (plist-get props :skip-cdr) 0))) (cond ((consp match-form) (cond ((cdr match-form) (cond ((and (symbolp (car match-form)) (memq (car match-form) '(&keys &plist &alist &hash))) (dash--match-kv match-form (dash--match-cons-get-cdr skip-cdr source))) ((dash--match-ignore-place-p (car match-form)) (dash--match-cons-1 (cdr match-form) source (plist-put props :skip-cdr (1+ skip-cdr)))) (t (-concat (dash--match (car match-form) (dash--match-cons-skip-cdr skip-cdr source)) (dash--match-cons-1 (cdr match-form) source))))) (t ;; Last matching place, no need for shift (dash--match (car match-form) (dash--match-cons-get-car skip-cdr source))))) ((eq match-form nil) nil) (t ;; Handle improper lists. Last matching place, no need for shift (dash--match match-form (dash--match-cons-get-cdr skip-cdr source)))))) (defun dash--vector-tail (seq start) "Return the tail of SEQ starting at START." (cond ((vectorp seq) (let* ((re-length (- (length seq) start)) (re (make-vector re-length 0))) (--dotimes re-length (aset re it (aref seq (+ it start)))) re)) ((stringp seq) (substring seq start)))) (defun dash--match-vector (match-form source) "Setup a vector matching environment and call the real matcher." (let ((s (dash--match-make-source-symbol))) (cond ;; don't bind `s' if we only have one sub-pattern ((= (length match-form) 1) (dash--match (aref match-form 0) `(aref ,source 0))) ;; if the source is a symbol, we don't need to re-bind it ((symbolp source) (dash--match-vector-1 match-form source)) ;; don't bind `s' if we only have one sub-pattern which is not ignored ((let* ((ignored-places (mapcar 'dash--match-ignore-place-p match-form)) (ignored-places-n (length (-remove 'null ignored-places)))) (when (= ignored-places-n (1- (length match-form))) (let ((n (-find-index 'null ignored-places))) (dash--match (aref match-form n) `(aref ,source ,n)))))) (t (cons (list s source) (dash--match-vector-1 match-form s)))))) (defun dash--match-vector-1 (match-form source) "Match MATCH-FORM against SOURCE. MATCH-FORM is a vector. Each element of MATCH-FORM is either a symbol, which gets bound to the respective value in source or another match form which gets destructured recursively. If second-from-last place in MATCH-FORM is the symbol &rest, the next element of the MATCH-FORM is matched against the tail of SOURCE, starting at index of the &rest symbol. This is conceptually the same as the (head . tail) match for improper lists, where dot plays the role of &rest. SOURCE is a vector. If the MATCH-FORM vector is shorter than SOURCE vector, only the (length MATCH-FORM) places are bound, the rest of the SOURCE is discarded." (let ((i 0) (l (length match-form)) (re)) (while (< i l) (let ((m (aref match-form i))) (push (cond ((and (symbolp m) (eq m '&rest)) (prog1 (dash--match (aref match-form (1+ i)) `(dash--vector-tail ,source ,i)) (setq i l))) ((and (symbolp m) ;; do not match symbols starting with _ (not (eq (aref (symbol-name m) 0) ?_))) (list (list m `(aref ,source ,i)))) ((not (symbolp m)) (dash--match m `(aref ,source ,i)))) re) (setq i (1+ i)))) (-flatten-n 1 (nreverse re)))) (defun dash--match-kv (match-form source) "Setup a kv matching environment and call the real matcher. kv can be any key-value store, such as plist, alist or hash-table." (let ((s (dash--match-make-source-symbol))) (cond ;; don't bind `s' if we only have one sub-pattern (&type key val) ((= (length match-form) 3) (dash--match-kv-1 (cdr match-form) source (car match-form))) ;; if the source is a symbol, we don't need to re-bind it ((symbolp source) (dash--match-kv-1 (cdr match-form) source (car match-form))) (t (cons (list s source) (dash--match-kv-1 (cdr match-form) s (car match-form))))))) (defun dash--match-kv-1 (match-form source type) "Match MATCH-FORM against SOURCE of type TYPE. MATCH-FORM is a proper list of the form (key1 place1 ... keyN placeN). Each placeK is either a symbol, which gets bound to the value of keyK retrieved from the key-value store, or another match form which gets destructured recursively. SOURCE is a key-value store of type TYPE, which can be a plist, an alist or a hash table. TYPE is a token specifying the type of the key-value store. Valid values are &plist, &alist and &hash." (-flatten-n 1 (-map (lambda (kv) (let* ((k (car kv)) (v (cadr kv)) (getter (cond ((or (eq type '&plist) (eq type '&keys)) `(plist-get ,source ,k)) ((eq type '&alist) `(cdr (assoc ,k ,source))) ((eq type '&hash) `(gethash ,k ,source))))) (cond ((symbolp v) (list (list v getter))) (t (dash--match v getter))))) (-partition 2 match-form)))) (defun dash--match-symbol (match-form source) "Bind a symbol. This works just like `let', there is no destructuring." (list (list match-form source))) (defun dash--match (match-form source) "Match MATCH-FORM against SOURCE. This function tests the MATCH-FORM and dispatches to specific matchers based on the type of the expression. Key-value stores are disambiguated by placing a token &plist, &alist or &hash as a first item in the MATCH-FORM." (cond ((symbolp match-form) (dash--match-symbol match-form source)) ((consp match-form) (cond ;; Handle the "x &as" bindings first. ((and (consp (cdr match-form)) (symbolp (car match-form)) (eq '&as (cadr match-form))) (let ((s (car match-form))) (cons (list s source) (dash--match (cddr match-form) s)))) ((memq (car match-form) '(&keys &plist &alist &hash)) (dash--match-kv match-form source)) (t (dash--match-cons match-form source)))) ((vectorp match-form) ;; We support the &as binding in vectors too (cond ((and (> (length match-form) 2) (symbolp (aref match-form 0)) (eq '&as (aref match-form 1))) (let ((s (aref match-form 0))) (cons (list s source) (dash--match (dash--vector-tail match-form 2) s)))) (t (dash--match-vector match-form source)))))) (defmacro -let* (varlist &rest body) "Bind variables according to VARLIST then eval BODY. VARLIST is a list of lists of the form (PATTERN SOURCE). Each PATTERN is matched against the SOURCE structurally. SOURCE is only evaluated once for each PATTERN. Each SOURCE can refer to the symbols already bound by this VARLIST. This is useful if you want to destructure SOURCE recursively but also want to name the intermediate structures. See `-let' for the list of all possible patterns." (declare (debug ((&rest (sexp form)) body)) (indent 1)) (let ((bindings (--mapcat (dash--match (car it) (cadr it)) varlist))) `(let* ,bindings ,@body))) (defmacro -let (varlist &rest body) "Bind variables according to VARLIST then eval BODY. VARLIST is a list of lists of the form (PATTERN SOURCE). Each PATTERN is matched against the SOURCE \"structurally\". SOURCE is only evaluated once for each PATTERN. Each PATTERN is matched recursively, and can therefore contain sub-patterns which are matched against corresponding sub-expressions of SOURCE. All the SOURCEs are evalled before any symbols are bound (i.e. \"in parallel\"). If VARLIST only contains one (PATTERN SOURCE) element, you can optionally specify it using a vector and discarding the outer-most parens. Thus (-let ((PATTERN SOURCE)) ..) becomes (-let [PATTERN SOURCE] ..). `-let' uses a convention of not binding places (symbols) starting with _ whenever it's possible. You can use this to skip over entries you don't care about. However, this is not *always* possible (as a result of implementation) and these symbols might get bound to undefined values. Following is the overview of supported patterns. Remember that patterns can be matched recursively, so every a, b, aK in the following can be a matching construct and not necessarily a symbol/variable. Symbol: a - bind the SOURCE to A. This is just like regular `let'. Conses and lists: (a) - bind `car' of cons/list to A (a . b) - bind car of cons to A and `cdr' to B (a b) - bind car of list to A and `cadr' to B (a1 a2 a3 ...) - bind 0th car of list to A1, 1st to A2, 2nd to A3 ... (a1 a2 a3 ... aN . rest) - as above, but bind the Nth cdr to REST. Vectors: [a] - bind 0th element of a non-list sequence to A (works with vectors, strings, bit arrays...) [a1 a2 a3 ...] - bind 0th element of non-list sequence to A0, 1st to A1, 2nd to A2, ... If the PATTERN is shorter than SOURCE, the values at places not in PATTERN are ignored. If the PATTERN is longer than SOURCE, an `error' is thrown. [a1 a2 a3 ... &rest rest] - as above, but bind the rest of the sequence to REST. This is conceptually the same as improper list matching (a1 a2 ... aN . rest) Key/value stores: (&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the SOURCE plist to aK. If the value is not found, aK is nil. (&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the SOURCE alist to aK. If the value is not found, aK is nil. (&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the SOURCE hash table to aK. If the value is not found, aK is nil. Further, special keyword &keys supports \"inline\" matching of plist-like key-value pairs, similarly to &keys keyword of `cl-defun'. (a1 a2 ... aN &keys key1 b1 ... keyN bK) This binds N values from the list to a1 ... aN, then interprets the cdr as a plist (see key/value matching above). You can name the source using the syntax SYMBOL &as PATTERN. This syntax works with lists (proper or improper), vectors and all types of maps. (list &as a b c) (list 1 2 3) binds A to 1, B to 2, C to 3 and LIST to (1 2 3). Similarly: (bounds &as beg . end) (cons 1 2) binds BEG to 1, END to 2 and BOUNDS to (1 . 2). (items &as first . rest) (list 1 2 3) binds FIRST to 1, REST to (2 3) and ITEMS to (1 2 3) [vect &as _ b c] [1 2 3] binds B to 2, C to 3 and VECT to [1 2 3] (_ avoids binding as usual). (plist &as &plist :b b) (list :a 1 :b 2 :c 3) binds B to 2 and PLIST to (:a 1 :b 2 :c 3). Same for &alist and &hash. This is especially useful when we want to capture the result of a computation and destructure at the same time. Consider the form (function-returning-complex-structure) returning a list of two vectors with two items each. We want to capture this entire result and pass it to another computation, but at the same time we want to get the second item from each vector. We can achieve it with pattern (result &as [_ a] [_ b]) (function-returning-complex-structure) Note: Clojure programmers may know this feature as the \":as binding\". The difference is that we put the &as at the front because we need to support improper list binding." (declare (debug ([&or (&rest (sexp form)) (vector [&rest [sexp form]])] body)) (indent 1)) (if (vectorp varlist) `(let* ,(dash--match (aref varlist 0) (aref varlist 1)) ,@body) (let* ((inputs (--map-indexed (list (make-symbol (format "input%d" it-index)) (cadr it)) varlist)) (new-varlist (--map (list (caar it) (cadr it)) (-zip varlist inputs)))) `(let ,inputs (-let* ,new-varlist ,@body))))) (defmacro -lambda (match-form &rest body) "Return a lambda which destructures its input as MATCH-FORM and executes BODY. Note that you have to enclose the MATCH-FORM in a pair of parens, such that: (-lambda (x) body) (-lambda (x y ...) body) has the usual semantics of `lambda'. Furthermore, these get translated into normal lambda, so there is no performance penalty. See `-let' for the description of destructuring mechanism." (declare (doc-string 2) (indent defun) (debug (&define sexp [&optional stringp] [&optional ("interactive" interactive)] def-body))) (cond ((not (consp match-form)) (signal 'wrong-type-argument "match-form must be a list")) ;; no destructuring, so just return regular lambda to make things faster ((-all? 'symbolp match-form) `(lambda ,match-form ,@body)) (t (let* ((inputs (--map-indexed (list it (make-symbol (format "input%d" it-index))) match-form))) ;; TODO: because inputs to the lambda are evaluated only once, ;; -let* need not to create the extra bindings to ensure that. ;; We should find a way to optimize that. Not critical however. `(lambda ,(--map (cadr it) inputs) (-let* ,inputs ,@body)))))) (defmacro -if-let* (vars-vals then &rest else) "If all VALS evaluate to true, bind them to their corresponding VARS and do THEN, otherwise do ELSE. VARS-VALS should be a list of (VAR VAL) pairs. Note: binding is done according to `-let*'. VALS are evaluated sequentially, and evaluation stops after the first nil VAL is encountered." (declare (debug ((&rest (sexp form)) form body)) (indent 2)) (->> vars-vals (--mapcat (dash--match (car it) (cadr it))) (--reduce-r-from (let ((var (car it)) (val (cadr it))) `(let ((,var ,val)) (if ,var ,acc ,@else))) then))) (defmacro -if-let (var-val then &rest else) "If VAL evaluates to non-nil, bind it to VAR and do THEN, otherwise do ELSE. VAR-VAL should be a (VAR VAL) pair. Note: binding is done according to `-let'." (declare (debug ((sexp form) form body)) (indent 2)) `(-if-let* (,var-val) ,then ,@else)) (defmacro --if-let (val then &rest else) "If VAL evaluates to non-nil, bind it to `it' and do THEN, otherwise do ELSE." (declare (debug (form form body)) (indent 2)) `(-if-let (it ,val) ,then ,@else)) (defmacro -when-let* (vars-vals &rest body) "If all VALS evaluate to true, bind them to their corresponding VARS and execute body. VARS-VALS should be a list of (VAR VAL) pairs. Note: binding is done according to `-let*'. VALS are evaluated sequentially, and evaluation stops after the first nil VAL is encountered." (declare (debug ((&rest (sexp form)) body)) (indent 1)) `(-if-let* ,vars-vals (progn ,@body))) (defmacro -when-let (var-val &rest body) "If VAL evaluates to non-nil, bind it to VAR and execute body. VAR-VAL should be a (VAR VAL) pair. Note: binding is done according to `-let'." (declare (debug ((sexp form) body)) (indent 1)) `(-if-let ,var-val (progn ,@body))) (defmacro --when-let (val &rest body) "If VAL evaluates to non-nil, bind it to `it' and execute body." (declare (debug (form body)) (indent 1)) `(--if-let ,val (progn ,@body))) (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: (let ((-compare-fn #'=)) (-union numbers1 numbers2 numbers3)") (defun -distinct (list) "Return a new list with all duplicates removed. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil. Alias: `-uniq'" (let (result) (--each list (unless (-contains? result it) (!cons it result))) (nreverse result))) (defalias '-uniq '-distinct) (defun -union (list list2) "Return a new list containing the elements of LIST1 and elements of LIST2 that are not in LIST1. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil." ;; We fall back to iteration implementation if the comparison ;; function isn't one of `eq', `eql' or `equal'. (let* ((result (reverse list)) ;; TODO: get rid of this dynamic variable, pass it as an ;; argument instead. (-compare-fn (if (bound-and-true-p -compare-fn) -compare-fn 'equal))) (if (memq -compare-fn '(eq eql equal)) (let ((ht (make-hash-table :test -compare-fn))) (--each list (puthash it t ht)) (--each list2 (unless (gethash it ht) (!cons it result)))) (--each list2 (unless (-contains? result it) (!cons it result)))) (nreverse result))) (defun -intersection (list list2) "Return a new list containing only the elements that are members of both LIST and LIST2. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil." (--filter (-contains? list2 it) list)) (defun -difference (list list2) "Return a new list with only the members of LIST that are not in LIST2. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil." (--filter (not (-contains? list2 it)) list)) (defun -contains? (list element) "Return non-nil if LIST contains ELEMENT. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil. Alias: `-contains-p'" (not (null (cond ((null -compare-fn) (member element list)) ((eq -compare-fn 'eq) (memq element list)) ((eq -compare-fn 'eql) (memql element list)) (t (let ((lst list)) (while (and lst (not (funcall -compare-fn element (car lst)))) (setq lst (cdr lst))) lst)))))) (defalias '-contains-p '-contains?) (defun -same-items? (list list2) "Return true if LIST and LIST2 has the same items. The order of the elements in the lists does not matter. Alias: `-same-items-p'" (let ((length-a (length list)) (length-b (length list2))) (and (= length-a length-b) (= length-a (length (-intersection list list2)))))) (defalias '-same-items-p '-same-items?) (defun -is-prefix? (prefix list) "Return non-nil if PREFIX is prefix of LIST. Alias: `-is-prefix-p'" (--each-while list (equal (car prefix) it) (!cdr prefix)) (not prefix)) (defun -is-suffix? (suffix list) "Return non-nil if SUFFIX is suffix of LIST. Alias: `-is-suffix-p'" (-is-prefix? (reverse suffix) (reverse list))) (defun -is-infix? (infix list) "Return non-nil if INFIX is infix of LIST. This operation runs in O(n^2) time Alias: `-is-infix-p'" (let (done) (while (and (not done) list) (setq done (-is-prefix? infix list)) (!cdr list)) done)) (defalias '-is-prefix-p '-is-prefix?) (defalias '-is-suffix-p '-is-suffix?) (defalias '-is-infix-p '-is-infix?) (defun -sort (comparator list) "Sort LIST, stably, comparing elements using COMPARATOR. Return the sorted list. LIST is NOT modified by side effects. COMPARATOR is called with two elements of LIST, and should return non-nil if the first element should sort before the second." (sort (copy-sequence list) comparator)) (defmacro --sort (form list) "Anaphoric form of `-sort'." (declare (debug (form form))) `(-sort (lambda (it other) ,form) ,list)) (defun -list (&rest args) "Return a list with ARGS. If first item of ARGS is already a list, simply return ARGS. If not, return a list with ARGS as elements." (let ((arg (car args))) (if (listp arg) arg args))) (defun -repeat (n x) "Return a list with X repeated N times. Return nil if N is less than 1." (let (ret) (--dotimes n (!cons x ret)) ret)) (defun -sum (list) "Return the sum of LIST." (apply '+ list)) (defun -product (list) "Return the product of LIST." (apply '* list)) (defun -max (list) "Return the largest value from LIST of numbers or markers." (apply 'max list)) (defun -min (list) "Return the smallest value from LIST of numbers or markers." (apply 'min list)) (defun -max-by (comparator list) "Take a comparison function COMPARATOR and a LIST and return the greatest element of the list by the comparison function. See also combinator `-on' which can transform the values before comparing them." (--reduce (if (funcall comparator it acc) it acc) list)) (defun -min-by (comparator list) "Take a comparison function COMPARATOR and a LIST and return the least element of the list by the comparison function. See also combinator `-on' which can transform the values before comparing them." (--reduce (if (funcall comparator it acc) acc it) list)) (defmacro --max-by (form list) "Anaphoric version of `-max-by'. The items for the comparator form are exposed as \"it\" and \"other\"." (declare (debug (form form))) `(-max-by (lambda (it other) ,form) ,list)) (defmacro --min-by (form list) "Anaphoric version of `-min-by'. The items for the comparator form are exposed as \"it\" and \"other\"." (declare (debug (form form))) `(-min-by (lambda (it other) ,form) ,list)) (defun -iterate (fun init n) "Return a list of iterated applications of FUN to INIT. This means a list of form: (init (fun init) (fun (fun init)) ...) N is the length of the returned list." (if (= n 0) nil (let ((r (list init))) (--dotimes (1- n) (push (funcall fun (car r)) r)) (nreverse r)))) (defun -fix (fn list) "Compute the (least) fixpoint of FN with initial input LIST. FN is called at least once, results are compared with `equal'." (let ((re (funcall fn list))) (while (not (equal list re)) (setq list re) (setq re (funcall fn re))) re)) (defmacro --fix (form list) "Anaphoric form of `-fix'." `(-fix (lambda (it) ,form) ,list)) (defun -unfold (fun seed) "Build a list from SEED using FUN. This is \"dual\" operation to `-reduce-r': while -reduce-r consumes a list to produce a single value, `-unfold' takes a seed value and builds a (potentially infinite!) list. FUN should return `nil' to stop the generating process, or a cons (A . B), where A will be prepended to the result and B is the new seed." (let ((last (funcall fun seed)) r) (while last (push (car last) r) (setq last (funcall fun (cdr last)))) (nreverse r))) (defmacro --unfold (form seed) "Anaphoric version of `-unfold'." (declare (debug (form form))) `(-unfold (lambda (it) ,form) ,seed)) (defun -cons-pair? (con) "Return non-nil if CON is true cons pair. That is (A . B) where B is not a list." (and (listp con) (not (listp (cdr con))))) (defun -cons-to-list (con) "Convert a cons pair to a list with `car' and `cdr' of the pair respectively." (list (car con) (cdr con))) (defun -value-to-list (val) "Convert a value to a list. If the value is a cons pair, make a list with two elements, `car' and `cdr' of the pair respectively. If the value is anything else, wrap it in a list." (cond ((-cons-pair? val) (-cons-to-list val)) (t (list val)))) (defun -tree-mapreduce-from (fn folder init-value tree) "Apply FN to each element of TREE, and make a list of the results. If elements of TREE are lists themselves, apply FN recursively to elements of these nested lists. Then reduce the resulting lists using FOLDER and initial value INIT-VALUE. See `-reduce-r-from'. This is the same as calling `-tree-reduce-from' after `-tree-map' but is twice as fast as it only traverse the structure once." (cond ((not tree) nil) ((-cons-pair? tree) (funcall fn tree)) ((listp tree) (-reduce-r-from folder init-value (mapcar (lambda (x) (-tree-mapreduce-from fn folder init-value x)) tree))) (t (funcall fn tree)))) (defmacro --tree-mapreduce-from (form folder init-value tree) "Anaphoric form of `-tree-mapreduce-from'." (declare (debug (form form form form))) `(-tree-mapreduce-from (lambda (it) ,form) (lambda (it acc) ,folder) ,init-value ,tree)) (defun -tree-mapreduce (fn folder tree) "Apply FN to each element of TREE, and make a list of the results. If elements of TREE are lists themselves, apply FN recursively to elements of these nested lists. Then reduce the resulting lists using FOLDER and initial value INIT-VALUE. See `-reduce-r-from'. This is the same as calling `-tree-reduce' after `-tree-map' but is twice as fast as it only traverse the structure once." (cond ((not tree) nil) ((-cons-pair? tree) (funcall fn tree)) ((listp tree) (-reduce-r folder (mapcar (lambda (x) (-tree-mapreduce fn folder x)) tree))) (t (funcall fn tree)))) (defmacro --tree-mapreduce (form folder tree) "Anaphoric form of `-tree-mapreduce'." (declare (debug (form form form))) `(-tree-mapreduce (lambda (it) ,form) (lambda (it acc) ,folder) ,tree)) (defun -tree-map (fn tree) "Apply FN to each element of TREE while preserving the tree structure." (cond ((not tree) nil) ((-cons-pair? tree) (funcall fn tree)) ((listp tree) (mapcar (lambda (x) (-tree-map fn x)) tree)) (t (funcall fn tree)))) (defmacro --tree-map (form tree) "Anaphoric form of `-tree-map'." (declare (debug (form form))) `(-tree-map (lambda (it) ,form) ,tree)) (defun -tree-reduce-from (fn init-value tree) "Use FN to reduce elements of list TREE. If elements of TREE are lists themselves, apply the reduction recursively. FN is first applied to INIT-VALUE and first element of the list, then on this result and second element from the list etc. The initial value is ignored on cons pairs as they always contain two elements." (cond ((not tree) nil) ((-cons-pair? tree) tree) ((listp tree) (-reduce-r-from fn init-value (mapcar (lambda (x) (-tree-reduce-from fn init-value x)) tree))) (t tree))) (defmacro --tree-reduce-from (form init-value tree) "Anaphoric form of `-tree-reduce-from'." (declare (debug (form form form))) `(-tree-reduce-from (lambda (it acc) ,form) ,init-value ,tree)) (defun -tree-reduce (fn tree) "Use FN to reduce elements of list TREE. If elements of TREE are lists themselves, apply the reduction recursively. FN is first applied to first element of the list and second element, then on this result and third element from the list etc. See `-reduce-r' for how exactly are lists of zero or one element handled." (cond ((not tree) nil) ((-cons-pair? tree) tree) ((listp tree) (-reduce-r fn (mapcar (lambda (x) (-tree-reduce fn x)) tree))) (t tree))) (defmacro --tree-reduce (form tree) "Anaphoric form of `-tree-reduce'." (declare (debug (form form))) `(-tree-reduce (lambda (it acc) ,form) ,tree)) (defun -tree-map-nodes (pred fun tree) "Call FUN on each node of TREE that satisfies PRED. If PRED returns nil, continue descending down this node. If PRED returns non-nil, apply FUN to this node and do not descend further." (if (funcall pred tree) (funcall fun tree) (if (and (listp tree) (not (-cons-pair? tree))) (-map (lambda (x) (-tree-map-nodes pred fun x)) tree) tree))) (defmacro --tree-map-nodes (pred form tree) "Anaphoric form of `-tree-map-nodes'." `(-tree-map-nodes (lambda (it) ,pred) (lambda (it) ,form) ,tree)) (defun -tree-seq (branch children tree) "Return a sequence of the nodes in TREE, in depth-first search order. BRANCH is a predicate of one argument that returns non-nil if the passed argument is a branch, that is, a node that can have children. CHILDREN is a function of one argument that returns the children of the passed branch node. Non-branch nodes are simply copied." (cons tree (when (funcall branch tree) (-mapcat (lambda (x) (-tree-seq branch children x)) (funcall children tree))))) (defmacro --tree-seq (branch children tree) "Anaphoric form of `-tree-seq'." `(-tree-seq (lambda (it) ,branch) (lambda (it) ,children) ,tree)) (defun -clone (list) "Create a deep copy of LIST. The new list has the same elements and structure but all cons are replaced with new ones. This is useful when you need to clone a structure such as plist or alist." (-tree-map 'identity list)) (defun dash-enable-font-lock () "Add syntax highlighting to dash functions, macros and magic values." (eval-after-load "lisp-mode" '(progn (let ((new-keywords '( "-each" "--each" "-each-while" "--each-while" "-dotimes" "--dotimes" "-map" "--map" "-reduce-from" "--reduce-from" "-reduce" "--reduce" "-reduce-r-from" "--reduce-r-from" "-reduce-r" "--reduce-r" "-filter" "--filter" "-select" "--select" "-remove" "--remove" "-reject" "--reject" "-remove-first" "--remove-first" "-reject-first" "--reject-first" "-remove-last" "--remove-last" "-reject-last" "--reject-last" "-remove-item" "-non-nil" "-keep" "--keep" "-map-indexed" "--map-indexed" "-splice" "--splice" "-splice-list" "--splice-list" "-map-when" "--map-when" "-replace-where" "--replace-where" "-map-first" "--map-first" "-map-last" "--map-last" "-replace" "-replace-first" "-replace-last" "-flatten" "-flatten-n" "-concat" "-mapcat" "--mapcat" "-copy" "-cons*" "-snoc" "-first" "--first" "-find" "--find" "-some" "--some" "-any" "--any" "-last" "--last" "-first-item" "-last-item" "-butlast" "-count" "--count" "-any?" "--any?" "-some?" "--some?" "-any-p" "--any-p" "-some-p" "--some-p" "-all?" "--all?" "-every?" "--every?" "-all-p" "--all-p" "-every-p" "--every-p" "-none?" "--none?" "-none-p" "--none-p" "-only-some?" "--only-some?" "-only-some-p" "--only-some-p" "-slice" "-take" "-drop" "-take-while" "--take-while" "-drop-while" "--drop-while" "-split-at" "-rotate" "-insert-at" "-replace-at" "-update-at" "--update-at" "-remove-at" "-remove-at-indices" "-split-with" "--split-with" "-split-on" "-split-when" "--split-when" "-separate" "--separate" "-partition-all-in-steps" "-partition-in-steps" "-partition-all" "-partition" "-partition-by" "--partition-by" "-partition-by-header" "--partition-by-header" "-group-by" "--group-by" "-interpose" "-interleave" "-zip-with" "--zip-with" "-zip" "-zip-fill" "-cycle" "-pad" "-annotate" "--annotate" "-table" "-table-flat" "-partial" "-elem-index" "-elem-indices" "-find-indices" "--find-indices" "-find-index" "--find-index" "-find-last-index" "--find-last-index" "-select-by-indices" "-grade-up" "-grade-down" "->" "->>" "-->" "-when-let" "-when-let*" "--when-let" "-if-let" "-if-let*" "--if-let" "-let*" "-let" "-lambda" "-distinct" "-uniq" "-union" "-intersection" "-difference" "-contains?" "-contains-p" "-same-items?" "-same-items-p" "-is-prefix-p" "-is-prefix?" "-is-suffix-p" "-is-suffix?" "-is-infix-p" "-is-infix?" "-sort" "--sort" "-list" "-repeat" "-sum" "-product" "-max" "-min" "-max-by" "--max-by" "-min-by" "--min-by" "-iterate" "--iterate" "-fix" "--fix" "-unfold" "--unfold" "-cons-pair?" "-cons-to-list" "-value-to-list" "-tree-mapreduce-from" "--tree-mapreduce-from" "-tree-mapreduce" "--tree-mapreduce" "-tree-map" "--tree-map" "-tree-reduce-from" "--tree-reduce-from" "-tree-reduce" "--tree-reduce" "-tree-seq" "--tree-seq" "-tree-map-nodes" "--tree-map-nodes" "-clone" "-rpartial" "-juxt" "-applify" "-on" "-flip" "-const" "-cut" "-orfn" "-andfn" "-iteratefn" "-fixfn" "-prodfn" )) (special-variables '( "it" "it-index" "acc" "other" ))) (font-lock-add-keywords 'emacs-lisp-mode `((,(concat "\\_<" (regexp-opt special-variables 'paren) "\\_>") 1 font-lock-variable-name-face)) 'append) (font-lock-add-keywords 'emacs-lisp-mode `((,(concat "(\\s-*" (regexp-opt new-keywords 'paren) "\\_>") 1 font-lock-keyword-face)) 'append)) (--each (buffer-list) (with-current-buffer it (when (and (eq major-mode 'emacs-lisp-mode) (boundp 'font-lock-mode) font-lock-mode) (font-lock-refresh-defaults))))))) (provide 'dash) ;;; dash.el ends here dash.el-2.12.1/dash.info000066400000000000000000003045201261164447300147230ustar00rootroot00000000000000This is dash.info, produced by makeinfo version 4.8 from dash.texi. INFO-DIR-SECTION Emacs START-INFO-DIR-ENTRY * Dash: (dash.info). A modern list library for GNU Emacs END-INFO-DIR-ENTRY This manual is for `dash.el' version 2.12.0. Copyright © 2012-2015 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see `http://www.gnu.org/licenses/'.  File: dash.info, Node: Top, Next: Installation, Up: (dir) dash **** This manual is for `dash.el' version 2.12.0. Copyright © 2012-2015 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see `http://www.gnu.org/licenses/'. * Menu: * Installation:: * Functions:: * Development:: * Index:: --- The Detailed Node Listing --- Installation * Using in a package:: * Syntax highlighting of dash functions:: Functions * Maps:: * Sublist selection:: * List to list:: * Reductions:: * Unfolding:: * Predicates:: * Partitioning:: * Indexing:: * Set operations:: * Other list operations:: * Tree operations:: * Threading macros:: * Binding:: * Side-effects:: * Destructive operations:: * Function combinators:: Development * Contribute:: How to contribute * Changes:: List of significant changes by version * Contributors:: List of contributors  File: dash.info, Node: Installation, Next: Functions, Prev: Top, Up: Top 1 Installation ************** It's available on marmalade (http://marmalade-repo.org/) and Melpa (http://melpa.milkbox.net/); use `M-x package-install': `M-x package-install dash' Install the dash library. `M-x package-install dash-functional' Optional, if you want the function combinators. Alternatively, you can just dump dash.el or dash-functional.el in your load path somewhere. * Menu: * Using in a package:: * Syntax highlighting of dash functions::  File: dash.info, Node: Using in a package, Next: Syntax highlighting of dash functions, Up: Installation 1.1 Using in a package ====================== Add this to the big comment block at the top: ;; Package-Requires: ((dash "2.12.0")) To get function combinators: ;; Package-Requires: ((dash "2.12.0") (dash-functional "1.2.0") (emacs "24"))  File: dash.info, Node: Syntax highlighting of dash functions, Prev: Using in a package, Up: Installation 1.2 Syntax highlighting of dash functions ========================================= Font lock of dash functions in emacs lisp buffers is now optional. Include this in your emacs settings to get syntax highlighting: (eval-after-load "dash" '(dash-enable-font-lock))  File: dash.info, Node: Functions, Next: Development, Prev: Installation, Up: Top 2 Functions *********** This chapter contains reference documentation for the dash application programming interface (API). All functions and constructs in the library are prefixed with a dash (-). There are also anaphoric versions of functions where that makes sense, prefixed with two dashes instead of one. For instance, while `-map' takes a function to map over the list, one can also use the anaphoric form with double dashes - which will then be executed with `it' exposed as the list item. Here's an example: (-map (lambda (n) (* n n)) '(1 2 3 4)) ;; normal version (--map (* it it) '(1 2 3 4)) ;; anaphoric version Of course, the original can also be written like (defun square (n) (* n n)) (-map 'square '(1 2 3 4)) which demonstrates the usefulness of both versions. * Menu: * Maps:: * Sublist selection:: * List to list:: * Reductions:: * Unfolding:: * Predicates:: * Partitioning:: * Indexing:: * Set operations:: * Other list operations:: * Tree operations:: * Threading macros:: * Binding:: * Side-effects:: * Destructive operations:: * Function combinators::  File: dash.info, Node: Maps, Next: Sublist selection, Up: Functions 2.1 Maps ======== Functions in this category take a transforming function, which is then applied sequentially to each or selected elements of the input list. The results are collected in order and returned as new list. -- Function: -map (fn list) Return a new list consisting of the result of applying FN to the items in LIST. (-map (lambda (num) (* num num)) '(1 2 3 4)) => '(1 4 9 16) (-map 'square '(1 2 3 4)) => '(1 4 9 16) (--map (* it it) '(1 2 3 4)) => '(1 4 9 16) -- Function: -map-when (pred rep list) Return a new list where the elements in LIST that does not match the PRED function are unchanged, and where the elements in LIST that do match the PRED function are mapped through the REP function. Alias: `-replace-where' See also: `-update-at' (*note -update-at::) (-map-when 'even? 'square '(1 2 3 4)) => '(1 4 3 16) (--map-when (> it 2) (* it it) '(1 2 3 4)) => '(1 2 9 16) (--map-when (= it 2) 17 '(1 2 3 4)) => '(1 17 3 4) -- Function: -map-first (pred rep list) Replace first item in LIST satisfying PRED with result of REP called on this item. See also: `-map-when' (*note -map-when::), `-replace-first' (*note -replace-first::) (-map-first 'even? 'square '(1 2 3 4)) => '(1 4 3 4) (--map-first (> it 2) (* it it) '(1 2 3 4)) => '(1 2 9 4) (--map-first (= it 2) 17 '(1 2 3 2)) => '(1 17 3 2) -- Function: -map-last (pred rep list) Replace first item in LIST satisfying PRED with result of REP called on this item. See also: `-map-when' (*note -map-when::), `-replace-last' (*note -replace-last::) (-map-last 'even? 'square '(1 2 3 4)) => '(1 2 3 16) (--map-last (> it 2) (* it it) '(1 2 3 4)) => '(1 2 3 16) (--map-last (= it 2) 17 '(1 2 3 2)) => '(1 2 3 17) -- Function: -map-indexed (fn list) Return a new list consisting of the result of (FN index item) for each item in LIST. In the anaphoric form `--map-indexed', the index is exposed as `it-index`. (-map-indexed (lambda (index item) (- item index)) '(1 2 3 4)) => '(1 1 1 1) (--map-indexed (- it it-index) '(1 2 3 4)) => '(1 1 1 1) -- Function: -annotate (fn list) Return a list of cons cells where each cell is FN applied to each element of LIST paired with the unmodified element of LIST. (-annotate '1+ '(1 2 3)) => '((2 . 1) (3 . 2) (4 . 3)) (-annotate 'length '(("h" "e" "l" "l" "o") ("hello" "world"))) => '((5 "h" "e" "l" "l" "o") (2 "hello" "world")) (--annotate (< 1 it) '(0 1 2 3)) => '((nil . 0) (nil . 1) (t . 2) (t . 3)) -- Function: -splice (pred fun list) Splice lists generated by FUN in place of elements matching PRED in LIST. FUN takes the element matching PRED as input. This function can be used as replacement for `,@' in case you need to splice several lists at marked positions (for example with keywords). See also: `-splice-list' (*note -splice-list::), `-insert-at' (*note -insert-at::) (-splice 'even? (lambda (x) (list x x)) '(1 2 3 4)) => '(1 2 2 3 4 4) (--splice 't (list it it) '(1 2 3 4)) => '(1 1 2 2 3 3 4 4) (--splice (equal it :magic) '((list of) (magical) (code)) '((foo) (bar) :magic (baz))) => '((foo) (bar) (list of) (magical) (code) (baz)) -- Function: -splice-list (pred new-list list) Splice NEW-LIST in place of elements matching PRED in LIST. See also: `-splice' (*note -splice::), `-insert-at' (*note -insert-at::) (-splice-list 'keywordp '(a b c) '(1 :foo 2)) => '(1 a b c 2) (-splice-list 'keywordp nil '(1 :foo 2)) => '(1 2) (--splice-list (keywordp it) '(a b c) '(1 :foo 2)) => '(1 a b c 2) -- Function: -mapcat (fn list) Return the concatenation of the result of mapping FN over LIST. Thus function FN should return a list. (-mapcat 'list '(1 2 3)) => '(1 2 3) (-mapcat (lambda (item) (list 0 item)) '(1 2 3)) => '(0 1 0 2 0 3) (--mapcat (list 0 it) '(1 2 3)) => '(0 1 0 2 0 3) -- Function: -copy (arg) Create a shallow copy of LIST. (-copy '(1 2 3)) => '(1 2 3) (let ((a '(1 2 3))) (eq a (-copy a))) => nil  File: dash.info, Node: Sublist selection, Next: List to list, Prev: Maps, Up: Functions 2.2 Sublist selection ===================== Functions returning a sublist of the original list. -- Function: -filter (pred list) Return a new list of the items in LIST for which PRED returns a non-nil value. Alias: `-select' (-filter (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) => '(2 4) (-filter 'even? '(1 2 3 4)) => '(2 4) (--filter (= 0 (% it 2)) '(1 2 3 4)) => '(2 4) -- Function: -remove (pred list) Return a new list of the items in LIST for which PRED returns nil. Alias: `-reject' (-remove (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) => '(1 3) (-remove 'even? '(1 2 3 4)) => '(1 3) (--remove (= 0 (% it 2)) '(1 2 3 4)) => '(1 3) -- Function: -remove-first (pred list) Return a new list with the first item matching PRED removed. Alias: `-reject-first' See also: `-remove' (*note -remove::), `-map-first' (*note -map-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) -- Function: -remove-last (pred list) Return a new list with the last item matching PRED removed. Alias: `-reject-last' See also: `-remove' (*note -remove::), `-map-last' (*note -map-last::) (-remove-last 'even? '(1 3 5 4 7 8 10 11)) => '(1 3 5 4 7 8 11) (-remove-last 'stringp '(1 2 "last" "second" "third")) => '(1 2 "last" "second") (--remove-last (> it 3) '(1 2 3 4 5 6 7 8 9 10)) => '(1 2 3 4 5 6 7 8 9) -- Function: -remove-item (item list) Remove all occurences of ITEM from LIST. Comparison is done with `equal'. (-remove-item 3 '(1 2 3 2 3 4 5 3)) => '(1 2 2 4 5) (-remove-item 'foo '(foo bar baz foo)) => '(bar baz) (-remove-item "bob" '("alice" "bob" "eve" "bob" "dave")) => '("alice" "eve" "dave") -- Function: -non-nil (list) Return all non-nil elements of LIST. (-non-nil '(1 nil 2 nil nil 3 4 nil 5 nil)) => '(1 2 3 4 5) -- Function: -slice (list from &optional to step) Return copy of LIST, starting from index FROM to index TO. FROM or TO may be negative. These values are then interpreted modulo the length of the list. If STEP is a number, only each STEPth item in the resulting section is returned. Defaults to 1. (-slice '(1 2 3 4 5) 1) => '(2 3 4 5) (-slice '(1 2 3 4 5) 0 3) => '(1 2 3) (-slice '(1 2 3 4 5 6 7 8 9) 1 -1 2) => '(2 4 6 8) -- Function: -take (n list) Return a new list of the first N items in LIST, or all items if there are fewer than N. (-take 3 '(1 2 3 4 5)) => '(1 2 3) (-take 17 '(1 2 3 4 5)) => '(1 2 3 4 5) -- Function: -drop (n list) Return the tail of LIST without the first N items. (-drop 3 '(1 2 3 4 5)) => '(4 5) (-drop 17 '(1 2 3 4 5)) => '() -- Function: -take-while (pred list) Return a new list of successive items from LIST while (PRED item) returns a non-nil value. (-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) -- Function: -drop-while (pred list) Return the tail of LIST starting from the first item for which (PRED item) returns nil. (-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) -- Function: -select-by-indices (indices list) Return a list whose elements are elements from LIST selected as `(nth i list)` for all i from 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") (-select-by-indices '(2 1 0) '("a" "b" "c")) => '("c" "b" "a") (-select-by-indices '(0 1 2 0 1 3 3 1) '("f" "a" "r" "l")) => '("f" "a" "r" "f" "a" "l" "l" "a")  File: dash.info, Node: List to list, Next: Reductions, Prev: Sublist selection, Up: Functions 2.3 List to list ================ Bag of various functions which modify input list. -- Function: -keep (fn list) Return a new list of the non-nil results of applying FN to the items in LIST. If you want to select the original items satisfying a predicate use `-filter' (*note -filter::). (-keep 'cdr '((1 2 3) (4 5) (6))) => '((2 3) (5)) (-keep (lambda (num) (when (> num 3) (* 10 num))) '(1 2 3 4 5 6)) => '(40 50 60) (--keep (when (> it 3) (* 10 it)) '(1 2 3 4 5 6)) => '(40 50 60) -- Function: -concat (&rest lists) Return a new list with the concatenation of the elements in the supplied LISTS. (-concat '(1)) => '(1) (-concat '(1) '(2)) => '(1 2) (-concat '(1) '(2 3) '(4)) => '(1 2 3 4) -- Function: -flatten (l) Take a nested list L and return its contents as a single, flat list. Note that because `nil' represents a list of zero elements (an empty list), any mention of nil in L will disappear after flattening. If you need to preserve nils, consider `-flatten-n' (*note -flatten-n::) or map them to some unique symbol and then map them back. Conses of two atoms are considered "terminals", that is, they aren't flattened further. See also: `-flatten-n' (*note -flatten-n::) (-flatten '((1))) => '(1) (-flatten '((1 (2 3) (((4 (5))))))) => '(1 2 3 4 5) (-flatten '(1 2 (3 . 4))) => '(1 2 (3 . 4)) -- Function: -flatten-n (num list) Flatten NUM levels of a nested LIST. See also: `-flatten' (*note -flatten::) (-flatten-n 1 '((1 2) ((3 4) ((5 6))))) => '(1 2 (3 4) ((5 6))) (-flatten-n 2 '((1 2) ((3 4) ((5 6))))) => '(1 2 3 4 (5 6)) (-flatten-n 3 '((1 2) ((3 4) ((5 6))))) => '(1 2 3 4 5 6) -- Function: -replace (old new list) Replace all OLD items in LIST with NEW. Elements are compared using `equal'. See also: `-replace-at' (*note -replace-at::) (-replace 1 "1" '(1 2 3 4 3 2 1)) => '("1" 2 3 4 3 2 "1") (-replace "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) => '("a" "nice" "bar" "sentence" "about" "bar") (-replace 1 2 nil) => nil -- Function: -replace-first (old new list) Replace the first occurence of OLD with NEW in LIST. Elements are compared using `equal'. See also: `-map-first' (*note -map-first::) (-replace-first 1 "1" '(1 2 3 4 3 2 1)) => '("1" 2 3 4 3 2 1) (-replace-first "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) => '("a" "nice" "bar" "sentence" "about" "foo") (-replace-first 1 2 nil) => nil -- Function: -replace-last (old new list) Replace the last occurence of OLD with NEW in LIST. Elements are compared using `equal'. See also: `-map-last' (*note -map-last::) (-replace-last 1 "1" '(1 2 3 4 3 2 1)) => '(1 2 3 4 3 2 "1") (-replace-last "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) => '("a" "nice" "foo" "sentence" "about" "bar") (-replace-last 1 2 nil) => nil -- Function: -insert-at (n x list) Return a list with X inserted into LIST at position N. See also: `-splice' (*note -splice::), `-splice-list' (*note -splice-list::) (-insert-at 1 'x '(a b c)) => '(a x b c) (-insert-at 12 'x '(a b c)) => '(a b c x) -- Function: -replace-at (n x list) Return a list with element at Nth position in LIST replaced with X. See also: `-replace' (*note -replace::) (-replace-at 0 9 '(0 1 2 3 4 5)) => '(9 1 2 3 4 5) (-replace-at 1 9 '(0 1 2 3 4 5)) => '(0 9 2 3 4 5) (-replace-at 4 9 '(0 1 2 3 4 5)) => '(0 1 2 3 9 5) -- Function: -update-at (n func list) Return a list with element at Nth position in LIST replaced with `(func (nth n list))`. See also: `-map-when' (*note -map-when::) (-update-at 0 (lambda (x) (+ x 9)) '(0 1 2 3 4 5)) => '(9 1 2 3 4 5) (-update-at 1 (lambda (x) (+ x 8)) '(0 1 2 3 4 5)) => '(0 9 2 3 4 5) (--update-at 2 (length it) '("foo" "bar" "baz" "quux")) => '("foo" "bar" 3 "quux") -- Function: -remove-at (n list) Return a list with element at Nth position in LIST removed. See also: `-remove-at-indices' (*note -remove-at-indices::), `-remove' (*note -remove::) (-remove-at 0 '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4" "5") (-remove-at 1 '("0" "1" "2" "3" "4" "5")) => '("0" "2" "3" "4" "5") (-remove-at 2 '("0" "1" "2" "3" "4" "5")) => '("0" "1" "3" "4" "5") -- Function: -remove-at-indices (indices list) Return a list whose elements are elements from LIST without elements selected as `(nth i list)` for all i from INDICES. See also: `-remove-at' (*note -remove-at::), `-remove' (*note -remove::) (-remove-at-indices '(0) '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4" "5") (-remove-at-indices '(0 2 4) '("0" "1" "2" "3" "4" "5")) => '("1" "3" "5") (-remove-at-indices '(0 5) '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4")  File: dash.info, Node: Reductions, Next: Unfolding, Prev: List to list, Up: Functions 2.4 Reductions ============== Functions reducing lists into single value. -- Function: -reduce-from (fn initial-value list) Return the result of applying FN to INITIAL-VALUE and the first item in LIST, then applying FN to that result and the 2nd item, etc. If LIST contains no items, return INITIAL-VALUE and FN is not called. In the anaphoric form `--reduce-from', the accumulated value is exposed as `acc`. See also: `-reduce' (*note -reduce::), `-reduce-r' (*note -reduce-r::) (-reduce-from '- 10 '(1 2 3)) => 4 (-reduce-from (lambda (memo item) (concat "(" memo " - " (int-to-string item) ")")) "10" '(1 2 3)) => "(((10 - 1) - 2) - 3)" (--reduce-from (concat acc " " it) "START" '("a" "b" "c")) => "START a b c" -- Function: -reduce-r-from (fn initial-value list) Replace conses with FN, nil with INITIAL-VALUE and evaluate the resulting expression. If LIST is empty, INITIAL-VALUE is returned and FN is not called. Note: this function works the same as `-reduce-from' (*note -reduce-from::) but the operation associates from right instead of from left. See also: `-reduce-r' (*note -reduce-r::), `-reduce' (*note -reduce::) (-reduce-r-from '- 10 '(1 2 3)) => -8 (-reduce-r-from (lambda (item memo) (concat "(" (int-to-string item) " - " memo ")")) "10" '(1 2 3)) => "(1 - (2 - (3 - 10)))" (--reduce-r-from (concat it " " acc) "END" '("a" "b" "c")) => "a b c END" -- Function: -reduce (fn list) Return the result of applying FN to the first 2 items in LIST, then applying FN to that result and the 3rd item, etc. If LIST contains no items, FN must accept no arguments as well, and reduce return the result of calling FN with no arguments. If LIST has only 1 item, it is returned and FN is not called. In the anaphoric form `--reduce', the accumulated value is exposed as `acc`. See also: `-reduce-from' (*note -reduce-from::), `-reduce-r' (*note -reduce-r::) (-reduce '- '(1 2 3 4)) => -8 (-reduce (lambda (memo item) (format "%s-%s" memo item)) '(1 2 3)) => "1-2-3" (--reduce (format "%s-%s" acc it) '(1 2 3)) => "1-2-3" -- Function: -reduce-r (fn list) Replace conses with FN and evaluate the resulting expression. The final nil is ignored. If LIST contains no items, FN must accept no arguments as well, and reduce return the result of calling FN with no arguments. If LIST has only 1 item, it is returned and FN is not called. The first argument of FN is the new item, the second is the accumulated value. Note: this function works the same as `-reduce' (*note -reduce::) but the operation associates from right instead of from left. See also: `-reduce-r-from' (*note -reduce-r-from::), `-reduce' (*note -reduce::) (-reduce-r '- '(1 2 3 4)) => -2 (-reduce-r (lambda (item memo) (format "%s-%s" memo item)) '(1 2 3)) => "3-2-1" (--reduce-r (format "%s-%s" acc it) '(1 2 3)) => "3-2-1" -- Function: -count (pred list) Counts the number of items in LIST where (PRED item) is non-nil. (-count 'even? '(1 2 3 4 5)) => 2 (--count (< it 4) '(1 2 3 4)) => 3 -- Function: -sum (list) Return the sum of LIST. (-sum '()) => 0 (-sum '(1)) => 1 (-sum '(1 2 3 4)) => 10 -- Function: -product (list) Return the product of LIST. (-product '()) => 1 (-product '(1)) => 1 (-product '(1 2 3 4)) => 24 -- Function: -min (list) Return the smallest value from LIST of numbers or markers. (-min '(0)) => 0 (-min '(3 2 1)) => 1 (-min '(1 2 3)) => 1 -- Function: -min-by (comparator list) Take a comparison function COMPARATOR and a LIST and return the least element of the list by the comparison function. See also combinator `-on' (*note -on::) which can transform the values before comparing them. (-min-by '> '(4 3 6 1)) => 1 (--min-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) => '(1 2 3) (--min-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) => '(2) -- Function: -max (list) Return the largest value from LIST of numbers or markers. (-max '(0)) => 0 (-max '(3 2 1)) => 3 (-max '(1 2 3)) => 3 -- Function: -max-by (comparator list) Take a comparison function COMPARATOR and a LIST and return the greatest element of the list by the comparison function. See also combinator `-on' (*note -on::) which can transform the values before comparing them. (-max-by '> '(4 3 6 1)) => 6 (--max-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) => '(3 2) (--max-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) => '(1 2 3)  File: dash.info, Node: Unfolding, Next: Predicates, Prev: Reductions, Up: Functions 2.5 Unfolding ============= Operations dual to reductions, building lists from seed value rather than consuming a list to produce a single value. -- Function: -iterate (fun init n) Return a list of iterated applications of FUN to INIT. This means a list of form: (init (fun init) (fun (fun init)) ...) N is the length of the returned list. (-iterate '1+ 1 10) => '(1 2 3 4 5 6 7 8 9 10) (-iterate (lambda (x) (+ x x)) 2 5) => '(2 4 8 16 32) (--iterate (* it it) 2 5) => '(2 4 16 256 65536) -- Function: -unfold (fun seed) Build a list from SEED using FUN. This is "dual" operation to `-reduce-r' (*note -reduce-r::): while -reduce-r consumes a list to produce a single value, `-unfold' (*note -unfold::) takes a seed value and builds a (potentially infinite!) list. FUN should return `nil' to stop the generating process, or a cons (A . B), where A will be prepended to the result and B is the new seed. (-unfold (lambda (x) (unless (= x 0) (cons x (1- x)))) 10) => '(10 9 8 7 6 5 4 3 2 1) (--unfold (when it (cons it (cdr it))) '(1 2 3 4)) => '((1 2 3 4) (2 3 4) (3 4) (4)) (--unfold (when it (cons it (butlast it))) '(1 2 3 4)) => '((1 2 3 4) (1 2 3) (1 2) (1))  File: dash.info, Node: Predicates, Next: Partitioning, Prev: Unfolding, Up: Functions 2.6 Predicates ============== -- Function: -any? (pred list) Return t if (PRED x) is non-nil for any x in LIST, else nil. Alias: `-any-p', `-some?', `-some-p' (-any? 'even? '(1 2 3)) => t (-any? 'even? '(1 3 5)) => nil (--any? (= 0 (% it 2)) '(1 2 3)) => t -- Function: -all? (pred list) Return t if (PRED x) is non-nil for all x in LIST, else nil. Alias: `-all-p', `-every?', `-every-p' (-all? 'even? '(1 2 3)) => nil (-all? 'even? '(2 4 6)) => t (--all? (= 0 (% it 2)) '(2 4 6)) => t -- Function: -none? (pred list) Return t if (PRED x) is nil for all x in LIST, else nil. Alias: `-none-p' (-none? 'even? '(1 2 3)) => nil (-none? 'even? '(1 3 5)) => t (--none? (= 0 (% it 2)) '(1 2 3)) => nil -- Function: -only-some? (pred list) Return `t` if at least one item of LIST matches PRED and at least one item of LIST does not match PRED. Return `nil` both if all items match the predicate or if none of the items match the predicate. Alias: `-only-some-p' (-only-some? 'even? '(1 2 3)) => t (-only-some? 'even? '(1 3 5)) => nil (-only-some? 'even? '(2 4 6)) => nil -- Function: -contains? (list element) Return non-nil if LIST contains ELEMENT. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil. Alias: `-contains-p' (-contains? '(1 2 3) 1) => t (-contains? '(1 2 3) 2) => t (-contains? '(1 2 3) 4) => nil -- Function: -same-items? (list list2) Return true if LIST and LIST2 has the same items. The order of the elements in the lists does not matter. Alias: `-same-items-p' (-same-items? '(1 2 3) '(1 2 3)) => t (-same-items? '(1 2 3) '(3 2 1)) => t (-same-items? '(1 2 3) '(1 2 3 4)) => nil -- Function: -is-prefix? (prefix list) Return non-nil if PREFIX is prefix of LIST. Alias: `-is-prefix-p' (-is-prefix? '(1 2 3) '(1 2 3 4 5)) => t (-is-prefix? '(1 2 3 4 5) '(1 2 3)) => nil (-is-prefix? '(1 3) '(1 2 3 4 5)) => nil -- Function: -is-suffix? (suffix list) Return non-nil if SUFFIX is suffix of LIST. Alias: `-is-suffix-p' (-is-suffix? '(3 4 5) '(1 2 3 4 5)) => t (-is-suffix? '(1 2 3 4 5) '(3 4 5)) => nil (-is-suffix? '(3 5) '(1 2 3 4 5)) => nil -- Function: -is-infix? (infix list) Return non-nil if INFIX is infix of LIST. This operation runs in O(n^2) time Alias: `-is-infix-p' (-is-infix? '(1 2 3) '(1 2 3 4 5)) => t (-is-infix? '(2 3 4) '(1 2 3 4 5)) => t (-is-infix? '(3 4 5) '(1 2 3 4 5)) => t  File: dash.info, Node: Partitioning, Next: Indexing, Prev: Predicates, Up: Functions 2.7 Partitioning ================ Functions partitioning the input list into a list of lists. -- Function: -split-at (n list) Return a list of ((-take N LIST) (-drop N LIST)), in no more than one pass through the list. (-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) -- Function: -split-with (pred list) Return a list of ((-take-while PRED LIST) (-drop-while PRED LIST)), in no more than one pass through the list. (-split-with 'even? '(1 2 3 4)) => '(nil (1 2 3 4)) (-split-with 'even? '(2 4 5 6)) => '((2 4) (5 6)) (--split-with (< it 4) '(1 2 3 4 3 2 1)) => '((1 2 3) (4 3 2 1)) -- Function: -split-on (item list) Split the LIST each time ITEM is found. Unlike `-partition-by' (*note -partition-by::), the ITEM is discarded from the results. Empty lists are also removed from the result. Comparison is done by `equal'. See also `-split-when' (*note -split-when::) (-split-on '| '(Nil | Leaf a | Node [Tree a])) => '((Nil) (Leaf a) (Node [Tree a])) (-split-on ':endgroup '("a" "b" :endgroup "c" :endgroup "d" "e")) => '(("a" "b") ("c") ("d" "e")) (-split-on ':endgroup '("a" "b" :endgroup :endgroup "d" "e")) => '(("a" "b") ("d" "e")) -- Function: -split-when (fn list) Split the LIST on each element where FN returns non-nil. Unlike `-partition-by' (*note -partition-by::), the "matched" element is discarded from the results. Empty lists are also removed from the result. This function can be thought of as a generalization of `split-string'. (-split-when 'even? '(1 2 3 4 5 6)) => '((1) (3) (5)) (-split-when 'even? '(1 2 3 4 6 8 9)) => '((1) (3) (9)) (--split-when (memq it '(&optional &rest)) '(a b &optional c d &rest args)) => '((a b) (c d) (args)) -- Function: -separate (pred list) Return a list of ((-filter PRED LIST) (-remove PRED LIST)), in one pass through the list. (-separate (lambda (num) (= 0 (% num 2))) '(1 2 3 4 5 6 7)) => '((2 4 6) (1 3 5 7)) (--separate (< it 5) '(3 7 5 9 3 2 1 4 6)) => '((3 3 2 1 4) (7 5 9 6)) (-separate 'cdr '((1 2) (1) (1 2 3) (4))) => '(((1 2) (1 2 3)) ((1) (4))) -- Function: -partition (n list) Return a new list with the items in LIST grouped into N-sized sublists. If there are not enough items to make the last group N-sized, those items are discarded. (-partition 2 '(1 2 3 4 5 6)) => '((1 2) (3 4) (5 6)) (-partition 2 '(1 2 3 4 5 6 7)) => '((1 2) (3 4) (5 6)) (-partition 3 '(1 2 3 4 5 6 7)) => '((1 2 3) (4 5 6)) -- Function: -partition-all (n list) Return a new list with the items in LIST grouped into N-sized sublists. The last group may contain less than N items. (-partition-all 2 '(1 2 3 4 5 6)) => '((1 2) (3 4) (5 6)) (-partition-all 2 '(1 2 3 4 5 6 7)) => '((1 2) (3 4) (5 6) (7)) (-partition-all 3 '(1 2 3 4 5 6 7)) => '((1 2 3) (4 5 6) (7)) -- Function: -partition-in-steps (n step list) Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. If there are not enough items to make the last group N-sized, those items are discarded. (-partition-in-steps 2 1 '(1 2 3 4)) => '((1 2) (2 3) (3 4)) (-partition-in-steps 3 2 '(1 2 3 4)) => '((1 2 3)) (-partition-in-steps 3 2 '(1 2 3 4 5)) => '((1 2 3) (3 4 5)) -- Function: -partition-all-in-steps (n step list) Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. The last groups may contain less than N items. (-partition-all-in-steps 2 1 '(1 2 3 4)) => '((1 2) (2 3) (3 4) (4)) (-partition-all-in-steps 3 2 '(1 2 3 4)) => '((1 2 3) (3 4)) (-partition-all-in-steps 3 2 '(1 2 3 4 5)) => '((1 2 3) (3 4 5) (5)) -- Function: -partition-by (fn list) Apply FN to each item in LIST, splitting it each time FN returns a new value. (-partition-by 'even? '()) => '() (-partition-by 'even? '(1 1 2 2 2 3 4 6 8)) => '((1 1) (2 2 2) (3) (4 6 8)) (--partition-by (< it 3) '(1 2 3 4 3 2 1)) => '((1 2) (3 4 3) (2 1)) -- Function: -partition-by-header (fn list) Apply FN to the first item in LIST. That is the header value. Apply FN to each item in LIST, splitting it each time FN returns the header value, but only after seeing at least one other value (the body). (--partition-by-header (= it 1) '(1 2 3 1 2 1 2 3 4)) => '((1 2 3) (1 2) (1 2 3 4)) (--partition-by-header (> it 0) '(1 2 0 1 0 1 2 3 0)) => '((1 2 0) (1 0) (1 2 3 0)) (-partition-by-header 'even? '(2 1 1 1 4 1 3 5 6 6 1)) => '((2 1 1 1) (4 1 3 5) (6 6 1)) -- Function: -group-by (fn list) Separate LIST into an alist whose keys are FN applied to the elements of LIST. Keys are compared by `equal'. (-group-by 'even? '()) => '() (-group-by 'even? '(1 1 2 2 2 3 4 6 8)) => '((nil 1 1 3) (t 2 2 2 4 6 8)) (--group-by (car (split-string it "/")) '("a/b" "c/d" "a/e")) => '(("a" "a/b" "a/e") ("c" "c/d"))  File: dash.info, Node: Indexing, Next: Set operations, Prev: Partitioning, Up: Functions 2.8 Indexing ============ Return indices of elements based on predicates, sort elements by indices etc. -- Function: -elem-index (elem list) Return the index of the first element in the given LIST which is equal to the query element ELEM, or nil if there is no such element. (-elem-index 2 '(6 7 8 2 3 4)) => 3 (-elem-index "bar" '("foo" "bar" "baz")) => 1 (-elem-index '(1 2) '((3) (5 6) (1 2) nil)) => 2 -- Function: -elem-indices (elem list) Return the indices of all elements in LIST equal to the query element ELEM, in ascending order. (-elem-indices 2 '(6 7 8 2 3 4 2 1)) => '(3 6) (-elem-indices "bar" '("foo" "bar" "baz")) => '(1) (-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) => '(1 3) -- Function: -find-index (pred list) Take a predicate PRED and a LIST and return the index of the first element in the list satisfying the predicate, or nil if there is no such element. (-find-index 'even? '(2 4 1 6 3 3 5 8)) => 0 (--find-index (< 5 it) '(2 4 1 6 3 3 5 8)) => 3 (-find-index (-partial 'string-lessp "baz") '("bar" "foo" "baz")) => 1 -- Function: -find-last-index (pred list) Take a predicate PRED and a LIST and return the index of the last element in the list satisfying the predicate, or nil if there is no such element. (-find-last-index 'even? '(2 4 1 6 3 3 5 8)) => 7 (--find-last-index (< 5 it) '(2 7 1 6 3 8 5 2)) => 5 (-find-last-index (-partial 'string-lessp "baz") '("q" "foo" "baz")) => 1 -- Function: -find-indices (pred list) Return the indices of all elements in LIST satisfying the predicate PRED, in ascending order. (-find-indices 'even? '(2 4 1 6 3 3 5 8)) => '(0 1 3 7) (--find-indices (< 5 it) '(2 4 1 6 3 3 5 8)) => '(3 7) (-find-indices (-partial 'string-lessp "baz") '("bar" "foo" "baz")) => '(1) -- Function: -grade-up (comparator list) Grade elements of LIST using COMPARATOR relation, yielding a permutation vector such that applying this permutation to LIST sorts it in ascending order. (-grade-up '< '(3 1 4 2 1 3 3)) => '(1 4 3 0 5 6 2) (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-up '< l) l)) => '(1 1 2 3 3 3 4) -- Function: -grade-down (comparator list) Grade elements of LIST using COMPARATOR relation, yielding a permutation vector such that applying this permutation to LIST sorts it in descending order. (-grade-down '< '(3 1 4 2 1 3 3)) => '(2 0 5 6 3 1 4) (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-down '< l) l)) => '(4 3 3 3 2 1 1)  File: dash.info, Node: Set operations, Next: Other list operations, Prev: Indexing, Up: Functions 2.9 Set operations ================== Operations pretending lists are sets. -- Function: -union (list list2) Return a new list containing the elements of LIST1 and elements of LIST2 that are not in LIST1. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil. (-union '(1 2 3) '(3 4 5)) => '(1 2 3 4 5) (-union '(1 2 3 4) '()) => '(1 2 3 4) (-union '(1 1 2 2) '(3 2 1)) => '(1 1 2 2 3) -- Function: -difference (list list2) Return a new list with only the members of LIST that are not in LIST2. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil. (-difference '() '()) => '() (-difference '(1 2 3) '(4 5 6)) => '(1 2 3) (-difference '(1 2 3 4) '(3 4 5 6)) => '(1 2) -- Function: -intersection (list list2) Return a new list containing only the elements that are members of both LIST and LIST2. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil. (-intersection '() '()) => '() (-intersection '(1 2 3) '(4 5 6)) => '() (-intersection '(1 2 3 4) '(3 4 5 6)) => '(3 4) -- Function: -distinct (list) Return a new list with all duplicates removed. The test for equality is done with `equal', or with `-compare-fn' if that's non-nil. Alias: `-uniq' (-distinct '()) => '() (-distinct '(1 2 2 4)) => '(1 2 4)  File: dash.info, Node: Other list operations, Next: Tree operations, Prev: Set operations, Up: Functions 2.10 Other list operations ========================== Other list functions not fit to be classified elsewhere. -- Function: -rotate (n list) Rotate LIST N places to the right. With N negative, rotate to the left. The time complexity is O(n). (-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) -- Function: -repeat (n x) Return a list with X repeated N times. Return nil if N is less than 1. (-repeat 3 :a) => '(:a :a :a) (-repeat 1 :a) => '(:a) (-repeat 0 :a) => nil -- Function: -cons* (&rest args) Make a new list from the elements of ARGS. The last 2 members of ARGS are used as the final cons of the result so if the final member of ARGS is not a list the result is a dotted list. (-cons* 1 2) => '(1 . 2) (-cons* 1 2 3) => '(1 2 . 3) (-cons* 1) => 1 -- Function: -snoc (list elem &rest elements) Append ELEM to the end of the list. This is like `cons', but operates on the end of list. If ELEMENTS is non nil, append these to the list as well. (-snoc '(1 2 3) 4) => '(1 2 3 4) (-snoc '(1 2 3) 4 5 6) => '(1 2 3 4 5 6) (-snoc '(1 2 3) '(4 5 6)) => '(1 2 3 (4 5 6)) -- Function: -interpose (sep list) Return a new list of all elements in LIST separated by SEP. (-interpose "-" '()) => '() (-interpose "-" '("a")) => '("a") (-interpose "-" '("a" "b" "c")) => '("a" "-" "b" "-" "c") -- Function: -interleave (&rest lists) Return a new list of the first item in each list, then the second etc. (-interleave '(1 2) '("a" "b")) => '(1 "a" 2 "b") (-interleave '(1 2) '("a" "b") '("A" "B")) => '(1 "a" "A" 2 "b" "B") (-interleave '(1 2 3) '("a" "b")) => '(1 "a" 2 "b") -- Function: -zip-with (fn list1 list2) Zip the two lists LIST1 and LIST2 using a function FN. This function is applied pairwise taking as first argument element of LIST1 and as second argument element of LIST2 at corresponding position. The anaphoric form `--zip-with' binds the elements from LIST1 as `it`, and the elements from LIST2 as `other`. (-zip-with '+ '(1 2 3) '(4 5 6)) => '(5 7 9) (-zip-with 'cons '(1 2 3) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) (--zip-with (concat it " and " other) '("Batman" "Jekyll") '("Robin" "Hyde")) => '("Batman and Robin" "Jekyll and Hyde") -- Function: -zip (&rest lists) Zip LISTS together. Group the head of each list, followed by the second elements of each list, and so on. The lengths of the returned groupings are equal to the length of the shortest input list. If two lists are provided as arguments, return the groupings as a list of cons cells. Otherwise, return the groupings as a list of lists. (-zip '(1 2 3) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) (-zip '(1 2 3) '(4 5 6 7)) => '((1 . 4) (2 . 5) (3 . 6)) (-zip '(1 2 3 4) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) -- Function: -zip-fill (fill-value &rest lists) Zip LISTS, with FILL-VALUE padded onto the shorter lists. The lengths of the returned groupings are equal to the length of the longest input list. (-zip-fill 0 '(1 2 3 4 5) '(6 7 8 9)) => '((1 . 6) (2 . 7) (3 . 8) (4 . 9) (5 . 0)) -- Function: -cycle (list) Return an infinite copy of LIST that will cycle through the elements and repeat from the beginning. (-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)) -- Function: -pad (fill-value &rest lists) Appends FILL-VALUE to the end of each list in LISTS such that they will all have the same length. (-pad 0 '()) => '(nil) (-pad 0 '(1)) => '((1)) (-pad 0 '(1 2 3) '(4 5)) => '((1 2 3) (4 5 0)) -- Function: -table (fn &rest lists) Compute outer product of LISTS using function FN. The function FN should have the same arity as the number of supplied lists. The outer product is computed by applying fn to all possible combinations created by taking one element from each list in order. The dimension of the result is (length lists). See also: `-table-flat' (*note -table-flat::) (-table '* '(1 2 3) '(1 2 3)) => '((1 2 3) (2 4 6) (3 6 9)) (-table (lambda (a b) (-sum (-zip-with '* a b))) '((1 2) (3 4)) '((1 3) (2 4))) => '((7 15) (10 22)) (apply '-table 'list (-repeat 3 '(1 2))) => '((((1 1 1) (2 1 1)) ((1 2 1) (2 2 1))) (((1 1 2) (2 1 2)) ((1 2 2) (2 2 2)))) -- Function: -table-flat (fn &rest lists) Compute flat outer product of LISTS using function FN. The function FN should have the same arity as the number of supplied lists. The outer product is computed by applying fn to all possible combinations created by taking one element from each list in order. The results are flattened, ignoring the tensor structure of the result. This is equivalent to calling: (-flatten-n (1- (length lists)) (-table fn lists)) but the implementation here is much more efficient. See also: `-flatten-n' (*note -flatten-n::), `-table' (*note -table::) (-table-flat 'list '(1 2 3) '(a b c)) => '((1 a) (2 a) (3 a) (1 b) (2 b) (3 b) (1 c) (2 c) (3 c)) (-table-flat '* '(1 2 3) '(1 2 3)) => '(1 2 3 2 4 6 3 6 9) (apply '-table-flat 'list (-repeat 3 '(1 2))) => '((1 1 1) (2 1 1) (1 2 1) (2 2 1) (1 1 2) (2 1 2) (1 2 2) (2 2 2)) -- Function: -first (pred list) Return the first x in LIST where (PRED x) is non-nil, else nil. To get the first item in the list no questions asked, use `car'. Alias: `-find' (-first 'even? '(1 2 3)) => 2 (-first 'even? '(1 3 5)) => nil (--first (> it 2) '(1 2 3)) => 3 -- Function: -some (pred list) Return (PRED x) for the first LIST item where (PRED x) is non-nil, else nil. Alias: `-any' (-some 'even? '(1 2 3)) => t (--some (member 'foo it) '((foo bar) (baz))) => '(foo bar) (--some (plist-get it :bar) '((:foo 1 :bar 2) (:baz 3))) => 2 -- Function: -last (pred list) Return the last x in LIST where (PRED x) is non-nil, else nil. (-last 'even? '(1 2 3 4 5 6 3 3 3)) => 6 (-last 'even? '(1 3 7 5 9)) => nil (--last (> (length it) 3) '("a" "looong" "word" "and" "short" "one")) => "short" -- Function: -first-item (list) Return the first item of LIST, or nil on an empty list. (-first-item '(1 2 3)) => 1 (-first-item nil) => nil -- Function: -last-item (list) Return the last item of LIST, or nil on an empty list. (-last-item '(1 2 3)) => 3 (-last-item nil) => nil -- Function: -butlast (list) Return a list of all items in list except for the last. (-butlast '(1 2 3)) => '(1 2) (-butlast '(1 2)) => '(1) (-butlast '(1)) => nil -- Function: -sort (comparator list) Sort LIST, stably, comparing elements using COMPARATOR. Return the sorted list. LIST is NOT modified by side effects. COMPARATOR is called with two elements of LIST, and should return non-nil if the first element should sort before the second. (-sort '< '(3 1 2)) => '(1 2 3) (-sort '> '(3 1 2)) => '(3 2 1) (--sort (< it other) '(3 1 2)) => '(1 2 3) -- Function: -list (&rest args) Return a list with ARGS. If first item of ARGS is already a list, simply return ARGS. If not, return a list with ARGS as elements. (-list 1) => '(1) (-list 1 2 3) => '(1 2 3) (-list '(1 2 3)) => '(1 2 3) -- Function: -fix (fn list) Compute the (least) fixpoint of FN with initial input LIST. FN is called at least once, results are compared with `equal'. (-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")  File: dash.info, Node: Tree operations, Next: Threading macros, Prev: Other list operations, Up: Functions 2.11 Tree operations ==================== Functions pretending lists are trees. -- Function: -tree-seq (branch children tree) Return a sequence of the nodes in TREE, in depth-first search order. BRANCH is a predicate of one argument that returns non-nil if the passed argument is a branch, that is, a node that can have children. CHILDREN is a function of one argument that returns the children of the passed branch node. Non-branch nodes are simply copied. (-tree-seq 'listp 'identity '(1 (2 3) 4 (5 (6 7)))) => '((1 (2 3) 4 (5 (6 7))) 1 (2 3) 2 3 4 (5 (6 7)) 5 (6 7) 6 7) (-tree-seq 'listp 'reverse '(1 (2 3) 4 (5 (6 7)))) => '((1 (2 3) 4 (5 (6 7))) (5 (6 7)) (6 7) 7 6 5 4 (2 3) 3 2 1) (--tree-seq (vectorp it) (append it nil) [1 [2 3] 4 [5 [6 7]]]) => '([1 [2 3] 4 [5 [6 7]]] 1 [2 3] 2 3 4 [5 [6 7]] 5 [6 7] 6 7) -- Function: -tree-map (fn tree) Apply FN to each element of TREE while preserving the tree structure. (-tree-map '1+ '(1 (2 3) (4 (5 6) 7))) => '(2 (3 4) (5 (6 7) 8)) (-tree-map '(lambda (x) (cons x (expt 2 x))) '(1 (2 3) 4)) => '((1 . 2) ((2 . 4) (3 . 8)) (4 . 16)) (--tree-map (length it) '("" ("

" "text" "

") "")) => '(6 (3 4 4) 7) -- Function: -tree-map-nodes (pred fun tree) Call FUN on each node of TREE that satisfies PRED. If PRED returns nil, continue descending down this node. If PRED returns non-nil, apply FUN to this node and do not descend further. (-tree-map-nodes 'vectorp (lambda (x) (-sum (append x nil))) '(1 [2 3] 4 (5 [6 7] 8))) => '(1 5 4 (5 13 8)) (-tree-map-nodes 'keywordp (lambda (x) (symbol-name x)) '(1 :foo 4 ((5 6 :bar) :baz 8))) => '(1 ":foo" 4 ((5 6 ":bar") ":baz" 8)) (--tree-map-nodes (eq (car-safe it) 'add-mode) (-concat it (list :mode 'emacs-lisp-mode)) '(with-mode emacs-lisp-mode (foo bar) (add-mode a b) (baz (add-mode c d)))) => '(with-mode emacs-lisp-mode (foo bar) (add-mode a b :mode emacs-lisp-mode) (baz (add-mode c d :mode emacs-lisp-mode))) -- Function: -tree-reduce (fn tree) Use FN to reduce elements of list TREE. If elements of TREE are lists themselves, apply the reduction recursively. FN is first applied to first element of the list and second element, then on this result and third element from the list etc. See `-reduce-r' (*note -reduce-r::) for how exactly are lists of zero or one element handled. (-tree-reduce '+ '(1 (2 3) (4 5))) => 15 (-tree-reduce 'concat '("strings" (" on" " various") ((" levels")))) => "strings on various levels" (--tree-reduce (cond ((stringp it) (concat it " " acc)) (t (let ((sn (symbol-name it))) (concat "<" sn ">" acc "")))) '(body (p "some words") (div "more" (b "bold") "words"))) => "

some words

more bold words
" -- Function: -tree-reduce-from (fn init-value tree) Use FN to reduce elements of list TREE. If elements of TREE are lists themselves, apply the reduction recursively. FN is first applied to INIT-VALUE and first element of the list, then on this result and second element from the list etc. The initial value is ignored on cons pairs as they always contain two elements. (-tree-reduce-from '+ 1 '(1 (1 1) ((1)))) => 8 (--tree-reduce-from (-concat acc (list it)) nil '(1 (2 3 (4 5)) (6 7))) => '((7 6) ((5 4) 3 2) 1) -- Function: -tree-mapreduce (fn folder tree) Apply FN to each element of TREE, and make a list of the results. If elements of TREE are lists themselves, apply FN recursively to elements of these nested lists. Then reduce the resulting lists using FOLDER and initial value INIT-VALUE. See `-reduce-r-from' (*note -reduce-r-from::). This is the same as calling `-tree-reduce' (*note -tree-reduce::) after `-tree-map' (*note -tree-map::) but is twice as fast as it only traverse the structure once. (-tree-mapreduce 'list 'append '(1 (2 (3 4) (5 6)) (7 (8 9)))) => '(1 2 3 4 5 6 7 8 9) (--tree-mapreduce 1 (+ it acc) '(1 (2 (4 9) (2 1)) (7 (4 3)))) => 9 (--tree-mapreduce 0 (max acc (1+ it)) '(1 (2 (4 9) (2 1)) (7 (4 3)))) => 3 -- Function: -tree-mapreduce-from (fn folder init-value tree) Apply FN to each element of TREE, and make a list of the results. If elements of TREE are lists themselves, apply FN recursively to elements of these nested lists. Then reduce the resulting lists using FOLDER and initial value INIT-VALUE. See `-reduce-r-from' (*note -reduce-r-from::). This is the same as calling `-tree-reduce-from' (*note -tree-reduce-from::) after `-tree-map' (*note -tree-map::) but is twice as fast as it only traverse the structure once. (-tree-mapreduce-from 'identity '* 1 '(1 (2 (3 4) (5 6)) (7 (8 9)))) => 362880 (--tree-mapreduce-from (+ it it) (cons it acc) nil '(1 (2 (4 9) (2 1)) (7 (4 3)))) => '(2 (4 (8 18) (4 2)) (14 (8 6))) (concat "{" (--tree-mapreduce-from (cond ((-cons-pair? it) (concat (symbol-name (car it)) " -> " (symbol-name (cdr it)))) (t (concat (symbol-name it) " : {"))) (concat it (unless (or (equal acc "}") (equal (substring it (1- (length it))) "{")) ", ") acc) "}" '((elips-mode (foo (bar . booze)) (baz . qux)) (c-mode (foo . bla) (bum . bam))))) => "{elips-mode : {foo : {bar -> booze{, baz -> qux{, c-mode : {foo -> bla, bum -> bam}}" -- Function: -clone (list) Create a deep copy of LIST. The new list has the same elements and structure but all cons are replaced with new ones. This is useful when you need to clone a structure such as plist or alist. (let* ((a '(1 2 3)) (b (-clone a))) (nreverse a) b) => '(1 2 3)  File: dash.info, Node: Threading macros, Next: Binding, Prev: Tree operations, Up: Functions 2.12 Threading macros ===================== -- Function: -> (x &optional form &rest more) Thread the expr through the forms. Insert X as the second item in the first form, making a list of it if it is not a list already. If there are more forms, insert the first form as the second item in second form, etc. (-> '(2 3 5)) => '(2 3 5) (-> '(2 3 5) (append '(8 13))) => '(2 3 5 8 13) (-> '(2 3 5) (append '(8 13)) (-slice 1 -1)) => '(3 5 8) -- Function: ->> (x &optional form &rest more) Thread the expr through the forms. Insert X as the last item in the first form, making a list of it if it is not a list already. If there are more forms, insert the first form as the last item in second form, etc. (->> '(1 2 3) (-map 'square)) => '(1 4 9) (->> '(1 2 3) (-map 'square) (-remove 'even?)) => '(1 9) (->> '(1 2 3) (-map 'square) (-reduce '+)) => 14 -- Function: -> (x form &rest more) Thread the expr through the forms. Insert X at the position signified by the token `it' in the first form. If there are more forms, insert the first form at the position signified by `it' in in second form, etc. (--> "def" (concat "abc" it "ghi")) => "abcdefghi" (--> "def" (concat "abc" it "ghi") (upcase it)) => "ABCDEFGHI" (--> "def" (concat "abc" it "ghi") upcase) => "ABCDEFGHI" -- Function: -some-> (x &optional form &rest more) When expr is non-nil, thread it through the first form (via `->' (*note ->::)), and when that result is non-nil, through the next form, etc. (-some-> '(2 3 5)) => '(2 3 5) (-some-> 5 square) => 25 (-some-> 5 even? square) => nil -- Function: -some->> (x &optional form &rest more) When expr is non-nil, thread it through the first form (via `->>' (*note ->>::)), and when that result is non-nil, through the next form, etc. (-some->> '(1 2 3) (-map 'square)) => '(1 4 9) (-some->> '(1 3 5) (-last 'even?) (+ 100)) => nil (-some->> '(2 4 6) (-last 'even?) (+ 100)) => 106 -- Function: -some-> (x &optional form &rest more) When expr in non-nil, thread it through the first form (via `-->' (*note -->::)), and when that result is non-nil, through the next form, etc. (-some--> "def" (concat "abc" it "ghi")) => "abcdefghi" (-some--> nil (concat "abc" it "ghi")) => nil (-some--> '(1 3 5) (-filter 'even? it) (append it it) (-map 'square it)) => nil  File: dash.info, Node: Binding, Next: Side-effects, Prev: Threading macros, Up: Functions 2.13 Binding ============ Convenient versions of `let` and `let*` constructs combined with flow control. -- Function: -when-let (var-val &rest body) If VAL evaluates to non-nil, bind it to VAR and execute body. VAR-VAL should be a (VAR VAL) pair. Note: binding is done according to `-let' (*note -let::). (-when-let (match-index (string-match "d" "abcd")) (+ match-index 2)) => 5 (-when-let ((&plist :foo foo) (list :foo "foo")) foo) => "foo" (-when-let ((&plist :foo foo) (list :bar "bar")) foo) => nil -- Function: -when-let* (vars-vals &rest body) If all VALS evaluate to true, bind them to their corresponding VARS and execute body. VARS-VALS should be a list of (VAR VAL) pairs. Note: binding is done according to `-let*' (*note -let*::). VALS are evaluated sequentially, and evaluation stops after the first nil VAL is encountered. (-when-let* ((x 5) (y 3) (z (+ y 4))) (+ x y z)) => 15 (-when-let* ((x 5) (y nil) (z 7)) (+ x y z)) => nil -- Function: -if-let (var-val then &rest else) If VAL evaluates to non-nil, bind it to VAR and do THEN, otherwise do ELSE. VAR-VAL should be a (VAR VAL) pair. Note: binding is done according to `-let' (*note -let::). (-if-let (match-index (string-match "d" "abc")) (+ match-index 3) 7) => 7 (--if-let (even? 4) it nil) => t -- Function: -if-let* (vars-vals then &rest else) If all VALS evaluate to true, bind them to their corresponding VARS and do THEN, otherwise do ELSE. VARS-VALS should be a list of (VAR VAL) pairs. Note: binding is done according to `-let*' (*note -let*::). VALS are evaluated sequentially, and evaluation stops after the first nil VAL is encountered. (-if-let* ((x 5) (y 3) (z 7)) (+ x y z) "foo") => 15 (-if-let* ((x 5) (y nil) (z 7)) (+ x y z) "foo") => "foo" (-if-let* (((_ _ x) '(nil nil 7))) x) => 7 -- Function: -let (varlist &rest body) Bind variables according to VARLIST then eval BODY. VARLIST is a list of lists of the form (PATTERN SOURCE). Each PATTERN is matched against the SOURCE "structurally". SOURCE is only evaluated once for each PATTERN. Each PATTERN is matched recursively, and can therefore contain sub-patterns which are matched against corresponding sub-expressions of SOURCE. All the SOURCEs are evalled before any symbols are bound (i.e. "in parallel"). If VARLIST only contains one (PATTERN SOURCE) element, you can optionally specify it using a vector and discarding the outer-most parens. Thus (-let ((PATTERN SOURCE)) ..) becomes (-let [PATTERN SOURCE] ..). `-let' (*note -let::) uses a convention of not binding places (symbols) starting with _ whenever it's possible. You can use this to skip over entries you don't care about. However, this is not *always* possible (as a result of implementation) and these symbols might get bound to undefined values. Following is the overview of supported patterns. Remember that patterns can be matched recursively, so every a, b, aK in the following can be a matching construct and not necessarily a symbol/variable. Symbol: a - bind the SOURCE to A. This is just like regular `let'. Conses and lists: (a) - bind `car' of cons/list to A (a . b) - bind car of cons to A and `cdr' to B (a b) - bind car of list to A and `cadr' to B (a1 a2 a3 ...) - bind 0th car of list to A1, 1st to A2, 2nd to A3 ... (a1 a2 a3 ... aN . rest) - as above, but bind the Nth cdr to REST. Vectors: [a] - bind 0th element of a non-list sequence to A (works with vectors, strings, bit arrays...) [a1 a2 a3 ...] - bind 0th element of non-list sequence to A0, 1st to A1, 2nd to A2, ... If the PATTERN is shorter than SOURCE, the values at places not in PATTERN are ignored. If the PATTERN is longer than SOURCE, an `error' is thrown. [a1 a2 a3 ... &rest rest] - as above, but bind the rest of the sequence to REST. This is conceptually the same as improper list matching (a1 a2 ... aN . rest) Key/value stores: (&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the SOURCE plist to aK. If the value is not found, aK is nil. (&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the SOURCE alist to aK. If the value is not found, aK is nil. (&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the SOURCE hash table to aK. If the value is not found, aK is nil. Further, special keyword &keys supports "inline" matching of plist-like key-value pairs, similarly to &keys keyword of `cl-defun'. (a1 a2 ... aN &keys key1 b1 ... keyN bK) This binds N values from the list to a1 ... aN, then interprets the cdr as a plist (see key/value matching above). You can name the source using the syntax SYMBOL &as PATTERN. This syntax works with lists (proper or improper), vectors and all types of maps. (list &as a b c) (list 1 2 3) binds A to 1, B to 2, C to 3 and LIST to (1 2 3). Similarly: (bounds &as beg . end) (cons 1 2) binds BEG to 1, END to 2 and BOUNDS to (1 . 2). (items &as first . rest) (list 1 2 3) binds FIRST to 1, REST to (2 3) and ITEMS to (1 2 3) [vect &as _ b c] [1 2 3] binds B to 2, C to 3 and VECT to [1 2 3] (_ avoids binding as usual). (plist &as &plist :b b) (list :a 1 :b 2 :c 3) binds B to 2 and PLIST to (:a 1 :b 2 :c 3). Same for &alist and &hash. This is especially useful when we want to capture the result of a computation and destructure at the same time. Consider the form (function-returning-complex-structure) returning a list of two vectors with two items each. We want to capture this entire result and pass it to another computation, but at the same time we want to get the second item from each vector. We can achieve it with pattern (result &as [_ a] [_ b]) (function-returning-complex-structure) Note: Clojure programmers may know this feature as the ":as binding". The difference is that we put the &as at the front because we need to support improper list binding. (-let (([a (b c) d] [1 (2 3) 4])) (list a b c d)) => '(1 2 3 4) (-let [(a b c . d) (list 1 2 3 4 5 6)] (list a b c d)) => '(1 2 3 (4 5 6)) (-let [(&plist :foo foo :bar bar) (list :baz 3 :foo 1 :qux 4 :bar 2)] (list foo bar)) => '(1 2) -- Function: -let* (varlist &rest body) Bind variables according to VARLIST then eval BODY. VARLIST is a list of lists of the form (PATTERN SOURCE). Each PATTERN is matched against the SOURCE structurally. SOURCE is only evaluated once for each PATTERN. Each SOURCE can refer to the symbols already bound by this VARLIST. This is useful if you want to destructure SOURCE recursively but also want to name the intermediate structures. See `-let' (*note -let::) for the list of all possible patterns. (-let* (((a . b) (cons 1 2)) ((c . d) (cons 3 4))) (list a b c d)) => '(1 2 3 4) (-let* (((a . b) (cons 1 (cons 2 3))) ((c . d) b)) (list a b c d)) => '(1 (2 . 3) 2 3) (-let* (((&alist "foo" foo "bar" bar) (list (cons "foo" 1) (cons "bar" (list 'a 'b 'c)))) ((a b c) bar)) (list foo a b c bar)) => '(1 a b c (a b c)) -- Function: -lambda (match-form &rest body) Return a lambda which destructures its input as MATCH-FORM and executes BODY. Note that you have to enclose the MATCH-FORM in a pair of parens, such that: (-lambda (x) body) (-lambda (x y ...) body) has the usual semantics of `lambda'. Furthermore, these get translated into normal lambda, so there is no performance penalty. See `-let' (*note -let::) for the description of destructuring mechanism. (-map (-lambda ((x y)) (+ x y)) '((1 2) (3 4) (5 6))) => '(3 7 11) (-map (-lambda ([x y]) (+ x y)) '([1 2] [3 4] [5 6])) => '(3 7 11) (funcall (-lambda ((_ . a) (_ . b)) (-concat a b)) '(1 2 3) '(4 5 6)) => '(2 3 5 6)  File: dash.info, Node: Side-effects, Next: Destructive operations, Prev: Binding, Up: Functions 2.14 Side-effects ================= Functions iterating over lists for side-effect only. -- Function: -each (list fn) Call FN with every item in LIST. Return nil, used for side-effects only. (let (s) (-each '(1 2 3) (lambda (item) (setq s (cons item s))))) => nil (let (s) (-each '(1 2 3) (lambda (item) (setq s (cons item s)))) s) => '(3 2 1) (let (s) (--each '(1 2 3) (setq s (cons it s))) s) => '(3 2 1) -- Function: -each-while (list pred fn) Call FN with every item in LIST while (PRED item) is non-nil. Return nil, used for side-effects only. (let (s) (-each-while '(2 4 5 6) 'even? (lambda (item) (!cons item s))) s) => '(4 2) (let (s) (--each-while '(1 2 3 4) (< it 3) (!cons it s)) s) => '(2 1) -- Function: -dotimes (num fn) Repeatedly calls FN (presumably for side-effects) passing in integers from 0 through NUM-1. (let (s) (-dotimes 3 (lambda (n) (!cons n s))) s) => '(2 1 0) (let (s) (--dotimes 5 (!cons it s)) s) => '(4 3 2 1 0)  File: dash.info, Node: Destructive operations, Next: Function combinators, Prev: Side-effects, Up: Functions 2.15 Destructive operations =========================== -- Function: !cons (car cdr) Destructive: Set CDR to the cons of CAR and CDR. (let (l) (!cons 5 l) l) => '(5) (let ((l '(3))) (!cons 5 l) l) => '(5 3) -- Function: !cdr (list) Destructive: Set LIST to the cdr of LIST. (let ((l '(3))) (!cdr l) l) => '() (let ((l '(3 5))) (!cdr l) l) => '(5)  File: dash.info, Node: Function combinators, Prev: Destructive operations, Up: Functions 2.16 Function combinators ========================= These combinators require Emacs 24 for its lexical scope. So they are offered in a separate package: `dash-functional`. -- Function: -partial (fn &rest args) Takes a function FN and fewer than the normal arguments to FN, and returns a fn that takes a variable number of additional ARGS. When called, the returned function calls FN with ARGS first and then additional args. (funcall (-partial '- 5) 3) => 2 (funcall (-partial '+ 5 2) 3) => 10 -- Function: -rpartial (fn &rest args) Takes a function FN and fewer than the normal arguments to FN, and returns a fn that takes a variable number of additional ARGS. When called, the returned function calls FN with the additional args first and then ARGS. (funcall (-rpartial '- 5) 8) => 3 (funcall (-rpartial '- 5 2) 10) => 3 -- Function: -juxt (&rest fns) Takes a list of functions and returns a fn that is the juxtaposition of those fns. The returned fn takes a variable number of args, and returns a list containing the result of applying each fn to the args (left-to-right). (funcall (-juxt '+ '-) 3 5) => '(8 -2) (-map (-juxt 'identity 'square) '(1 2 3)) => '((1 1) (2 4) (3 9)) -- Function: -compose (&rest fns) Takes a list of functions and returns a fn that is the composition of those fns. The returned fn takes a variable number of arguments, and returns the result of applying each fn to the result of applying the previous fn to the arguments (right-to-left). (funcall (-compose 'square '+) 2 3) => (square (+ 2 3)) (funcall (-compose 'identity 'square) 3) => (square 3) (funcall (-compose 'square 'identity) 3) => (square 3) -- Function: -applify (fn) Changes an n-arity function FN to a 1-arity function that expects a list with n items as arguments (-map (-applify '+) '((1 1 1) (1 2 3) (5 5 5))) => '(3 6 15) (-map (-applify (lambda (a b c) (\` ((\, a) ((\, b) ((\, c))))))) '((1 1 1) (1 2 3) (5 5 5))) => '((1 (1 (1))) (1 (2 (3))) (5 (5 (5)))) (funcall (-applify '<) '(3 6)) => t -- Function: -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). In types: (b -> b -> c) -> (a -> b) -> a -> a -> c (-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 'int-to-string) '(2 100 22)) => 22 -- Function: -flip (func) Swap the order of arguments for binary function FUNC. In types: (a -> b -> c) -> b -> a -> c (funcall (-flip '<) 2 1) => t (funcall (-flip '-) 3 8) => 5 (-sort (-flip '<) '(4 3 6 1)) => '(6 4 3 1) -- Function: -const (c) Return a function that returns C ignoring any additional arguments. In types: a -> b -> a (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 -- Function: -cut (&rest params) Take n-ary function and n arguments and specialize some of them. Arguments denoted by <> will be left unspecialized. See SRFI-26 for detailed description. (funcall (-cut list 1 <> 3 <> 5) 2 4) => '(1 2 3 4 5) (-map (-cut funcall <> 5) '(1+ 1- (lambda (x) (/ 1.0 x)))) => '(6 4 0.2) (-filter (-cut < <> 5) '(1 3 5 7 9)) => '(1 3) -- Function: -not (pred) Take an unary predicates PRED and return an unary predicate that returns t if PRED returns nil and nil if PRED returns non-nil. (funcall (-not 'even?) 5) => t (-filter (-not (-partial '< 4)) '(1 2 3 4 5 6 7 8)) => '(1 2 3 4) -- Function: -orfn (&rest preds) Take list of unary predicates PREDS and return an unary predicate with argument x that returns non-nil if at least one of the PREDS returns non-nil on x. In types: [a -> Bool] -> a -> Bool (-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 -- Function: -andfn (&rest preds) Take list of unary predicates PREDS and return an 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 (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) -- Function: -iteratefn (fn n) Return a function FN composed N times with itself. FN is a unary function. If you need to use a function of higher arity, use `-applify' (*note -applify::) first to turn it into an unary function. With n = 0, this acts as identity function. In types: (a -> a) -> Int -> a -> a. This function satisfies the following law: (funcall (-iteratefn fn n) init) = (-last-item (-iterate fn init (1+ n))). (funcall (-iteratefn (lambda (x) (* x x)) 3) 2) => 256 (funcall (-iteratefn '1+ 3) 1) => 4 (funcall (-iteratefn 'cdr 3) '(1 2 3 4 5)) => '(4 5) -- Function: -fixfn (fn &optional equal-test halt-test) Return a function that computes the (least) fixpoint of FN. FN must be a unary function. The returned lambda takes a single argument, X, the initial value for the fixpoint iteration. The iteration halts when either of the following conditions is satisified: 1. Iteration converges to the fixpoint, with equality being tested using EQUAL-TEST. If EQUAL-TEST is not specified, `equal' is used. For functions over the floating point numbers, it may be necessary to provide an appropriate appoximate comparsion test. 2. HALT-TEST returns a non-nil value. HALT-TEST defaults to a simple counter that returns t after `-fixfn-max-iterations', to guard against infinite iteration. Otherwise, HALT-TEST must be a function that accepts a single argument, the current value of X, and returns non-nil as long as iteration should continue. In this way, a more sophisticated convergence test may be supplied by the caller. The return value of the lambda is either the fixpoint or, if iteration halted before converging, a cons with car `halted' and cdr the final output from HALT-TEST. In types: (a -> a) -> a -> a. (funcall (-fixfn 'cos 'approx-equal) 0.7) => 0.7390851332151607 (funcall (-fixfn (lambda (x) (expt (+ x 10) 0.25))) 2.0) => 1.8555845286409378 (funcall (-fixfn 'sin 'approx-equal) 0.1) => '(halted . t) -- Function: -prodfn (&rest fns) Take a list of n functions and return a function that takes a list of length n, applying i-th function to i-th element of the input list. Returns a list of length n. In types (for n=2): ((a -> b), (c -> d)) -> (a, c) -> (b, d) This function satisfies the following laws: (-compose (-prodfn f g ...) (-prodfn f' g' ...)) = (-prodfn (-compose f f') (-compose g g') ...) (-prodfn f g ...) = (-juxt (-compose f (-partial 'nth 0)) (-compose g (-partial 'nth 1)) ...) (-compose (-prodfn f g ...) (-juxt f' g' ...)) = (-juxt (-compose f f') (-compose g g') ...) (-compose (-partial 'nth n) (-prod f1 f2 ...)) = (-compose fn (-partial 'nth n)) (funcall (-prodfn '1+ '1- 'int-to-string) '(1 2 3)) => '(2 1 "3") (-map (-prodfn '1+ '1-) '((1 2) (3 4) (5 6) (7 8))) => '((2 1) (4 3) (6 5) (8 7)) (apply '+ (funcall (-prodfn 'length 'string-to-int) '((1 2 3) "15"))) => 18  File: dash.info, Node: Development, Next: Index, Prev: Functions, Up: Top 3 Development ************* The dash repository is hosted on GitHub: `https://github.com/magnars/dash.el' * Menu: * Contribute:: How to contribute * Changes:: List of significant changes by version * Contributors:: List of contributors  File: dash.info, Node: Contribute, Next: Changes, Up: Development 3.1 Contribute ============== Yes, please do. Pure functions in the list manipulation realm only, please. There's a suite of tests in dev/examples.el, so remember to add tests for your function, or it might get broken later. Run the tests with `./run-tests.sh'. Create the docs with `./create-docs.sh'. I highly recommend that you install these as a pre-commit hook, so that the tests are always running and the docs are always in sync: cp pre-commit.sh .git/hooks/pre-commit Oh, and don't edit `README.md' directly, it is auto-generated. Change `readme-template.md' or `examples-to-docs.el' instead. The same goes for the info manual.  File: dash.info, Node: Changes, Next: Contributors, Prev: Contribute, Up: Development 3.2 Changes =========== Changes in 2.10: * Add `-let' destructuring to `-if-let' and `-when-let' (Fredrik Bergroth) Changes in 2.9: * Add `-let', `-let*' and `-lambda' with destructuring * Add `-tree-seq' and `-tree-map-nodes' * Add `-non-nil' * Add `-fix' * Add `-fixfn' (dash-functional 1.2) * Add `-copy' (Wilfred Hughes) Changes in 2.8: * Add `-butlast' Changes in 2.7: * `-zip' now supports more than two lists (Steve Lamb) * Add `-cycle', `-pad', `-annotate', `-zip-fill' (Steve Lamb) * Add `-table', `-table-flat' (finite cartesian product) * Add `-flatten-n' * `-slice' now supports "step" argument * Add functional combinators `-iteratefn', `-prodfn' * Add `-replace', `-splice', `-splice-list' which generalize `-replace-at' and `-insert-at' * Add `-compose', `-iteratefn' and `-prodfn' (dash-functional 1.1) Changes in 2.6: * Add `-is-prefix-p', `-is-suffix-p', `-is-infix-p' (Matus Goljer) * Add `-iterate', `-unfold' (Matus Goljer) * Add `-split-on', `-split-when' (Matus Goljer) * Add `-find-last-index' (Matus Goljer) * Add `-list' (Johan Andersson) Changes in 2.5: * Add `-same-items?' (Johan Andersson) * A few bugfixes Changes in 2.4: * Add `-snoc' (Matus Goljer) * Add `-replace-at', `-update-at', `-remove-at', and `-remove-at-indices' (Matus Goljer) Changes in 2.3: * Add tree operations (Matus Goljer) * Make font-lock optional Changes in 2.2: * Add `-compose' (Christina Whyte) Changes in 2.1: * Add indexing operations (Matus Goljer) Changes in 2.0: * Split out `dash-functional.el' (Matus Goljer) * Add `-andfn', `-orfn', `-not', `-cut', `-const', `-flip' and `-on'. (Matus Goljer) * Fix `-min', `-max', `-min-by' and `-max-by' (Matus Goljer) Changes in 1.8: * Add `-first-item' and `-last-item' (Wilfred Hughes) Changes in 1.7: * Add `-rotate' (Matus Goljer) Changes in 1.6: * Add `-min', `-max', `-min-by' and `-max-by' (Johan Andersson) Changes in 1.5: * Add `-sum' and `-product' (Johan Andersson) Changes in 1.4: * Add `-sort' * Add `-reduce-r' (Matus Goljer) * Add `-reduce-r-from' (Matus Goljer) Changes in 1.3: * Add `-partition-in-steps' * Add `-partition-all-in-steps' Changes in 1.2: * Add `-last' (Matus Goljer) * Add `-insert-at' (Emanuel Evans) * Add `-when-let' and `-if-let' (Emanuel Evans) * Add `-when-let*' and `-if-let*' (Emanuel Evans) * Some bugfixes  File: dash.info, Node: Contributors, Prev: Changes, Up: Development 3.3 Contributors ================ * Matus Goljer (https://github.com/Fuco1) contributed lots of features and functions. * Takafumi Arakaki (https://github.com/tkf) contributed `-group-by'. * tali713 (https://github.com/tali713) is the author of `-applify'. * Víctor M. Valenzuela (https://github.com/vemv) contributed `-repeat'. * Nic Ferrier (https://github.com/nicferrier) contributed `-cons*'. * Wilfred Hughes (https://github.com/Wilfred) contributed `-slice', `-first-item' and `-last-item'. * Emanuel Evans (https://github.com/shosti) contributed `-if-let', `-when-let' and `-insert-at'. * Johan Andersson (https://github.com/rejeep) contributed `-sum', `-product' and `-same-items?' * Christina Whyte (https://github.com/kurisuwhyte) contributed `-compose' * Steve Lamb (https://github.com/steventlamb) contributed `-cycle', `-pad', `-annotate', `-zip-fill' and an n-ary version of `-zip'. * Fredrik Bergroth (https://github.com/fbergroth) made the `-if-let' family use `-let' destructuring and improved script for generating documentation. * Mark Oteiza (https://github.com/holomorph) contributed the script to create an info manual. * Vasilij Schneidermann (https://github.com/wasamasa) contributed `-some'. * William West (https://github.com/occidens) made `-fixfn' more robust at handling floats. Thanks!  File: dash.info, Node: Index, Prev: Development, Up: Top Index ***** [index] * Menu: * !cdr: Destructive operations. (line 15) * !cons: Destructive operations. (line 7) * -->: Threading macros. (line 33) * ->: Threading macros. (line 7) * ->>: Threading macros. (line 20) * -all?: Predicates. (line 19) * -andfn: Function combinators. (line 141) * -annotate: Maps. (line 79) * -any?: Predicates. (line 7) * -applify: Function combinators. (line 57) * -butlast: Other list operations. (line 238) * -clone: Tree operations. (line 124) * -compose: Function combinators. (line 43) * -concat: List to list. (line 23) * -cons*: Other list operations. (line 29) * -const: Function combinators. (line 94) * -contains?: Predicates. (line 58) * -copy: Maps. (line 134) * -count: Reductions. (line 91) * -cut: Function combinators. (line 107) * -cycle: Other list operations. (line 119) * -difference: Set operations. (line 21) * -distinct: Set operations. (line 45) * -dotimes: Side-effects. (line 29) * -drop: Sublist selection. (line 108) * -drop-while: Sublist selection. (line 127) * -each: Side-effects. (line 9) * -each-while: Side-effects. (line 20) * -elem-index: Indexing. (line 10) * -elem-indices: Indexing. (line 22) * -filter: Sublist selection. (line 9) * -find-index: Indexing. (line 33) * -find-indices: Indexing. (line 57) * -find-last-index: Indexing. (line 45) * -first: Other list operations. (line 185) * -first-item: Other list operations. (line 222) * -fix: Other list operations. (line 275) * -fixfn: Function combinators. (line 178) * -flatten: List to list. (line 34) * -flatten-n: List to list. (line 56) * -flip: Function combinators. (line 82) * -grade-down: Indexing. (line 78) * -grade-up: Indexing. (line 68) * -group-by: Partitioning. (line 146) * -if-let: Binding. (line 37) * -if-let*: Binding. (line 48) * -insert-at: List to list. (line 110) * -interleave: Other list operations. (line 67) * -interpose: Other list operations. (line 57) * -intersection: Set operations. (line 33) * -is-infix?: Predicates. (line 111) * -is-prefix?: Predicates. (line 87) * -is-suffix?: Predicates. (line 99) * -iterate: Unfolding. (line 10) * -iteratefn: Function combinators. (line 155) * -juxt: Function combinators. (line 32) * -keep: List to list. (line 9) * -lambda: Binding. (line 224) * -last: Other list operations. (line 212) * -last-item: Other list operations. (line 230) * -let: Binding. (line 64) * -let*: Binding. (line 204) * -list: Other list operations. (line 262) * -map: Maps. (line 11) * -map-first: Maps. (line 39) * -map-indexed: Maps. (line 67) * -map-last: Maps. (line 53) * -map-when: Maps. (line 22) * -mapcat: Maps. (line 123) * -max: Reductions. (line 143) * -max-by: Reductions. (line 153) * -min: Reductions. (line 119) * -min-by: Reductions. (line 129) * -non-nil: Sublist selection. (line 77) * -none?: Predicates. (line 31) * -not: Function combinators. (line 120) * -on: Function combinators. (line 68) * -only-some?: Predicates. (line 43) * -orfn: Function combinators. (line 129) * -pad: Other list operations. (line 130) * -partial: Function combinators. (line 10) * -partition: Partitioning. (line 75) * -partition-all: Partitioning. (line 87) * -partition-all-in-steps: Partitioning. (line 110) * -partition-by: Partitioning. (line 122) * -partition-by-header: Partitioning. (line 133) * -partition-in-steps: Partitioning. (line 98) * -prodfn: Function combinators. (line 213) * -product: Reductions. (line 109) * -reduce: Reductions. (line 47) * -reduce-from: Reductions. (line 9) * -reduce-r: Reductions. (line 67) * -reduce-r-from: Reductions. (line 28) * -remove: Sublist selection. (line 22) * -remove-at: List to list. (line 147) * -remove-at-indices: List to list. (line 160) * -remove-first: Sublist selection. (line 35) * -remove-item: Sublist selection. (line 65) * -remove-last: Sublist selection. (line 50) * -repeat: Other list operations. (line 18) * -replace: List to list. (line 68) * -replace-at: List to list. (line 121) * -replace-first: List to list. (line 82) * -replace-last: List to list. (line 96) * -rotate: Other list operations. (line 9) * -rpartial: Function combinators. (line 21) * -same-items?: Predicates. (line 73) * -select-by-indices: Sublist selection. (line 138) * -separate: Partitioning. (line 64) * -slice: Sublist selection. (line 83) * -snoc: Other list operations. (line 43) * -some: Other list operations. (line 199) * -some-->: Threading macros. (line 70) * -some->: Threading macros. (line 46) * -some->>: Threading macros. (line 58) * -sort: Other list operations. (line 248) * -splice: Maps. (line 90) * -splice-list: Maps. (line 110) * -split-at: Partitioning. (line 9) * -split-on: Partitioning. (line 29) * -split-when: Partitioning. (line 47) * -split-with: Partitioning. (line 18) * -sum: Reductions. (line 99) * -table: Other list operations. (line 141) * -table-flat: Other list operations. (line 160) * -take: Sublist selection. (line 99) * -take-while: Sublist selection. (line 116) * -tree-map: Tree operations. (line 29) * -tree-map-nodes: Tree operations. (line 40) * -tree-mapreduce: Tree operations. (line 86) * -tree-mapreduce-from: Tree operations. (line 105) * -tree-reduce: Tree operations. (line 54) * -tree-reduce-from: Tree operations. (line 71) * -tree-seq: Tree operations. (line 9) * -unfold: Unfolding. (line 26) * -union: Set operations. (line 9) * -update-at: List to list. (line 134) * -when-let: Binding. (line 10) * -when-let*: Binding. (line 23) * -zip: Other list operations. (line 94) * -zip-fill: Other list operations. (line 111) * -zip-with: Other list operations. (line 78)  Tag Table: Node: Top965 Node: Installation2459 Node: Using in a package3028 Node: Syntax highlighting of dash functions3392 Node: Functions3776 Node: Maps4977 Ref: -map5273 Ref: -map-when5611 Ref: -map-first6185 Ref: -map-last6652 Ref: -map-indexed7115 Ref: -annotate7517 Ref: -splice8004 Ref: -splice-list8770 Ref: -mapcat9221 Ref: -copy9594 Node: Sublist selection9780 Ref: -filter9973 Ref: -remove10340 Ref: -remove-first10695 Ref: -remove-last11207 Ref: -remove-item11713 Ref: -non-nil12100 Ref: -slice12258 Ref: -take12787 Ref: -drop13039 Ref: -take-while13238 Ref: -drop-while13585 Ref: -select-by-indices13938 Node: List to list14445 Ref: -keep14632 Ref: -concat15128 Ref: -flatten15422 Ref: -flatten-n16164 Ref: -replace16544 Ref: -replace-first16996 Ref: -replace-last17481 Ref: -insert-at17959 Ref: -replace-at18276 Ref: -update-at18664 Ref: -remove-at19144 Ref: -remove-at-indices19621 Node: Reductions20188 Ref: -reduce-from20357 Ref: -reduce-r-from21116 Ref: -reduce21885 Ref: -reduce-r22665 Ref: -count23569 Ref: -sum23791 Ref: -product23977 Ref: -min24183 Ref: -min-by24406 Ref: -max24922 Ref: -max-by25144 Node: Unfolding25665 Ref: -iterate25904 Ref: -unfold26346 Node: Predicates27137 Ref: -any?27261 Ref: -all?27574 Ref: -none?27889 Ref: -only-some?28184 Ref: -contains?28654 Ref: -same-items?29026 Ref: -is-prefix?29404 Ref: -is-suffix?29720 Ref: -is-infix?30036 Node: Partitioning30383 Ref: -split-at30571 Ref: -split-with30854 Ref: -split-on31254 Ref: -split-when31918 Ref: -separate32547 Ref: -partition32986 Ref: -partition-all33435 Ref: -partition-in-steps33860 Ref: -partition-all-in-steps34354 Ref: -partition-by34836 Ref: -partition-by-header35215 Ref: -group-by35815 Node: Indexing36245 Ref: -elem-index36447 Ref: -elem-indices36839 Ref: -find-index37219 Ref: -find-last-index37659 Ref: -find-indices38116 Ref: -grade-up38521 Ref: -grade-down38922 Node: Set operations39330 Ref: -union39513 Ref: -difference39944 Ref: -intersection40348 Ref: -distinct40772 Node: Other list operations41080 Ref: -rotate41305 Ref: -repeat41598 Ref: -cons*41858 Ref: -snoc42242 Ref: -interpose42648 Ref: -interleave42943 Ref: -zip-with43309 Ref: -zip43997 Ref: -zip-fill44644 Ref: -cycle44965 Ref: -pad45335 Ref: -table45655 Ref: -table-flat46438 Ref: -first47427 Ref: -some47790 Ref: -last48153 Ref: -first-item48484 Ref: -last-item48681 Ref: -butlast48874 Ref: -sort49118 Ref: -list49608 Ref: -fix49936 Node: Tree operations50470 Ref: -tree-seq50666 Ref: -tree-map51521 Ref: -tree-map-nodes51961 Ref: -tree-reduce52813 Ref: -tree-reduce-from53688 Ref: -tree-mapreduce54287 Ref: -tree-mapreduce-from55131 Ref: -clone56401 Node: Threading macros56728 Ref: ->56873 Ref: ->>57363 Ref: -->57867 Ref: -some->58382 Ref: -some->>58752 Ref: -some-->59184 Node: Binding59650 Ref: -when-let59854 Ref: -when-let*60344 Ref: -if-let60867 Ref: -if-let*61258 Ref: -let61869 Ref: -let*66973 Ref: -lambda67910 Node: Side-effects68707 Ref: -each68901 Ref: -each-while69304 Ref: -dotimes69662 Node: Destructive operations69963 Ref: !cons70136 Ref: !cdr70343 Node: Function combinators70539 Ref: -partial70808 Ref: -rpartial71201 Ref: -juxt71601 Ref: -compose72030 Ref: -applify72584 Ref: -on73028 Ref: -flip73548 Ref: -const73857 Ref: -cut74198 Ref: -not74651 Ref: -orfn74962 Ref: -andfn75395 Ref: -iteratefn75888 Ref: -fixfn76585 Ref: -prodfn78140 Node: Development79187 Node: Contribute79536 Node: Changes80257 Node: Contributors82871 Node: Index84393  End Tag Table dash.el-2.12.1/dash.texi000066400000000000000000002635721261164447300147540ustar00rootroot00000000000000\input texinfo @c -*- texinfo -*- @c %**start of header @setfilename dash.info @settitle dash @documentencoding UTF-8 @documentlanguage en @syncodeindex fn cp @dircategory Emacs @direntry * Dash: (dash.info). A modern list library for GNU Emacs @end direntry @c %**end of header @copying This manual is for @code{dash.el} version 2.12.0. Copyright © 2012-2015 Free Software Foundation, Inc. @quotation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see @uref{http://www.gnu.org/licenses/}. @end quotation @end copying @finalout @titlepage @title Dash @author Magnar Sveen @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top dash @insertcopying @end ifnottex @menu * Installation:: * Functions:: * Development:: * Index:: @detailmenu --- The Detailed Node Listing --- Installation * Using in a package:: * Syntax highlighting of dash functions:: Functions * Maps:: * Sublist selection:: * List to list:: * Reductions:: * Unfolding:: * Predicates:: * Partitioning:: * Indexing:: * Set operations:: * Other list operations:: * Tree operations:: * Threading macros:: * Binding:: * Side-effects:: * Destructive operations:: * Function combinators:: Development * Contribute:: How to contribute * Changes:: List of significant changes by version * Contributors:: List of contributors @end detailmenu @end menu @node Installation @chapter Installation It's available on @uref{http://marmalade-repo.org/,marmalade} and @uref{http://melpa.milkbox.net/,Melpa}; use @code{M-x package-install}: @table @kbd @item M-x package-install @key{RET} dash Install the dash library. @end table @table @kbd @item M-x package-install @key{RET} dash-functional Optional, if you want the function combinators. @end table Alternatively, you can just dump @verb{~dash.el~} or @verb{~dash-functional.el~} in your load path somewhere. @menu * Using in a package:: * Syntax highlighting of dash functions:: @end menu @node Using in a package @section Using in a package Add this to the big comment block at the top: @lisp ;; Package-Requires: ((dash "2.12.0")) @end lisp @noindent To get function combinators: @lisp ;; Package-Requires: ((dash "2.12.0") (dash-functional "1.2.0") (emacs "24")) @end lisp @node Syntax highlighting of dash functions @section Syntax highlighting of dash functions Font lock of dash functions in emacs lisp buffers is now optional. Include this in your emacs settings to get syntax highlighting: @lisp (eval-after-load "dash" '(dash-enable-font-lock)) @end lisp @node Functions @chapter Functions This chapter contains reference documentation for the dash @abbr{application programming interface,API}. All functions and constructs in the library are prefixed with a dash (-). There are also anaphoric versions of functions where that makes sense, prefixed with two dashes instead of one. For instance, while @code{-map} takes a function to map over the list, one can also use the anaphoric form with double dashes - which will then be executed with @code{it} exposed as the list item. Here's an example: @lisp (-map (lambda (n) (* n n)) '(1 2 3 4)) ;; normal version (--map (* it it) '(1 2 3 4)) ;; anaphoric version @end lisp @noindent Of course, the original can also be written like @lisp (defun square (n) (* n n)) (-map 'square '(1 2 3 4)) @end lisp @noindent which demonstrates the usefulness of both versions. @menu * Maps:: * Sublist selection:: * List to list:: * Reductions:: * Unfolding:: * Predicates:: * Partitioning:: * Indexing:: * Set operations:: * Other list operations:: * Tree operations:: * Threading macros:: * Binding:: * Side-effects:: * Destructive operations:: * Function combinators:: @end menu @node Maps @section Maps Functions in this category take a transforming function, which is then applied sequentially to each or selected elements of the input list. The results are collected in order and returned as new list. @anchor{-map} @defun -map (fn list) Return a new list consisting of the result of applying @var{fn} to the items in @var{list}. @example @group (-map (lambda (num) (* num num)) '(1 2 3 4)) @result{} '(1 4 9 16) @end group @group (-map 'square '(1 2 3 4)) @result{} '(1 4 9 16) @end group @group (--map (* it it) '(1 2 3 4)) @result{} '(1 4 9 16) @end group @end example @end defun @anchor{-map-when} @defun -map-when (pred rep list) Return a new list where the elements in @var{list} that does not match the @var{pred} function are unchanged, and where the elements in @var{list} that do match the @var{pred} function are mapped through the @var{rep} function. Alias: @code{-replace-where} See also: @code{-update-at} (@pxref{-update-at}) @example @group (-map-when 'even? 'square '(1 2 3 4)) @result{} '(1 4 3 16) @end group @group (--map-when (> it 2) (* it it) '(1 2 3 4)) @result{} '(1 2 9 16) @end group @group (--map-when (= it 2) 17 '(1 2 3 4)) @result{} '(1 17 3 4) @end group @end example @end defun @anchor{-map-first} @defun -map-first (pred rep list) Replace first item in @var{list} satisfying @var{pred} with result of @var{rep} called on this item. See also: @code{-map-when} (@pxref{-map-when}), @code{-replace-first} (@pxref{-replace-first}) @example @group (-map-first 'even? 'square '(1 2 3 4)) @result{} '(1 4 3 4) @end group @group (--map-first (> it 2) (* it it) '(1 2 3 4)) @result{} '(1 2 9 4) @end group @group (--map-first (= it 2) 17 '(1 2 3 2)) @result{} '(1 17 3 2) @end group @end example @end defun @anchor{-map-last} @defun -map-last (pred rep list) Replace first item in @var{list} satisfying @var{pred} with result of @var{rep} called on this item. See also: @code{-map-when} (@pxref{-map-when}), @code{-replace-last} (@pxref{-replace-last}) @example @group (-map-last 'even? 'square '(1 2 3 4)) @result{} '(1 2 3 16) @end group @group (--map-last (> it 2) (* it it) '(1 2 3 4)) @result{} '(1 2 3 16) @end group @group (--map-last (= it 2) 17 '(1 2 3 2)) @result{} '(1 2 3 17) @end group @end example @end defun @anchor{-map-indexed} @defun -map-indexed (fn list) Return a new list consisting of the result of (@var{fn} index item) for each item in @var{list}. In the anaphoric form @code{--map-indexed}, the index is exposed as `it-index`. @example @group (-map-indexed (lambda (index item) (- item index)) '(1 2 3 4)) @result{} '(1 1 1 1) @end group @group (--map-indexed (- it it-index) '(1 2 3 4)) @result{} '(1 1 1 1) @end group @end example @end defun @anchor{-annotate} @defun -annotate (fn list) Return a list of cons cells where each cell is @var{fn} applied to each element of @var{list} paired with the unmodified element of @var{list}. @example @group (-annotate '1+ '(1 2 3)) @result{} '((2 . 1) (3 . 2) (4 . 3)) @end group @group (-annotate 'length '(("h" "e" "l" "l" "o") ("hello" "world"))) @result{} '((5 "h" "e" "l" "l" "o") (2 "hello" "world")) @end group @group (--annotate (< 1 it) '(0 1 2 3)) @result{} '((nil . 0) (nil . 1) (t . 2) (t . 3)) @end group @end example @end defun @anchor{-splice} @defun -splice (pred fun list) Splice lists generated by @var{fun} in place of elements matching @var{pred} in @var{list}. @var{fun} takes the element matching @var{pred} as input. This function can be used as replacement for @code{,@@} in case you need to splice several lists at marked positions (for example with keywords). See also: @code{-splice-list} (@pxref{-splice-list}), @code{-insert-at} (@pxref{-insert-at}) @example @group (-splice 'even? (lambda (x) (list x x)) '(1 2 3 4)) @result{} '(1 2 2 3 4 4) @end group @group (--splice 't (list it it) '(1 2 3 4)) @result{} '(1 1 2 2 3 3 4 4) @end group @group (--splice (equal it :magic) '((list of) (magical) (code)) '((foo) (bar) :magic (baz))) @result{} '((foo) (bar) (list of) (magical) (code) (baz)) @end group @end example @end defun @anchor{-splice-list} @defun -splice-list (pred new-list list) Splice @var{new-list} in place of elements matching @var{pred} in @var{list}. See also: @code{-splice} (@pxref{-splice}), @code{-insert-at} (@pxref{-insert-at}) @example @group (-splice-list 'keywordp '(a b c) '(1 :foo 2)) @result{} '(1 a b c 2) @end group @group (-splice-list 'keywordp nil '(1 :foo 2)) @result{} '(1 2) @end group @group (--splice-list (keywordp it) '(a b c) '(1 :foo 2)) @result{} '(1 a b c 2) @end group @end example @end defun @anchor{-mapcat} @defun -mapcat (fn list) Return the concatenation of the result of mapping @var{fn} over @var{list}. Thus function @var{fn} should return a list. @example @group (-mapcat 'list '(1 2 3)) @result{} '(1 2 3) @end group @group (-mapcat (lambda (item) (list 0 item)) '(1 2 3)) @result{} '(0 1 0 2 0 3) @end group @group (--mapcat (list 0 it) '(1 2 3)) @result{} '(0 1 0 2 0 3) @end group @end example @end defun @anchor{-copy} @defun -copy (arg) Create a shallow copy of @var{list}. @example @group (-copy '(1 2 3)) @result{} '(1 2 3) @end group @group (let ((a '(1 2 3))) (eq a (-copy a))) @result{} nil @end group @end example @end defun @node Sublist selection @section Sublist selection Functions returning a sublist of the original list. @anchor{-filter} @defun -filter (pred list) Return a new list of the items in @var{list} for which @var{pred} returns a non-nil value. Alias: @code{-select} @example @group (-filter (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) @result{} '(2 4) @end group @group (-filter 'even? '(1 2 3 4)) @result{} '(2 4) @end group @group (--filter (= 0 (% it 2)) '(1 2 3 4)) @result{} '(2 4) @end group @end example @end defun @anchor{-remove} @defun -remove (pred list) Return a new list of the items in @var{list} for which @var{pred} returns nil. Alias: @code{-reject} @example @group (-remove (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) @result{} '(1 3) @end group @group (-remove 'even? '(1 2 3 4)) @result{} '(1 3) @end group @group (--remove (= 0 (% it 2)) '(1 2 3 4)) @result{} '(1 3) @end group @end example @end defun @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}) @example @group (-remove-first 'even? '(1 3 5 4 7 8 10)) @result{} '(1 3 5 7 8 10) @end group @group (-remove-first 'stringp '(1 2 "first" "second" "third")) @result{} '(1 2 "second" "third") @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) @end group @end example @end defun @anchor{-remove-last} @defun -remove-last (pred list) Return a new list with the last item matching @var{pred} removed. Alias: @code{-reject-last} See also: @code{-remove} (@pxref{-remove}), @code{-map-last} (@pxref{-map-last}) @example @group (-remove-last 'even? '(1 3 5 4 7 8 10 11)) @result{} '(1 3 5 4 7 8 11) @end group @group (-remove-last 'stringp '(1 2 "last" "second" "third")) @result{} '(1 2 "last" "second") @end group @group (--remove-last (> it 3) '(1 2 3 4 5 6 7 8 9 10)) @result{} '(1 2 3 4 5 6 7 8 9) @end group @end example @end defun @anchor{-remove-item} @defun -remove-item (item list) Remove all occurences of @var{item} from @var{list}. Comparison is done with @code{equal}. @example @group (-remove-item 3 '(1 2 3 2 3 4 5 3)) @result{} '(1 2 2 4 5) @end group @group (-remove-item 'foo '(foo bar baz foo)) @result{} '(bar baz) @end group @group (-remove-item "bob" '("alice" "bob" "eve" "bob" "dave")) @result{} '("alice" "eve" "dave") @end group @end example @end defun @anchor{-non-nil} @defun -non-nil (list) Return all non-nil elements of @var{list}. @example @group (-non-nil '(1 nil 2 nil nil 3 4 nil 5 nil)) @result{} '(1 2 3 4 5) @end group @end example @end defun @anchor{-slice} @defun -slice (list from &optional to step) Return copy of @var{list}, starting from index @var{from} to index @var{to}. @var{from} or @var{to} may be negative. These values are then interpreted modulo the length of the list. If @var{step} is a number, only each STEPth item in the resulting section is returned. Defaults to 1. @example @group (-slice '(1 2 3 4 5) 1) @result{} '(2 3 4 5) @end group @group (-slice '(1 2 3 4 5) 0 3) @result{} '(1 2 3) @end group @group (-slice '(1 2 3 4 5 6 7 8 9) 1 -1 2) @result{} '(2 4 6 8) @end group @end example @end defun @anchor{-take} @defun -take (n list) Return a new list of the first @var{n} items in @var{list}, or all items if there are fewer than @var{n}. @example @group (-take 3 '(1 2 3 4 5)) @result{} '(1 2 3) @end group @group (-take 17 '(1 2 3 4 5)) @result{} '(1 2 3 4 5) @end group @end example @end defun @anchor{-drop} @defun -drop (n list) Return the tail of @var{list} without the first @var{n} items. @example @group (-drop 3 '(1 2 3 4 5)) @result{} '(4 5) @end group @group (-drop 17 '(1 2 3 4 5)) @result{} '() @end group @end example @end defun @anchor{-take-while} @defun -take-while (pred list) Return a new list of successive items from @var{list} while (@var{pred} item) returns a non-nil value. @example @group (-take-while 'even? '(1 2 3 4)) @result{} '() @end group @group (-take-while 'even? '(2 4 5 6)) @result{} '(2 4) @end group @group (--take-while (< it 4) '(1 2 3 4 3 2 1)) @result{} '(1 2 3) @end group @end example @end defun @anchor{-drop-while} @defun -drop-while (pred list) Return the tail of @var{list} starting from the first item for which (@var{pred} item) returns nil. @example @group (-drop-while 'even? '(1 2 3 4)) @result{} '(1 2 3 4) @end group @group (-drop-while 'even? '(2 4 5 6)) @result{} '(5 6) @end group @group (--drop-while (< it 4) '(1 2 3 4 3 2 1)) @result{} '(4 3 2 1) @end group @end example @end defun @anchor{-select-by-indices} @defun -select-by-indices (indices list) Return a list whose elements are elements from @var{list} selected as `(nth i list)` for all i from @var{indices}. @example @group (-select-by-indices '(4 10 2 3 6) '("v" "e" "l" "o" "c" "i" "r" "a" "p" "t" "o" "r")) @result{} '("c" "o" "l" "o" "r") @end group @group (-select-by-indices '(2 1 0) '("a" "b" "c")) @result{} '("c" "b" "a") @end group @group (-select-by-indices '(0 1 2 0 1 3 3 1) '("f" "a" "r" "l")) @result{} '("f" "a" "r" "f" "a" "l" "l" "a") @end group @end example @end defun @node List to list @section List to list Bag of various functions which modify input list. @anchor{-keep} @defun -keep (fn list) Return a new list of the non-nil results of applying @var{fn} to the items in @var{list}. If you want to select the original items satisfying a predicate use @code{-filter} (@pxref{-filter}). @example @group (-keep 'cdr '((1 2 3) (4 5) (6))) @result{} '((2 3) (5)) @end group @group (-keep (lambda (num) (when (> num 3) (* 10 num))) '(1 2 3 4 5 6)) @result{} '(40 50 60) @end group @group (--keep (when (> it 3) (* 10 it)) '(1 2 3 4 5 6)) @result{} '(40 50 60) @end group @end example @end defun @anchor{-concat} @defun -concat (&rest lists) Return a new list with the concatenation of the elements in the supplied @var{lists}. @example @group (-concat '(1)) @result{} '(1) @end group @group (-concat '(1) '(2)) @result{} '(1 2) @end group @group (-concat '(1) '(2 3) '(4)) @result{} '(1 2 3 4) @end group @end example @end defun @anchor{-flatten} @defun -flatten (l) Take a nested list @var{l} and return its contents as a single, flat list. Note that because @code{nil} represents a list of zero elements (an empty list), any mention of nil in @var{l} will disappear after flattening. If you need to preserve nils, consider @code{-flatten-n} (@pxref{-flatten-n}) or map them to some unique symbol and then map them back. Conses of two atoms are considered "terminals", that is, they aren't flattened further. See also: @code{-flatten-n} (@pxref{-flatten-n}) @example @group (-flatten '((1))) @result{} '(1) @end group @group (-flatten '((1 (2 3) (((4 (5))))))) @result{} '(1 2 3 4 5) @end group @group (-flatten '(1 2 (3 . 4))) @result{} '(1 2 (3 . 4)) @end group @end example @end defun @anchor{-flatten-n} @defun -flatten-n (num list) Flatten @var{num} levels of a nested @var{list}. See also: @code{-flatten} (@pxref{-flatten}) @example @group (-flatten-n 1 '((1 2) ((3 4) ((5 6))))) @result{} '(1 2 (3 4) ((5 6))) @end group @group (-flatten-n 2 '((1 2) ((3 4) ((5 6))))) @result{} '(1 2 3 4 (5 6)) @end group @group (-flatten-n 3 '((1 2) ((3 4) ((5 6))))) @result{} '(1 2 3 4 5 6) @end group @end example @end defun @anchor{-replace} @defun -replace (old new list) Replace all @var{old} items in @var{list} with @var{new}. Elements are compared using @code{equal}. See also: @code{-replace-at} (@pxref{-replace-at}) @example @group (-replace 1 "1" '(1 2 3 4 3 2 1)) @result{} '("1" 2 3 4 3 2 "1") @end group @group (-replace "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) @result{} '("a" "nice" "bar" "sentence" "about" "bar") @end group @group (-replace 1 2 nil) @result{} nil @end group @end example @end defun @anchor{-replace-first} @defun -replace-first (old new list) Replace the first occurence of @var{old} with @var{new} in @var{list}. Elements are compared using @code{equal}. See also: @code{-map-first} (@pxref{-map-first}) @example @group (-replace-first 1 "1" '(1 2 3 4 3 2 1)) @result{} '("1" 2 3 4 3 2 1) @end group @group (-replace-first "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) @result{} '("a" "nice" "bar" "sentence" "about" "foo") @end group @group (-replace-first 1 2 nil) @result{} nil @end group @end example @end defun @anchor{-replace-last} @defun -replace-last (old new list) Replace the last occurence of @var{old} with @var{new} in @var{list}. Elements are compared using @code{equal}. See also: @code{-map-last} (@pxref{-map-last}) @example @group (-replace-last 1 "1" '(1 2 3 4 3 2 1)) @result{} '(1 2 3 4 3 2 "1") @end group @group (-replace-last "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) @result{} '("a" "nice" "foo" "sentence" "about" "bar") @end group @group (-replace-last 1 2 nil) @result{} nil @end group @end example @end defun @anchor{-insert-at} @defun -insert-at (n x list) Return a list with @var{x} inserted into @var{list} at position @var{n}. See also: @code{-splice} (@pxref{-splice}), @code{-splice-list} (@pxref{-splice-list}) @example @group (-insert-at 1 'x '(a b c)) @result{} '(a x b c) @end group @group (-insert-at 12 'x '(a b c)) @result{} '(a b c x) @end group @end example @end defun @anchor{-replace-at} @defun -replace-at (n x list) Return a list with element at Nth position in @var{list} replaced with @var{x}. See also: @code{-replace} (@pxref{-replace}) @example @group (-replace-at 0 9 '(0 1 2 3 4 5)) @result{} '(9 1 2 3 4 5) @end group @group (-replace-at 1 9 '(0 1 2 3 4 5)) @result{} '(0 9 2 3 4 5) @end group @group (-replace-at 4 9 '(0 1 2 3 4 5)) @result{} '(0 1 2 3 9 5) @end group @end example @end defun @anchor{-update-at} @defun -update-at (n func list) Return a list with element at Nth position in @var{list} replaced with `(func (nth n list))`. See also: @code{-map-when} (@pxref{-map-when}) @example @group (-update-at 0 (lambda (x) (+ x 9)) '(0 1 2 3 4 5)) @result{} '(9 1 2 3 4 5) @end group @group (-update-at 1 (lambda (x) (+ x 8)) '(0 1 2 3 4 5)) @result{} '(0 9 2 3 4 5) @end group @group (--update-at 2 (length it) '("foo" "bar" "baz" "quux")) @result{} '("foo" "bar" 3 "quux") @end group @end example @end defun @anchor{-remove-at} @defun -remove-at (n list) Return a list with element at Nth position in @var{list} removed. See also: @code{-remove-at-indices} (@pxref{-remove-at-indices}), @code{-remove} (@pxref{-remove}) @example @group (-remove-at 0 '("0" "1" "2" "3" "4" "5")) @result{} '("1" "2" "3" "4" "5") @end group @group (-remove-at 1 '("0" "1" "2" "3" "4" "5")) @result{} '("0" "2" "3" "4" "5") @end group @group (-remove-at 2 '("0" "1" "2" "3" "4" "5")) @result{} '("0" "1" "3" "4" "5") @end group @end example @end defun @anchor{-remove-at-indices} @defun -remove-at-indices (indices list) Return a list whose elements are elements from @var{list} without elements selected as `(nth i list)` for all i from @var{indices}. See also: @code{-remove-at} (@pxref{-remove-at}), @code{-remove} (@pxref{-remove}) @example @group (-remove-at-indices '(0) '("0" "1" "2" "3" "4" "5")) @result{} '("1" "2" "3" "4" "5") @end group @group (-remove-at-indices '(0 2 4) '("0" "1" "2" "3" "4" "5")) @result{} '("1" "3" "5") @end group @group (-remove-at-indices '(0 5) '("0" "1" "2" "3" "4" "5")) @result{} '("1" "2" "3" "4") @end group @end example @end defun @node Reductions @section Reductions Functions reducing lists into single value. @anchor{-reduce-from} @defun -reduce-from (fn initial-value list) Return the result of applying @var{fn} to @var{initial-value} and the first item in @var{list}, then applying @var{fn} to that result and the 2nd item, etc. If @var{list} contains no items, return @var{initial-value} and @var{fn} is not called. In the anaphoric form @code{--reduce-from}, the accumulated value is exposed as `acc`. See also: @code{-reduce} (@pxref{-reduce}), @code{-reduce-r} (@pxref{-reduce-r}) @example @group (-reduce-from '- 10 '(1 2 3)) @result{} 4 @end group @group (-reduce-from (lambda (memo item) (concat "(" memo " - " (int-to-string item) ")")) "10" '(1 2 3)) @result{} "(((10 - 1) - 2) - 3)" @end group @group (--reduce-from (concat acc " " it) "START" '("a" "b" "c")) @result{} "START a b c" @end group @end example @end defun @anchor{-reduce-r-from} @defun -reduce-r-from (fn initial-value list) Replace conses with @var{fn}, nil with @var{initial-value} and evaluate the resulting expression. If @var{list} is empty, @var{initial-value} is returned and @var{fn} is not called. Note: this function works the same as @code{-reduce-from} (@pxref{-reduce-from}) but the operation associates from right instead of from left. See also: @code{-reduce-r} (@pxref{-reduce-r}), @code{-reduce} (@pxref{-reduce}) @example @group (-reduce-r-from '- 10 '(1 2 3)) @result{} -8 @end group @group (-reduce-r-from (lambda (item memo) (concat "(" (int-to-string item) " - " memo ")")) "10" '(1 2 3)) @result{} "(1 - (2 - (3 - 10)))" @end group @group (--reduce-r-from (concat it " " acc) "END" '("a" "b" "c")) @result{} "a b c END" @end group @end example @end defun @anchor{-reduce} @defun -reduce (fn list) Return the result of applying @var{fn} to the first 2 items in @var{list}, then applying @var{fn} to that result and the 3rd item, etc. If @var{list} contains no items, @var{fn} must accept no arguments as well, and reduce return the result of calling @var{fn} with no arguments. If @var{list} has only 1 item, it is returned and @var{fn} is not called. In the anaphoric form @code{--reduce}, the accumulated value is exposed as `acc`. See also: @code{-reduce-from} (@pxref{-reduce-from}), @code{-reduce-r} (@pxref{-reduce-r}) @example @group (-reduce '- '(1 2 3 4)) @result{} -8 @end group @group (-reduce (lambda (memo item) (format "%s-%s" memo item)) '(1 2 3)) @result{} "1-2-3" @end group @group (--reduce (format "%s-%s" acc it) '(1 2 3)) @result{} "1-2-3" @end group @end example @end defun @anchor{-reduce-r} @defun -reduce-r (fn list) Replace conses with @var{fn} and evaluate the resulting expression. The final nil is ignored. If @var{list} contains no items, @var{fn} must accept no arguments as well, and reduce return the result of calling @var{fn} with no arguments. If @var{list} has only 1 item, it is returned and @var{fn} is not called. The first argument of @var{fn} is the new item, the second is the accumulated value. Note: this function works the same as @code{-reduce} (@pxref{-reduce}) but the operation associates from right instead of from left. See also: @code{-reduce-r-from} (@pxref{-reduce-r-from}), @code{-reduce} (@pxref{-reduce}) @example @group (-reduce-r '- '(1 2 3 4)) @result{} -2 @end group @group (-reduce-r (lambda (item memo) (format "%s-%s" memo item)) '(1 2 3)) @result{} "3-2-1" @end group @group (--reduce-r (format "%s-%s" acc it) '(1 2 3)) @result{} "3-2-1" @end group @end example @end defun @anchor{-count} @defun -count (pred list) Counts the number of items in @var{list} where (@var{pred} item) is non-nil. @example @group (-count 'even? '(1 2 3 4 5)) @result{} 2 @end group @group (--count (< it 4) '(1 2 3 4)) @result{} 3 @end group @end example @end defun @anchor{-sum} @defun -sum (list) Return the sum of @var{list}. @example @group (-sum '()) @result{} 0 @end group @group (-sum '(1)) @result{} 1 @end group @group (-sum '(1 2 3 4)) @result{} 10 @end group @end example @end defun @anchor{-product} @defun -product (list) Return the product of @var{list}. @example @group (-product '()) @result{} 1 @end group @group (-product '(1)) @result{} 1 @end group @group (-product '(1 2 3 4)) @result{} 24 @end group @end example @end defun @anchor{-min} @defun -min (list) Return the smallest value from @var{list} of numbers or markers. @example @group (-min '(0)) @result{} 0 @end group @group (-min '(3 2 1)) @result{} 1 @end group @group (-min '(1 2 3)) @result{} 1 @end group @end example @end defun @anchor{-min-by} @defun -min-by (comparator list) Take a comparison function @var{comparator} and a @var{list} and return the least element of the list by the comparison function. See also combinator @code{-on} (@pxref{-on}) which can transform the values before comparing them. @example @group (-min-by '> '(4 3 6 1)) @result{} 1 @end group @group (--min-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) @result{} '(1 2 3) @end group @group (--min-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) @result{} '(2) @end group @end example @end defun @anchor{-max} @defun -max (list) Return the largest value from @var{list} of numbers or markers. @example @group (-max '(0)) @result{} 0 @end group @group (-max '(3 2 1)) @result{} 3 @end group @group (-max '(1 2 3)) @result{} 3 @end group @end example @end defun @anchor{-max-by} @defun -max-by (comparator list) Take a comparison function @var{comparator} and a @var{list} and return the greatest element of the list by the comparison function. See also combinator @code{-on} (@pxref{-on}) which can transform the values before comparing them. @example @group (-max-by '> '(4 3 6 1)) @result{} 6 @end group @group (--max-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) @result{} '(3 2) @end group @group (--max-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) @result{} '(1 2 3) @end group @end example @end defun @node Unfolding @section Unfolding Operations dual to reductions, building lists from seed value rather than consuming a list to produce a single value. @anchor{-iterate} @defun -iterate (fun init n) Return a list of iterated applications of @var{fun} to @var{init}. This means a list of form: (init (fun init) (fun (fun init)) ...) @var{n} is the length of the returned list. @example @group (-iterate '1+ 1 10) @result{} '(1 2 3 4 5 6 7 8 9 10) @end group @group (-iterate (lambda (x) (+ x x)) 2 5) @result{} '(2 4 8 16 32) @end group @group (--iterate (* it it) 2 5) @result{} '(2 4 16 256 65536) @end group @end example @end defun @anchor{-unfold} @defun -unfold (fun seed) Build a list from @var{seed} using @var{fun}. This is "dual" operation to @code{-reduce-r} (@pxref{-reduce-r}): while -reduce-r consumes a list to produce a single value, @code{-unfold} (@pxref{-unfold}) takes a seed value and builds a (potentially infinite!) list. @var{fun} should return @code{nil} to stop the generating process, or a cons (@var{a} . @var{b}), where @var{a} will be prepended to the result and @var{b} is the new seed. @example @group (-unfold (lambda (x) (unless (= x 0) (cons x (1- x)))) 10) @result{} '(10 9 8 7 6 5 4 3 2 1) @end group @group (--unfold (when it (cons it (cdr it))) '(1 2 3 4)) @result{} '((1 2 3 4) (2 3 4) (3 4) (4)) @end group @group (--unfold (when it (cons it (butlast it))) '(1 2 3 4)) @result{} '((1 2 3 4) (1 2 3) (1 2) (1)) @end group @end example @end defun @node Predicates @section Predicates @anchor{-any?} @defun -any? (pred list) Return t if (@var{pred} x) is non-nil for any x in @var{list}, else nil. Alias: @code{-any-p}, @code{-some?}, @code{-some-p} @example @group (-any? 'even? '(1 2 3)) @result{} t @end group @group (-any? 'even? '(1 3 5)) @result{} nil @end group @group (--any? (= 0 (% it 2)) '(1 2 3)) @result{} t @end group @end example @end defun @anchor{-all?} @defun -all? (pred list) Return t if (@var{pred} x) is non-nil for all x in @var{list}, else nil. Alias: @code{-all-p}, @code{-every?}, @code{-every-p} @example @group (-all? 'even? '(1 2 3)) @result{} nil @end group @group (-all? 'even? '(2 4 6)) @result{} t @end group @group (--all? (= 0 (% it 2)) '(2 4 6)) @result{} t @end group @end example @end defun @anchor{-none?} @defun -none? (pred list) Return t if (@var{pred} x) is nil for all x in @var{list}, else nil. Alias: @code{-none-p} @example @group (-none? 'even? '(1 2 3)) @result{} nil @end group @group (-none? 'even? '(1 3 5)) @result{} t @end group @group (--none? (= 0 (% it 2)) '(1 2 3)) @result{} nil @end group @end example @end defun @anchor{-only-some?} @defun -only-some? (pred list) Return `t` if at least one item of @var{list} matches @var{pred} and at least one item of @var{list} does not match @var{pred}. Return `nil` both if all items match the predicate or if none of the items match the predicate. Alias: @code{-only-some-p} @example @group (-only-some? 'even? '(1 2 3)) @result{} t @end group @group (-only-some? 'even? '(1 3 5)) @result{} nil @end group @group (-only-some? 'even? '(2 4 6)) @result{} nil @end group @end example @end defun @anchor{-contains?} @defun -contains? (list element) Return non-nil if @var{list} contains @var{element}. The test for equality is done with @code{equal}, or with @code{-compare-fn} if that's non-nil. Alias: @code{-contains-p} @example @group (-contains? '(1 2 3) 1) @result{} t @end group @group (-contains? '(1 2 3) 2) @result{} t @end group @group (-contains? '(1 2 3) 4) @result{} nil @end group @end example @end defun @anchor{-same-items?} @defun -same-items? (list list2) Return true if @var{list} and @var{list2} has the same items. The order of the elements in the lists does not matter. Alias: @code{-same-items-p} @example @group (-same-items? '(1 2 3) '(1 2 3)) @result{} t @end group @group (-same-items? '(1 2 3) '(3 2 1)) @result{} t @end group @group (-same-items? '(1 2 3) '(1 2 3 4)) @result{} nil @end group @end example @end defun @anchor{-is-prefix?} @defun -is-prefix? (prefix list) Return non-nil if @var{prefix} is prefix of @var{list}. Alias: @code{-is-prefix-p} @example @group (-is-prefix? '(1 2 3) '(1 2 3 4 5)) @result{} t @end group @group (-is-prefix? '(1 2 3 4 5) '(1 2 3)) @result{} nil @end group @group (-is-prefix? '(1 3) '(1 2 3 4 5)) @result{} nil @end group @end example @end defun @anchor{-is-suffix?} @defun -is-suffix? (suffix list) Return non-nil if @var{suffix} is suffix of @var{list}. Alias: @code{-is-suffix-p} @example @group (-is-suffix? '(3 4 5) '(1 2 3 4 5)) @result{} t @end group @group (-is-suffix? '(1 2 3 4 5) '(3 4 5)) @result{} nil @end group @group (-is-suffix? '(3 5) '(1 2 3 4 5)) @result{} nil @end group @end example @end defun @anchor{-is-infix?} @defun -is-infix? (infix list) Return non-nil if @var{infix} is infix of @var{list}. This operation runs in @var{o}(n^2) time Alias: @code{-is-infix-p} @example @group (-is-infix? '(1 2 3) '(1 2 3 4 5)) @result{} t @end group @group (-is-infix? '(2 3 4) '(1 2 3 4 5)) @result{} t @end group @group (-is-infix? '(3 4 5) '(1 2 3 4 5)) @result{} t @end group @end example @end defun @node Partitioning @section Partitioning 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. @example @group (-split-at 3 '(1 2 3 4 5)) @result{} '((1 2 3) (4 5)) @end group @group (-split-at 17 '(1 2 3 4 5)) @result{} '((1 2 3 4 5) nil) @end group @end example @end defun @anchor{-split-with} @defun -split-with (pred list) Return a list of ((-take-while @var{pred} @var{list}) (-drop-while @var{pred} @var{list})), in no more than one pass through the list. @example @group (-split-with 'even? '(1 2 3 4)) @result{} '(nil (1 2 3 4)) @end group @group (-split-with 'even? '(2 4 5 6)) @result{} '((2 4) (5 6)) @end group @group (--split-with (< it 4) '(1 2 3 4 3 2 1)) @result{} '((1 2 3) (4 3 2 1)) @end group @end example @end defun @anchor{-split-on} @defun -split-on (item list) Split the @var{list} each time @var{item} is found. Unlike @code{-partition-by} (@pxref{-partition-by}), the @var{item} is discarded from the results. Empty lists are also removed from the result. Comparison is done by @code{equal}. See also @code{-split-when} (@pxref{-split-when}) @example @group (-split-on '| '(Nil | Leaf a | Node [Tree a])) @result{} '((Nil) (Leaf a) (Node [Tree a])) @end group @group (-split-on ':endgroup '("a" "b" :endgroup "c" :endgroup "d" "e")) @result{} '(("a" "b") ("c") ("d" "e")) @end group @group (-split-on ':endgroup '("a" "b" :endgroup :endgroup "d" "e")) @result{} '(("a" "b") ("d" "e")) @end group @end example @end defun @anchor{-split-when} @defun -split-when (fn list) Split the @var{list} on each element where @var{fn} returns non-nil. Unlike @code{-partition-by} (@pxref{-partition-by}), the "matched" element is discarded from the results. Empty lists are also removed from the result. This function can be thought of as a generalization of @code{split-string}. @example @group (-split-when 'even? '(1 2 3 4 5 6)) @result{} '((1) (3) (5)) @end group @group (-split-when 'even? '(1 2 3 4 6 8 9)) @result{} '((1) (3) (9)) @end group @group (--split-when (memq it '(&optional &rest)) '(a b &optional c d &rest args)) @result{} '((a b) (c d) (args)) @end group @end example @end defun @anchor{-separate} @defun -separate (pred list) Return a list of ((-filter @var{pred} @var{list}) (-remove @var{pred} @var{list})), in one pass through the list. @example @group (-separate (lambda (num) (= 0 (% num 2))) '(1 2 3 4 5 6 7)) @result{} '((2 4 6) (1 3 5 7)) @end group @group (--separate (< it 5) '(3 7 5 9 3 2 1 4 6)) @result{} '((3 3 2 1 4) (7 5 9 6)) @end group @group (-separate 'cdr '((1 2) (1) (1 2 3) (4))) @result{} '(((1 2) (1 2 3)) ((1) (4))) @end group @end example @end defun @anchor{-partition} @defun -partition (n list) Return a new list with the items in @var{list} grouped into @var{n-}sized sublists. If there are not enough items to make the last group @var{n-}sized, those items are discarded. @example @group (-partition 2 '(1 2 3 4 5 6)) @result{} '((1 2) (3 4) (5 6)) @end group @group (-partition 2 '(1 2 3 4 5 6 7)) @result{} '((1 2) (3 4) (5 6)) @end group @group (-partition 3 '(1 2 3 4 5 6 7)) @result{} '((1 2 3) (4 5 6)) @end group @end example @end defun @anchor{-partition-all} @defun -partition-all (n list) Return a new list with the items in @var{list} grouped into @var{n-}sized sublists. The last group may contain less than @var{n} items. @example @group (-partition-all 2 '(1 2 3 4 5 6)) @result{} '((1 2) (3 4) (5 6)) @end group @group (-partition-all 2 '(1 2 3 4 5 6 7)) @result{} '((1 2) (3 4) (5 6) (7)) @end group @group (-partition-all 3 '(1 2 3 4 5 6 7)) @result{} '((1 2 3) (4 5 6) (7)) @end group @end example @end defun @anchor{-partition-in-steps} @defun -partition-in-steps (n step list) Return a new list with the items in @var{list} grouped into @var{n-}sized sublists at offsets @var{step} apart. If there are not enough items to make the last group @var{n-}sized, those items are discarded. @example @group (-partition-in-steps 2 1 '(1 2 3 4)) @result{} '((1 2) (2 3) (3 4)) @end group @group (-partition-in-steps 3 2 '(1 2 3 4)) @result{} '((1 2 3)) @end group @group (-partition-in-steps 3 2 '(1 2 3 4 5)) @result{} '((1 2 3) (3 4 5)) @end group @end example @end defun @anchor{-partition-all-in-steps} @defun -partition-all-in-steps (n step list) Return a new list with the items in @var{list} grouped into @var{n-}sized sublists at offsets @var{step} apart. The last groups may contain less than @var{n} items. @example @group (-partition-all-in-steps 2 1 '(1 2 3 4)) @result{} '((1 2) (2 3) (3 4) (4)) @end group @group (-partition-all-in-steps 3 2 '(1 2 3 4)) @result{} '((1 2 3) (3 4)) @end group @group (-partition-all-in-steps 3 2 '(1 2 3 4 5)) @result{} '((1 2 3) (3 4 5) (5)) @end group @end example @end defun @anchor{-partition-by} @defun -partition-by (fn list) Apply @var{fn} to each item in @var{list}, splitting it each time @var{fn} returns a new value. @example @group (-partition-by 'even? '()) @result{} '() @end group @group (-partition-by 'even? '(1 1 2 2 2 3 4 6 8)) @result{} '((1 1) (2 2 2) (3) (4 6 8)) @end group @group (--partition-by (< it 3) '(1 2 3 4 3 2 1)) @result{} '((1 2) (3 4 3) (2 1)) @end group @end example @end defun @anchor{-partition-by-header} @defun -partition-by-header (fn list) Apply @var{fn} to the first item in @var{list}. That is the header value. Apply @var{fn} to each item in @var{list}, splitting it each time @var{fn} returns the header value, but only after seeing at least one other value (the body). @example @group (--partition-by-header (= it 1) '(1 2 3 1 2 1 2 3 4)) @result{} '((1 2 3) (1 2) (1 2 3 4)) @end group @group (--partition-by-header (> it 0) '(1 2 0 1 0 1 2 3 0)) @result{} '((1 2 0) (1 0) (1 2 3 0)) @end group @group (-partition-by-header 'even? '(2 1 1 1 4 1 3 5 6 6 1)) @result{} '((2 1 1 1) (4 1 3 5) (6 6 1)) @end group @end example @end defun @anchor{-group-by} @defun -group-by (fn list) Separate @var{list} into an alist whose keys are @var{fn} applied to the elements of @var{list}. Keys are compared by @code{equal}. @example @group (-group-by 'even? '()) @result{} '() @end group @group (-group-by 'even? '(1 1 2 2 2 3 4 6 8)) @result{} '((nil 1 1 3) (t 2 2 2 4 6 8)) @end group @group (--group-by (car (split-string it "/")) '("a/b" "c/d" "a/e")) @result{} '(("a" "a/b" "a/e") ("c" "c/d")) @end group @end example @end defun @node Indexing @section Indexing Return indices of elements based on predicates, sort elements by indices etc. @anchor{-elem-index} @defun -elem-index (elem list) Return the index of the first element in the given @var{list} which is equal to the query element @var{elem}, or nil if there is no such element. @example @group (-elem-index 2 '(6 7 8 2 3 4)) @result{} 3 @end group @group (-elem-index "bar" '("foo" "bar" "baz")) @result{} 1 @end group @group (-elem-index '(1 2) '((3) (5 6) (1 2) nil)) @result{} 2 @end group @end example @end defun @anchor{-elem-indices} @defun -elem-indices (elem list) Return the indices of all elements in @var{list} equal to the query element @var{elem}, in ascending order. @example @group (-elem-indices 2 '(6 7 8 2 3 4 2 1)) @result{} '(3 6) @end group @group (-elem-indices "bar" '("foo" "bar" "baz")) @result{} '(1) @end group @group (-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) @result{} '(1 3) @end group @end example @end defun @anchor{-find-index} @defun -find-index (pred list) Take a predicate @var{pred} and a @var{list} and return the index of the first element in the list satisfying the predicate, or nil if there is no such element. @example @group (-find-index 'even? '(2 4 1 6 3 3 5 8)) @result{} 0 @end group @group (--find-index (< 5 it) '(2 4 1 6 3 3 5 8)) @result{} 3 @end group @group (-find-index (-partial 'string-lessp "baz") '("bar" "foo" "baz")) @result{} 1 @end group @end example @end defun @anchor{-find-last-index} @defun -find-last-index (pred list) Take a predicate @var{pred} and a @var{list} and return the index of the last element in the list satisfying the predicate, or nil if there is no such element. @example @group (-find-last-index 'even? '(2 4 1 6 3 3 5 8)) @result{} 7 @end group @group (--find-last-index (< 5 it) '(2 7 1 6 3 8 5 2)) @result{} 5 @end group @group (-find-last-index (-partial 'string-lessp "baz") '("q" "foo" "baz")) @result{} 1 @end group @end example @end defun @anchor{-find-indices} @defun -find-indices (pred list) Return the indices of all elements in @var{list} satisfying the predicate @var{pred}, in ascending order. @example @group (-find-indices 'even? '(2 4 1 6 3 3 5 8)) @result{} '(0 1 3 7) @end group @group (--find-indices (< 5 it) '(2 4 1 6 3 3 5 8)) @result{} '(3 7) @end group @group (-find-indices (-partial 'string-lessp "baz") '("bar" "foo" "baz")) @result{} '(1) @end group @end example @end defun @anchor{-grade-up} @defun -grade-up (comparator list) Grade elements of @var{list} using @var{comparator} relation, yielding a permutation vector such that applying this permutation to @var{list} sorts it in ascending order. @example @group (-grade-up '< '(3 1 4 2 1 3 3)) @result{} '(1 4 3 0 5 6 2) @end group @group (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-up '< l) l)) @result{} '(1 1 2 3 3 3 4) @end group @end example @end defun @anchor{-grade-down} @defun -grade-down (comparator list) Grade elements of @var{list} using @var{comparator} relation, yielding a permutation vector such that applying this permutation to @var{list} sorts it in descending order. @example @group (-grade-down '< '(3 1 4 2 1 3 3)) @result{} '(2 0 5 6 3 1 4) @end group @group (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-down '< l) l)) @result{} '(4 3 3 3 2 1 1) @end group @end example @end defun @node Set operations @section Set operations Operations pretending lists are sets. @anchor{-union} @defun -union (list list2) Return a new list containing the elements of @var{list1} and elements of @var{list2} that are not in @var{list1}. The test for equality is done with @code{equal}, or with @code{-compare-fn} if that's non-nil. @example @group (-union '(1 2 3) '(3 4 5)) @result{} '(1 2 3 4 5) @end group @group (-union '(1 2 3 4) '()) @result{} '(1 2 3 4) @end group @group (-union '(1 1 2 2) '(3 2 1)) @result{} '(1 1 2 2 3) @end group @end example @end defun @anchor{-difference} @defun -difference (list list2) Return a new list with only the members of @var{list} that are not in @var{list2}. The test for equality is done with @code{equal}, or with @code{-compare-fn} if that's non-nil. @example @group (-difference '() '()) @result{} '() @end group @group (-difference '(1 2 3) '(4 5 6)) @result{} '(1 2 3) @end group @group (-difference '(1 2 3 4) '(3 4 5 6)) @result{} '(1 2) @end group @end example @end defun @anchor{-intersection} @defun -intersection (list list2) Return a new list containing only the elements that are members of both @var{list} and @var{list2}. The test for equality is done with @code{equal}, or with @code{-compare-fn} if that's non-nil. @example @group (-intersection '() '()) @result{} '() @end group @group (-intersection '(1 2 3) '(4 5 6)) @result{} '() @end group @group (-intersection '(1 2 3 4) '(3 4 5 6)) @result{} '(3 4) @end group @end example @end defun @anchor{-distinct} @defun -distinct (list) Return a new list with all duplicates removed. The test for equality is done with @code{equal}, or with @code{-compare-fn} if that's non-nil. Alias: @code{-uniq} @example @group (-distinct '()) @result{} '() @end group @group (-distinct '(1 2 2 4)) @result{} '(1 2 4) @end group @end example @end defun @node Other list operations @section Other list operations Other list functions not fit to be classified elsewhere. @anchor{-rotate} @defun -rotate (n list) Rotate @var{list} @var{n} places to the right. With @var{n} negative, rotate to the left. The time complexity is @var{o}(n). @example @group (-rotate 3 '(1 2 3 4 5 6 7)) @result{} '(5 6 7 1 2 3 4) @end group @group (-rotate -3 '(1 2 3 4 5 6 7)) @result{} '(4 5 6 7 1 2 3) @end group @end example @end defun @anchor{-repeat} @defun -repeat (n x) Return a list with @var{x} repeated @var{n} times. Return nil if @var{n} is less than 1. @example @group (-repeat 3 :a) @result{} '(:a :a :a) @end group @group (-repeat 1 :a) @result{} '(:a) @end group @group (-repeat 0 :a) @result{} nil @end group @end example @end defun @anchor{-cons*} @defun -cons* (&rest args) Make a new list from the elements of @var{args}. The last 2 members of @var{args} are used as the final cons of the result so if the final member of @var{args} is not a list the result is a dotted list. @example @group (-cons* 1 2) @result{} '(1 . 2) @end group @group (-cons* 1 2 3) @result{} '(1 2 . 3) @end group @group (-cons* 1) @result{} 1 @end group @end example @end defun @anchor{-snoc} @defun -snoc (list elem &rest elements) Append @var{elem} to the end of the list. This is like @code{cons}, but operates on the end of list. If @var{elements} is non nil, append these to the list as well. @example @group (-snoc '(1 2 3) 4) @result{} '(1 2 3 4) @end group @group (-snoc '(1 2 3) 4 5 6) @result{} '(1 2 3 4 5 6) @end group @group (-snoc '(1 2 3) '(4 5 6)) @result{} '(1 2 3 (4 5 6)) @end group @end example @end defun @anchor{-interpose} @defun -interpose (sep list) Return a new list of all elements in @var{list} separated by @var{sep}. @example @group (-interpose "-" '()) @result{} '() @end group @group (-interpose "-" '("a")) @result{} '("a") @end group @group (-interpose "-" '("a" "b" "c")) @result{} '("a" "-" "b" "-" "c") @end group @end example @end defun @anchor{-interleave} @defun -interleave (&rest lists) Return a new list of the first item in each list, then the second etc. @example @group (-interleave '(1 2) '("a" "b")) @result{} '(1 "a" 2 "b") @end group @group (-interleave '(1 2) '("a" "b") '("A" "B")) @result{} '(1 "a" "A" 2 "b" "B") @end group @group (-interleave '(1 2 3) '("a" "b")) @result{} '(1 "a" 2 "b") @end group @end example @end defun @anchor{-zip-with} @defun -zip-with (fn list1 list2) Zip the two lists @var{list1} and @var{list2} using a function @var{fn}. This function is applied pairwise taking as first argument element of @var{list1} and as second argument element of @var{list2} at corresponding position. The anaphoric form @code{--zip-with} binds the elements from @var{list1} as `it`, and the elements from @var{list2} as `other`. @example @group (-zip-with '+ '(1 2 3) '(4 5 6)) @result{} '(5 7 9) @end group @group (-zip-with 'cons '(1 2 3) '(4 5 6)) @result{} '((1 . 4) (2 . 5) (3 . 6)) @end group @group (--zip-with (concat it " and " other) '("Batman" "Jekyll") '("Robin" "Hyde")) @result{} '("Batman and Robin" "Jekyll and Hyde") @end group @end example @end defun @anchor{-zip} @defun -zip (&rest lists) Zip @var{lists} together. Group the head of each list, followed by the second elements of each list, and so on. The lengths of the returned groupings are equal to the length of the shortest input list. If two lists are provided as arguments, return the groupings as a list of cons cells. Otherwise, return the groupings as a list of lists. @example @group (-zip '(1 2 3) '(4 5 6)) @result{} '((1 . 4) (2 . 5) (3 . 6)) @end group @group (-zip '(1 2 3) '(4 5 6 7)) @result{} '((1 . 4) (2 . 5) (3 . 6)) @end group @group (-zip '(1 2 3 4) '(4 5 6)) @result{} '((1 . 4) (2 . 5) (3 . 6)) @end group @end example @end defun @anchor{-zip-fill} @defun -zip-fill (fill-value &rest lists) Zip @var{lists}, with @var{fill-value} padded onto the shorter lists. The lengths of the returned groupings are equal to the length of the longest input list. @example @group (-zip-fill 0 '(1 2 3 4 5) '(6 7 8 9)) @result{} '((1 . 6) (2 . 7) (3 . 8) (4 . 9) (5 . 0)) @end group @end example @end defun @anchor{-cycle} @defun -cycle (list) Return an infinite copy of @var{list} that will cycle through the elements and repeat from the beginning. @example @group (-take 5 (-cycle '(1 2 3))) @result{} '(1 2 3 1 2) @end group @group (-take 7 (-cycle '(1 "and" 3))) @result{} '(1 "and" 3 1 "and" 3 1) @end group @group (-zip (-cycle '(1 2 3)) '(1 2)) @result{} '((1 . 1) (2 . 2)) @end group @end example @end defun @anchor{-pad} @defun -pad (fill-value &rest lists) Appends @var{fill-value} to the end of each list in @var{lists} such that they will all have the same length. @example @group (-pad 0 '()) @result{} '(nil) @end group @group (-pad 0 '(1)) @result{} '((1)) @end group @group (-pad 0 '(1 2 3) '(4 5)) @result{} '((1 2 3) (4 5 0)) @end group @end example @end defun @anchor{-table} @defun -table (fn &rest lists) Compute outer product of @var{lists} using function @var{fn}. The function @var{fn} should have the same arity as the number of supplied lists. The outer product is computed by applying fn to all possible combinations created by taking one element from each list in order. The dimension of the result is (length lists). See also: @code{-table-flat} (@pxref{-table-flat}) @example @group (-table '* '(1 2 3) '(1 2 3)) @result{} '((1 2 3) (2 4 6) (3 6 9)) @end group @group (-table (lambda (a b) (-sum (-zip-with '* a b))) '((1 2) (3 4)) '((1 3) (2 4))) @result{} '((7 15) (10 22)) @end group @group (apply '-table 'list (-repeat 3 '(1 2))) @result{} '((((1 1 1) (2 1 1)) ((1 2 1) (2 2 1))) (((1 1 2) (2 1 2)) ((1 2 2) (2 2 2)))) @end group @end example @end defun @anchor{-table-flat} @defun -table-flat (fn &rest lists) Compute flat outer product of @var{lists} using function @var{fn}. The function @var{fn} should have the same arity as the number of supplied lists. The outer product is computed by applying fn to all possible combinations created by taking one element from each list in order. The results are flattened, ignoring the tensor structure of the result. This is equivalent to calling: (-flatten-n (1- (length lists)) (-table fn lists)) but the implementation here is much more efficient. See also: @code{-flatten-n} (@pxref{-flatten-n}), @code{-table} (@pxref{-table}) @example @group (-table-flat 'list '(1 2 3) '(a b c)) @result{} '((1 a) (2 a) (3 a) (1 b) (2 b) (3 b) (1 c) (2 c) (3 c)) @end group @group (-table-flat '* '(1 2 3) '(1 2 3)) @result{} '(1 2 3 2 4 6 3 6 9) @end group @group (apply '-table-flat 'list (-repeat 3 '(1 2))) @result{} '((1 1 1) (2 1 1) (1 2 1) (2 2 1) (1 1 2) (2 1 2) (1 2 2) (2 2 2)) @end group @end example @end defun @anchor{-first} @defun -first (pred list) Return the first x in @var{list} where (@var{pred} x) is non-nil, else nil. To get the first item in the list no questions asked, use @code{car}. Alias: @code{-find} @example @group (-first 'even? '(1 2 3)) @result{} 2 @end group @group (-first 'even? '(1 3 5)) @result{} nil @end group @group (--first (> it 2) '(1 2 3)) @result{} 3 @end group @end example @end defun @anchor{-some} @defun -some (pred list) Return (@var{pred} x) for the first @var{list} item where (@var{pred} x) is non-nil, else nil. Alias: @code{-any} @example @group (-some 'even? '(1 2 3)) @result{} t @end group @group (--some (member 'foo it) '((foo bar) (baz))) @result{} '(foo bar) @end group @group (--some (plist-get it :bar) '((:foo 1 :bar 2) (:baz 3))) @result{} 2 @end group @end example @end defun @anchor{-last} @defun -last (pred list) Return the last x in @var{list} where (@var{pred} x) is non-nil, else nil. @example @group (-last 'even? '(1 2 3 4 5 6 3 3 3)) @result{} 6 @end group @group (-last 'even? '(1 3 7 5 9)) @result{} nil @end group @group (--last (> (length it) 3) '("a" "looong" "word" "and" "short" "one")) @result{} "short" @end group @end example @end defun @anchor{-first-item} @defun -first-item (list) Return the first item of @var{list}, or nil on an empty list. @example @group (-first-item '(1 2 3)) @result{} 1 @end group @group (-first-item nil) @result{} nil @end group @end example @end defun @anchor{-last-item} @defun -last-item (list) Return the last item of @var{list}, or nil on an empty list. @example @group (-last-item '(1 2 3)) @result{} 3 @end group @group (-last-item nil) @result{} nil @end group @end example @end defun @anchor{-butlast} @defun -butlast (list) Return a list of all items in list except for the last. @example @group (-butlast '(1 2 3)) @result{} '(1 2) @end group @group (-butlast '(1 2)) @result{} '(1) @end group @group (-butlast '(1)) @result{} nil @end group @end example @end defun @anchor{-sort} @defun -sort (comparator list) Sort @var{list}, stably, comparing elements using @var{comparator}. Return the sorted list. @var{list} is @var{not} modified by side effects. @var{comparator} is called with two elements of @var{list}, and should return non-nil if the first element should sort before the second. @example @group (-sort '< '(3 1 2)) @result{} '(1 2 3) @end group @group (-sort '> '(3 1 2)) @result{} '(3 2 1) @end group @group (--sort (< it other) '(3 1 2)) @result{} '(1 2 3) @end group @end example @end defun @anchor{-list} @defun -list (&rest args) Return a list with @var{args}. If first item of @var{args} is already a list, simply return @var{args}. If not, return a list with @var{args} as elements. @example @group (-list 1) @result{} '(1) @end group @group (-list 1 2 3) @result{} '(1 2 3) @end group @group (-list '(1 2 3)) @result{} '(1 2 3) @end group @end example @end defun @anchor{-fix} @defun -fix (fn list) Compute the (least) fixpoint of @var{fn} with initial input @var{list}. @var{fn} is called at least once, results are compared with @code{equal}. @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)) @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") @end group @end example @end defun @node Tree operations @section Tree operations Functions pretending lists are trees. @anchor{-tree-seq} @defun -tree-seq (branch children tree) Return a sequence of the nodes in @var{tree}, in depth-first search order. @var{branch} is a predicate of one argument that returns non-nil if the passed argument is a branch, that is, a node that can have children. @var{children} is a function of one argument that returns the children of the passed branch node. Non-branch nodes are simply copied. @example @group (-tree-seq 'listp 'identity '(1 (2 3) 4 (5 (6 7)))) @result{} '((1 (2 3) 4 (5 (6 7))) 1 (2 3) 2 3 4 (5 (6 7)) 5 (6 7) 6 7) @end group @group (-tree-seq 'listp 'reverse '(1 (2 3) 4 (5 (6 7)))) @result{} '((1 (2 3) 4 (5 (6 7))) (5 (6 7)) (6 7) 7 6 5 4 (2 3) 3 2 1) @end group @group (--tree-seq (vectorp it) (append it nil) [1 [2 3] 4 [5 [6 7]]]) @result{} '([1 [2 3] 4 [5 [6 7]]] 1 [2 3] 2 3 4 [5 [6 7]] 5 [6 7] 6 7) @end group @end example @end defun @anchor{-tree-map} @defun -tree-map (fn tree) Apply @var{fn} to each element of @var{tree} while preserving the tree structure. @example @group (-tree-map '1+ '(1 (2 3) (4 (5 6) 7))) @result{} '(2 (3 4) (5 (6 7) 8)) @end group @group (-tree-map '(lambda (x) (cons x (expt 2 x))) '(1 (2 3) 4)) @result{} '((1 . 2) ((2 . 4) (3 . 8)) (4 . 16)) @end group @group (--tree-map (length it) '("" ("

" "text" "

") "")) @result{} '(6 (3 4 4) 7) @end group @end example @end defun @anchor{-tree-map-nodes} @defun -tree-map-nodes (pred fun tree) Call @var{fun} on each node of @var{tree} that satisfies @var{pred}. If @var{pred} returns nil, continue descending down this node. If @var{pred} returns non-nil, apply @var{fun} to this node and do not descend further. @example @group (-tree-map-nodes 'vectorp (lambda (x) (-sum (append x nil))) '(1 [2 3] 4 (5 [6 7] 8))) @result{} '(1 5 4 (5 13 8)) @end group @group (-tree-map-nodes 'keywordp (lambda (x) (symbol-name x)) '(1 :foo 4 ((5 6 :bar) :baz 8))) @result{} '(1 ":foo" 4 ((5 6 ":bar") ":baz" 8)) @end group @group (--tree-map-nodes (eq (car-safe it) 'add-mode) (-concat it (list :mode 'emacs-lisp-mode)) '(with-mode emacs-lisp-mode (foo bar) (add-mode a b) (baz (add-mode c d)))) @result{} '(with-mode emacs-lisp-mode (foo bar) (add-mode a b :mode emacs-lisp-mode) (baz (add-mode c d :mode emacs-lisp-mode))) @end group @end example @end defun @anchor{-tree-reduce} @defun -tree-reduce (fn tree) Use @var{fn} to reduce elements of list @var{tree}. If elements of @var{tree} are lists themselves, apply the reduction recursively. @var{fn} is first applied to first element of the list and second element, then on this result and third element from the list etc. See @code{-reduce-r} (@pxref{-reduce-r}) for how exactly are lists of zero or one element handled. @example @group (-tree-reduce '+ '(1 (2 3) (4 5))) @result{} 15 @end group @group (-tree-reduce 'concat '("strings" (" on" " various") ((" levels")))) @result{} "strings on various levels" @end group @group (--tree-reduce (cond ((stringp it) (concat it " " acc)) (t (let ((sn (symbol-name it))) (concat "<" sn ">" acc "")))) '(body (p "some words") (div "more" (b "bold") "words"))) @result{} "

some words

more bold words
" @end group @end example @end defun @anchor{-tree-reduce-from} @defun -tree-reduce-from (fn init-value tree) Use @var{fn} to reduce elements of list @var{tree}. If elements of @var{tree} are lists themselves, apply the reduction recursively. @var{fn} is first applied to @var{init-value} and first element of the list, then on this result and second element from the list etc. The initial value is ignored on cons pairs as they always contain two elements. @example @group (-tree-reduce-from '+ 1 '(1 (1 1) ((1)))) @result{} 8 @end group @group (--tree-reduce-from (-concat acc (list it)) nil '(1 (2 3 (4 5)) (6 7))) @result{} '((7 6) ((5 4) 3 2) 1) @end group @end example @end defun @anchor{-tree-mapreduce} @defun -tree-mapreduce (fn folder tree) Apply @var{fn} to each element of @var{tree}, and make a list of the results. If elements of @var{tree} are lists themselves, apply @var{fn} recursively to elements of these nested lists. Then reduce the resulting lists using @var{folder} and initial value @var{init-value}. See @code{-reduce-r-from} (@pxref{-reduce-r-from}). This is the same as calling @code{-tree-reduce} (@pxref{-tree-reduce}) after @code{-tree-map} (@pxref{-tree-map}) but is twice as fast as it only traverse the structure once. @example @group (-tree-mapreduce 'list 'append '(1 (2 (3 4) (5 6)) (7 (8 9)))) @result{} '(1 2 3 4 5 6 7 8 9) @end group @group (--tree-mapreduce 1 (+ it acc) '(1 (2 (4 9) (2 1)) (7 (4 3)))) @result{} 9 @end group @group (--tree-mapreduce 0 (max acc (1+ it)) '(1 (2 (4 9) (2 1)) (7 (4 3)))) @result{} 3 @end group @end example @end defun @anchor{-tree-mapreduce-from} @defun -tree-mapreduce-from (fn folder init-value tree) Apply @var{fn} to each element of @var{tree}, and make a list of the results. If elements of @var{tree} are lists themselves, apply @var{fn} recursively to elements of these nested lists. Then reduce the resulting lists using @var{folder} and initial value @var{init-value}. See @code{-reduce-r-from} (@pxref{-reduce-r-from}). This is the same as calling @code{-tree-reduce-from} (@pxref{-tree-reduce-from}) after @code{-tree-map} (@pxref{-tree-map}) but is twice as fast as it only traverse the structure once. @example @group (-tree-mapreduce-from 'identity '* 1 '(1 (2 (3 4) (5 6)) (7 (8 9)))) @result{} 362880 @end group @group (--tree-mapreduce-from (+ it it) (cons it acc) nil '(1 (2 (4 9) (2 1)) (7 (4 3)))) @result{} '(2 (4 (8 18) (4 2)) (14 (8 6))) @end group @group (concat "@{" (--tree-mapreduce-from (cond ((-cons-pair? it) (concat (symbol-name (car it)) " -> " (symbol-name (cdr it)))) (t (concat (symbol-name it) " : @{"))) (concat it (unless (or (equal acc "@}") (equal (substring it (1- (length it))) "@{")) ", ") acc) "@}" '((elips-mode (foo (bar . booze)) (baz . qux)) (c-mode (foo . bla) (bum . bam))))) @result{} "@{elips-mode : @{foo : @{bar -> booze@{, baz -> qux@{, c-mode : @{foo -> bla, bum -> bam@}@}" @end group @end example @end defun @anchor{-clone} @defun -clone (list) Create a deep copy of @var{list}. The new list has the same elements and structure but all cons are replaced with new ones. This is useful when you need to clone a structure such as plist or alist. @example @group (let* ((a '(1 2 3)) (b (-clone a))) (nreverse a) b) @result{} '(1 2 3) @end group @end example @end defun @node Threading macros @section Threading macros @anchor{->} @defun -> (x &optional form &rest more) Thread the expr through the forms. Insert @var{x} as the second item in the first form, making a list of it if it is not a list already. If there are more forms, insert the first form as the second item in second form, etc. @example @group (-> '(2 3 5)) @result{} '(2 3 5) @end group @group (-> '(2 3 5) (append '(8 13))) @result{} '(2 3 5 8 13) @end group @group (-> '(2 3 5) (append '(8 13)) (-slice 1 -1)) @result{} '(3 5 8) @end group @end example @end defun @anchor{->>} @defun ->> (x &optional form &rest more) Thread the expr through the forms. Insert @var{x} as the last item in the first form, making a list of it if it is not a list already. If there are more forms, insert the first form as the last item in second form, etc. @example @group (->> '(1 2 3) (-map 'square)) @result{} '(1 4 9) @end group @group (->> '(1 2 3) (-map 'square) (-remove 'even?)) @result{} '(1 9) @end group @group (->> '(1 2 3) (-map 'square) (-reduce '+)) @result{} 14 @end group @end example @end defun @anchor{-->} @defun --> (x form &rest more) Thread the expr through the forms. Insert @var{x} at the position signified by the token @code{it} in the first form. If there are more forms, insert the first form at the position signified by @code{it} in in second form, etc. @example @group (--> "def" (concat "abc" it "ghi")) @result{} "abcdefghi" @end group @group (--> "def" (concat "abc" it "ghi") (upcase it)) @result{} "ABCDEFGHI" @end group @group (--> "def" (concat "abc" it "ghi") upcase) @result{} "ABCDEFGHI" @end group @end example @end defun @anchor{-some->} @defun -some-> (x &optional form &rest more) When expr is non-nil, thread it through the first form (via @code{->} (@pxref{->})), and when that result is non-nil, through the next form, etc. @example @group (-some-> '(2 3 5)) @result{} '(2 3 5) @end group @group (-some-> 5 square) @result{} 25 @end group @group (-some-> 5 even? square) @result{} nil @end group @end example @end defun @anchor{-some->>} @defun -some->> (x &optional form &rest more) When expr is non-nil, thread it through the first form (via @code{->>} (@pxref{->>})), and when that result is non-nil, through the next form, etc. @example @group (-some->> '(1 2 3) (-map 'square)) @result{} '(1 4 9) @end group @group (-some->> '(1 3 5) (-last 'even?) (+ 100)) @result{} nil @end group @group (-some->> '(2 4 6) (-last 'even?) (+ 100)) @result{} 106 @end group @end example @end defun @anchor{-some-->} @defun -some--> (x &optional form &rest more) When expr in non-nil, thread it through the first form (via @code{-->} (@pxref{-->})), and when that result is non-nil, through the next form, etc. @example @group (-some--> "def" (concat "abc" it "ghi")) @result{} "abcdefghi" @end group @group (-some--> nil (concat "abc" it "ghi")) @result{} nil @end group @group (-some--> '(1 3 5) (-filter 'even? it) (append it it) (-map 'square it)) @result{} nil @end group @end example @end defun @node Binding @section Binding Convenient versions of `let` and `let*` constructs combined with flow control. @anchor{-when-let} @defun -when-let (var-val &rest body) If @var{val} evaluates to non-nil, bind it to @var{var} and execute body. @var{var-val} should be a (@var{var} @var{val}) pair. Note: binding is done according to @code{-let} (@pxref{-let}). @example @group (-when-let (match-index (string-match "d" "abcd")) (+ match-index 2)) @result{} 5 @end group @group (-when-let ((&plist :foo foo) (list :foo "foo")) foo) @result{} "foo" @end group @group (-when-let ((&plist :foo foo) (list :bar "bar")) foo) @result{} nil @end group @end example @end defun @anchor{-when-let*} @defun -when-let* (vars-vals &rest body) If all @var{vals} evaluate to true, bind them to their corresponding @var{vars} and execute body. @var{vars-vals} should be a list of (@var{var} @var{val}) pairs. Note: binding is done according to @code{-let*} (@pxref{-let*}). @var{vals} are evaluated sequentially, and evaluation stops after the first nil @var{val} is encountered. @example @group (-when-let* ((x 5) (y 3) (z (+ y 4))) (+ x y z)) @result{} 15 @end group @group (-when-let* ((x 5) (y nil) (z 7)) (+ x y z)) @result{} nil @end group @end example @end defun @anchor{-if-let} @defun -if-let (var-val then &rest else) If @var{val} evaluates to non-nil, bind it to @var{var} and do @var{then}, otherwise do @var{else}. @var{var-val} should be a (@var{var} @var{val}) pair. Note: binding is done according to @code{-let} (@pxref{-let}). @example @group (-if-let (match-index (string-match "d" "abc")) (+ match-index 3) 7) @result{} 7 @end group @group (--if-let (even? 4) it nil) @result{} t @end group @end example @end defun @anchor{-if-let*} @defun -if-let* (vars-vals then &rest else) If all @var{vals} evaluate to true, bind them to their corresponding @var{vars} and do @var{then}, otherwise do @var{else}. @var{vars-vals} should be a list of (@var{var} @var{val}) pairs. Note: binding is done according to @code{-let*} (@pxref{-let*}). @var{vals} are evaluated sequentially, and evaluation stops after the first nil @var{val} is encountered. @example @group (-if-let* ((x 5) (y 3) (z 7)) (+ x y z) "foo") @result{} 15 @end group @group (-if-let* ((x 5) (y nil) (z 7)) (+ x y z) "foo") @result{} "foo" @end group @group (-if-let* (((_ _ x) '(nil nil 7))) x) @result{} 7 @end group @end example @end defun @anchor{-let} @defun -let (varlist &rest body) Bind variables according to @var{varlist} then eval @var{body}. @var{varlist} is a list of lists of the form (@var{pattern} @var{source}). Each @var{pattern} is matched against the @var{source} "structurally". @var{source} is only evaluated once for each @var{pattern}. Each @var{pattern} is matched recursively, and can therefore contain sub-patterns which are matched against corresponding sub-expressions of @var{source}. All the SOURCEs are evalled before any symbols are bound (i.e. "in parallel"). If @var{varlist} only contains one (@var{pattern} @var{source}) element, you can optionally specify it using a vector and discarding the outer-most parens. Thus (-let ((@var{pattern} @var{source})) ..) becomes (-let [@var{pattern} @var{source}] ..). @code{-let} (@pxref{-let}) uses a convention of not binding places (symbols) starting with _ whenever it's possible. You can use this to skip over entries you don't care about. However, this is not *always* possible (as a result of implementation) and these symbols might get bound to undefined values. Following is the overview of supported patterns. Remember that patterns can be matched recursively, so every a, b, aK in the following can be a matching construct and not necessarily a symbol/variable. Symbol: a - bind the @var{source} to @var{a}. This is just like regular @code{let}. Conses and lists: (a) - bind @code{car} of cons/list to @var{a} (a . b) - bind car of cons to @var{a} and @code{cdr} to @var{b} (a b) - bind car of list to @var{a} and @code{cadr} to @var{b} (a1 a2 a3 ...) - bind 0th car of list to @var{a1}, 1st to @var{a2}, 2nd to @var{a3} ... (a1 a2 a3 ... aN . rest) - as above, but bind the Nth cdr to @var{rest}. Vectors: [a] - bind 0th element of a non-list sequence to @var{a} (works with vectors, strings, bit arrays...) [a1 a2 a3 ...] - bind 0th element of non-list sequence to @var{a0}, 1st to @var{a1}, 2nd to @var{a2}, ... If the @var{pattern} is shorter than @var{source}, the values at places not in @var{pattern} are ignored. If the @var{pattern} is longer than @var{source}, an @code{error} is thrown. [a1 a2 a3 ... &rest rest] - as above, but bind the rest of the sequence to @var{rest}. This is conceptually the same as improper list matching (a1 a2 ... aN . rest) Key/value stores: (&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the @var{source} plist to aK. If the value is not found, aK is nil. (&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the @var{source} alist to aK. If the value is not found, aK is nil. (&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the @var{source} hash table to aK. If the value is not found, aK is nil. Further, special keyword &keys supports "inline" matching of plist-like key-value pairs, similarly to &keys keyword of @code{cl-defun}. (a1 a2 ... aN &keys key1 b1 ... keyN bK) This binds @var{n} values from the list to a1 ... aN, then interprets the cdr as a plist (see key/value matching above). You can name the source using the syntax @var{symbol} &as @var{pattern}. This syntax works with lists (proper or improper), vectors and all types of maps. (list &as a b c) (list 1 2 3) binds @var{a} to 1, @var{b} to 2, @var{c} to 3 and @var{list} to (1 2 3). Similarly: (bounds &as beg . end) (cons 1 2) binds @var{beg} to 1, @var{end} to 2 and @var{bounds} to (1 . 2). (items &as first . rest) (list 1 2 3) binds @var{first} to 1, @var{rest} to (2 3) and @var{items} to (1 2 3) [vect &as _ b c] [1 2 3] binds @var{b} to 2, @var{c} to 3 and @var{vect} to [1 2 3] (_ avoids binding as usual). (plist &as &plist :b b) (list :a 1 :b 2 :c 3) binds @var{b} to 2 and @var{plist} to (:a 1 :b 2 :c 3). Same for &alist and &hash. This is especially useful when we want to capture the result of a computation and destructure at the same time. Consider the form (function-returning-complex-structure) returning a list of two vectors with two items each. We want to capture this entire result and pass it to another computation, but at the same time we want to get the second item from each vector. We can achieve it with pattern (result &as [_ a] [_ b]) (function-returning-complex-structure) Note: Clojure programmers may know this feature as the ":as binding". The difference is that we put the &as at the front because we need to support improper list binding. @example @group (-let (([a (b c) d] [1 (2 3) 4])) (list a b c d)) @result{} '(1 2 3 4) @end group @group (-let [(a b c . d) (list 1 2 3 4 5 6)] (list a b c d)) @result{} '(1 2 3 (4 5 6)) @end group @group (-let [(&plist :foo foo :bar bar) (list :baz 3 :foo 1 :qux 4 :bar 2)] (list foo bar)) @result{} '(1 2) @end group @end example @end defun @anchor{-let*} @defun -let* (varlist &rest body) Bind variables according to @var{varlist} then eval @var{body}. @var{varlist} is a list of lists of the form (@var{pattern} @var{source}). Each @var{pattern} is matched against the @var{source} structurally. @var{source} is only evaluated once for each @var{pattern}. Each @var{source} can refer to the symbols already bound by this @var{varlist}. This is useful if you want to destructure @var{source} recursively but also want to name the intermediate structures. See @code{-let} (@pxref{-let}) for the list of all possible patterns. @example @group (-let* (((a . b) (cons 1 2)) ((c . d) (cons 3 4))) (list a b c d)) @result{} '(1 2 3 4) @end group @group (-let* (((a . b) (cons 1 (cons 2 3))) ((c . d) b)) (list a b c d)) @result{} '(1 (2 . 3) 2 3) @end group @group (-let* (((&alist "foo" foo "bar" bar) (list (cons "foo" 1) (cons "bar" (list 'a 'b 'c)))) ((a b c) bar)) (list foo a b c bar)) @result{} '(1 a b c (a b c)) @end group @end example @end defun @anchor{-lambda} @defun -lambda (match-form &rest body) Return a lambda which destructures its input as @var{match-form} and executes @var{body}. Note that you have to enclose the @var{match-form} in a pair of parens, such that: (-lambda (x) body) (-lambda (x y ...) body) has the usual semantics of @code{lambda}. Furthermore, these get translated into normal lambda, so there is no performance penalty. See @code{-let} (@pxref{-let}) for the description of destructuring mechanism. @example @group (-map (-lambda ((x y)) (+ x y)) '((1 2) (3 4) (5 6))) @result{} '(3 7 11) @end group @group (-map (-lambda ([x y]) (+ x y)) '([1 2] [3 4] [5 6])) @result{} '(3 7 11) @end group @group (funcall (-lambda ((_ . a) (_ . b)) (-concat a b)) '(1 2 3) '(4 5 6)) @result{} '(2 3 5 6) @end group @end example @end defun @node Side-effects @section Side-effects Functions iterating over lists for side-effect only. @anchor{-each} @defun -each (list fn) Call @var{fn} with every item in @var{list}. Return nil, used for side-effects only. @example @group (let (s) (-each '(1 2 3) (lambda (item) (setq s (cons item s))))) @result{} nil @end group @group (let (s) (-each '(1 2 3) (lambda (item) (setq s (cons item s)))) s) @result{} '(3 2 1) @end group @group (let (s) (--each '(1 2 3) (setq s (cons it s))) s) @result{} '(3 2 1) @end group @end example @end defun @anchor{-each-while} @defun -each-while (list pred fn) Call @var{fn} with every item in @var{list} while (@var{pred} item) is non-nil. Return nil, used for side-effects only. @example @group (let (s) (-each-while '(2 4 5 6) 'even? (lambda (item) (!cons item s))) s) @result{} '(4 2) @end group @group (let (s) (--each-while '(1 2 3 4) (< it 3) (!cons it s)) s) @result{} '(2 1) @end group @end example @end defun @anchor{-dotimes} @defun -dotimes (num fn) Repeatedly calls @var{fn} (presumably for side-effects) passing in integers from 0 through @var{num-1}. @example @group (let (s) (-dotimes 3 (lambda (n) (!cons n s))) s) @result{} '(2 1 0) @end group @group (let (s) (--dotimes 5 (!cons it s)) s) @result{} '(4 3 2 1 0) @end group @end example @end defun @node Destructive operations @section Destructive operations @anchor{!cons} @defun !cons (car cdr) Destructive: Set @var{cdr} to the cons of @var{car} and @var{cdr}. @example @group (let (l) (!cons 5 l) l) @result{} '(5) @end group @group (let ((l '(3))) (!cons 5 l) l) @result{} '(5 3) @end group @end example @end defun @anchor{!cdr} @defun !cdr (list) Destructive: Set @var{list} to the cdr of @var{list}. @example @group (let ((l '(3))) (!cdr l) l) @result{} '() @end group @group (let ((l '(3 5))) (!cdr l) l) @result{} '(5) @end group @end example @end defun @node Function combinators @section Function combinators These combinators require Emacs 24 for its lexical scope. So they are offered in a separate package: `dash-functional`. @anchor{-partial} @defun -partial (fn &rest args) Takes a function @var{fn} and fewer than the normal arguments to @var{fn}, and returns a fn that takes a variable number of additional @var{args}. When called, the returned function calls @var{fn} with @var{args} first and then additional args. @example @group (funcall (-partial '- 5) 3) @result{} 2 @end group @group (funcall (-partial '+ 5 2) 3) @result{} 10 @end group @end example @end defun @anchor{-rpartial} @defun -rpartial (fn &rest args) Takes a function @var{fn} and fewer than the normal arguments to @var{fn}, and returns a fn that takes a variable number of additional @var{args}. When called, the returned function calls @var{fn} with the additional args first and then @var{args}. @example @group (funcall (-rpartial '- 5) 8) @result{} 3 @end group @group (funcall (-rpartial '- 5 2) 10) @result{} 3 @end group @end example @end defun @anchor{-juxt} @defun -juxt (&rest fns) Takes a list of functions and returns a fn that is the juxtaposition of those fns. The returned fn takes a variable number of args, and returns a list containing the result of applying each fn to the args (left-to-right). @example @group (funcall (-juxt '+ '-) 3 5) @result{} '(8 -2) @end group @group (-map (-juxt 'identity 'square) '(1 2 3)) @result{} '((1 1) (2 4) (3 9)) @end group @end example @end defun @anchor{-compose} @defun -compose (&rest fns) Takes a list of functions and returns a fn that is the composition of those fns. The returned fn takes a variable number of arguments, and returns the result of applying each fn to the result of applying the previous fn to the arguments (right-to-left). @example @group (funcall (-compose 'square '+) 2 3) @result{} (square (+ 2 3)) @end group @group (funcall (-compose 'identity 'square) 3) @result{} (square 3) @end group @group (funcall (-compose 'square 'identity) 3) @result{} (square 3) @end group @end example @end defun @anchor{-applify} @defun -applify (fn) Changes an n-arity function @var{fn} to a 1-arity function that expects a list with n items as arguments @example @group (-map (-applify '+) '((1 1 1) (1 2 3) (5 5 5))) @result{} '(3 6 15) @end group @group (-map (-applify (lambda (a b c) (\` ((\, a) ((\, b) ((\, c))))))) '((1 1 1) (1 2 3) (5 5 5))) @result{} '((1 (1 (1))) (1 (2 (3))) (5 (5 (5)))) @end group @group (funcall (-applify '<) '(3 6)) @result{} t @end group @end example @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). In types: (b -> b -> c) -> (a -> b) -> a -> a -> c @example @group (-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) @end group @group (-min-by (-on 'string-lessp 'int-to-string) '(2 100 22)) @result{} 22 @end group @end example @end defun @anchor{-flip} @defun -flip (func) Swap the order of arguments for binary function @var{func}. In types: (a -> b -> c) -> b -> a -> c @example @group (funcall (-flip '<) 2 1) @result{} t @end group @group (funcall (-flip '-) 3 8) @result{} 5 @end group @group (-sort (-flip '<) '(4 3 6 1)) @result{} '(6 4 3 1) @end group @end example @end defun @anchor{-const} @defun -const (c) Return a function that returns @var{c} ignoring any additional arguments. In types: a -> b -> a @example @group (funcall (-const 2) 1 3 "foo") @result{} 2 @end group @group (-map (-const 1) '("a" "b" "c" "d")) @result{} '(1 1 1 1) @end group @group (-sum (-map (-const 1) '("a" "b" "c" "d"))) @result{} 4 @end group @end example @end defun @anchor{-cut} @defun -cut (&rest params) Take n-ary function and n arguments and specialize some of them. Arguments denoted by <> will be left unspecialized. See @var{srfi-26} for detailed description. @example @group (funcall (-cut list 1 <> 3 <> 5) 2 4) @result{} '(1 2 3 4 5) @end group @group (-map (-cut funcall <> 5) '(1+ 1- (lambda (x) (/ 1.0 x)))) @result{} '(6 4 0.2) @end group @group (-filter (-cut < <> 5) '(1 3 5 7 9)) @result{} '(1 3) @end group @end example @end defun @anchor{-not} @defun -not (pred) Take an unary predicates @var{pred} and return an unary predicate that returns t if @var{pred} returns nil and nil if @var{pred} returns non-nil. @example @group (funcall (-not 'even?) 5) @result{} t @end group @group (-filter (-not (-partial '< 4)) '(1 2 3 4 5 6 7 8)) @result{} '(1 2 3 4) @end group @end example @end defun @anchor{-orfn} @defun -orfn (&rest preds) Take list of unary predicates @var{preds} and return an unary predicate with argument x that returns non-nil if at least one of the @var{preds} returns non-nil on x. In types: [a -> Bool] -> a -> Bool @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) @end group @group (funcall (-orfn 'stringp 'even?) "foo") @result{} t @end group @end example @end defun @anchor{-andfn} @defun -andfn (&rest preds) Take list of unary predicates @var{preds} and return an unary predicate with argument x that returns non-nil if all of the @var{preds} returns non-nil on x. In types: [a -> Bool] -> a -> Bool @example @group (funcall (-andfn (-cut < <> 10) 'even?) 6) @result{} t @end group @group (funcall (-andfn (-cut < <> 10) 'even?) 12) @result{} nil @end group @group (-filter (-andfn (-not 'even?) (-cut >= 5 <>)) '(1 2 3 4 5 6 7 8 9 10)) @result{} '(1 3 5) @end group @end example @end defun @anchor{-iteratefn} @defun -iteratefn (fn n) Return a function @var{fn} composed @var{n} times with itself. @var{fn} is a unary function. If you need to use a function of higher arity, use @code{-applify} (@pxref{-applify}) first to turn it into an unary function. With n = 0, this acts as identity function. In types: (a -> a) -> Int -> a -> a. This function satisfies the following law: (funcall (-iteratefn fn n) init) = (-last-item (-iterate fn init (1+ n))). @example @group (funcall (-iteratefn (lambda (x) (* x x)) 3) 2) @result{} 256 @end group @group (funcall (-iteratefn '1+ 3) 1) @result{} 4 @end group @group (funcall (-iteratefn 'cdr 3) '(1 2 3 4 5)) @result{} '(4 5) @end group @end example @end defun @anchor{-fixfn} @defun -fixfn (fn &optional equal-test halt-test) Return a function that computes the (least) fixpoint of @var{fn}. @var{fn} must be a unary function. The returned lambda takes a single argument, @var{x}, the initial value for the fixpoint iteration. The iteration halts when either of the following conditions is satisified: 1. Iteration converges to the fixpoint, with equality being tested using @var{equal-test}. If @var{equal-test} is not specified, @code{equal} is used. For functions over the floating point numbers, it may be necessary to provide an appropriate appoximate comparsion test. 2. @var{halt-test} returns a non-nil value. @var{halt-test} defaults to a simple counter that returns t after @code{-fixfn-max-iterations}, to guard against infinite iteration. Otherwise, @var{halt-test} must be a function that accepts a single argument, the current value of @var{x}, and returns non-nil as long as iteration should continue. In this way, a more sophisticated convergence test may be supplied by the caller. The return value of the lambda is either the fixpoint or, if iteration halted before converging, a cons with car @code{halted} and cdr the final output from @var{halt-test}. In types: (a -> a) -> a -> a. @example @group (funcall (-fixfn 'cos 'approx-equal) 0.7) @result{} 0.7390851332151607 @end group @group (funcall (-fixfn (lambda (x) (expt (+ x 10) 0.25))) 2.0) @result{} 1.8555845286409378 @end group @group (funcall (-fixfn 'sin 'approx-equal) 0.1) @result{} '(halted . t) @end group @end example @end defun @anchor{-prodfn} @defun -prodfn (&rest fns) Take a list of n functions and return a function that takes a list of length n, applying i-th function to i-th element of the input list. Returns a list of length n. In types (for n=2): ((a -> b), (c -> d)) -> (a, c) -> (b, d) This function satisfies the following laws: (-compose (-prodfn f g ...) (-prodfn f' g' ...)) = (-prodfn (-compose f f') (-compose g g') ...) (-prodfn f g ...) = (-juxt (-compose f (-partial 'nth 0)) (-compose g (-partial 'nth 1)) ...) (-compose (-prodfn f g ...) (-juxt f' g' ...)) = (-juxt (-compose f f') (-compose g g') ...) (-compose (-partial 'nth n) (-prod f1 f2 ...)) = (-compose fn (-partial 'nth n)) @example @group (funcall (-prodfn '1+ '1- 'int-to-string) '(1 2 3)) @result{} '(2 1 "3") @end group @group (-map (-prodfn '1+ '1-) '((1 2) (3 4) (5 6) (7 8))) @result{} '((2 1) (4 3) (6 5) (8 7)) @end group @group (apply '+ (funcall (-prodfn 'length 'string-to-int) '((1 2 3) "15"))) @result{} 18 @end group @end example @end defun @node Development @chapter Development The dash repository is hosted on GitHub: @uref{https://github.com/magnars/dash.el} @menu * Contribute:: How to contribute * Changes:: List of significant changes by version * Contributors:: List of contributors @end menu @node Contribute @section Contribute Yes, please do. Pure functions in the list manipulation realm only, please. There's a suite of tests in @verb{~dev/examples.el~}, so remember to add tests for your function, or it might get broken later. Run the tests with @code{./run-tests.sh}. Create the docs with @code{./create-docs.sh}. I highly recommend that you install these as a pre-commit hook, so that the tests are always running and the docs are always in sync: @verbatim cp pre-commit.sh .git/hooks/pre-commit @end verbatim Oh, and don't edit @file{README.md} directly, it is auto-generated. Change @file{readme-template.md} or @file{examples-to-docs.el} instead. The same goes for the info manual. @node Changes @section Changes @noindent Changes in 2.10: @itemize @item Add @code{-let} destructuring to @code{-if-let} and @code{-when-let} (Fredrik Bergroth) @end itemize @noindent Changes in 2.9: @itemize @item Add @code{-let}, @code{-let*} and @code{-lambda} with destructuring @item Add @code{-tree-seq} and @code{-tree-map-nodes} @item Add @code{-non-nil} @item Add @code{-fix} @item Add @code{-fixfn} (dash-functional 1.2) @item Add @code{-copy} (Wilfred Hughes) @end itemize @noindent Changes in 2.8: @itemize @item Add @code{-butlast} @end itemize @noindent Changes in 2.7: @itemize @item @code{-zip} now supports more than two lists (Steve Lamb) @item Add @code{-cycle}, @code{-pad}, @code{-annotate}, @code{-zip-fill} (Steve Lamb) @item Add @code{-table}, @code{-table-flat} (finite cartesian product) @item Add @code{-flatten-n} @item @code{-slice} now supports "step" argument @item Add functional combinators @code{-iteratefn}, @code{-prodfn} @item Add @code{-replace}, @code{-splice}, @code{-splice-list} which generalize @code{-replace-at} and @code{-insert-at} @item Add @code{-compose}, @code{-iteratefn} and @code{-prodfn} (dash-functional 1.1) @end itemize @noindent Changes in 2.6: @itemize @item Add @code{-is-prefix-p}, @code{-is-suffix-p}, @code{-is-infix-p} (Matus Goljer) @item Add @code{-iterate}, @code{-unfold} (Matus Goljer) @item Add @code{-split-on}, @code{-split-when} (Matus Goljer) @item Add @code{-find-last-index} (Matus Goljer) @item Add @code{-list} (Johan Andersson) @end itemize @noindent Changes in 2.5: @itemize @item Add @code{-same-items?} (Johan Andersson) @item A few bugfixes @end itemize @noindent Changes in 2.4: @itemize @item Add @code{-snoc} (Matus Goljer) @item Add @code{-replace-at}, @code{-update-at}, @code{-remove-at}, and @code{-remove-at-indices} (Matus Goljer) @end itemize @noindent Changes in 2.3: @itemize @item Add tree operations (Matus Goljer) @item Make font-lock optional @end itemize @noindent Changes in 2.2: @itemize @item Add @code{-compose} (Christina Whyte) @end itemize @noindent Changes in 2.1: @itemize @item Add indexing operations (Matus Goljer) @end itemize @noindent Changes in 2.0: @itemize @item Split out @code{dash-functional.el} (Matus Goljer) @item Add @code{-andfn}, @code{-orfn}, @code{-not}, @code{-cut}, @code{-const}, @code{-flip} and @code{-on}. (Matus Goljer) @item Fix @code{-min}, @code{-max}, @code{-min-by} and @code{-max-by} (Matus Goljer) @end itemize @noindent Changes in 1.8: @itemize @item Add @code{-first-item} and @code{-last-item} (Wilfred Hughes) @end itemize @noindent Changes in 1.7: @itemize @item Add @code{-rotate} (Matus Goljer) @end itemize @noindent Changes in 1.6: @itemize @item Add @code{-min}, @code{-max}, @code{-min-by} and @code{-max-by} (Johan Andersson) @end itemize @noindent Changes in 1.5: @itemize @item Add @code{-sum} and @code{-product} (Johan Andersson) @end itemize @noindent Changes in 1.4: @itemize @item Add @code{-sort} @item Add @code{-reduce-r} (Matus Goljer) @item Add @code{-reduce-r-from} (Matus Goljer) @end itemize @noindent Changes in 1.3: @itemize @item Add @code{-partition-in-steps} @item Add @code{-partition-all-in-steps} @end itemize @noindent Changes in 1.2: @itemize @item Add @code{-last} (Matus Goljer) @item Add @code{-insert-at} (Emanuel Evans) @item Add @code{-when-let} and @code{-if-let} (Emanuel Evans) @item Add @code{-when-let*} and @code{-if-let*} (Emanuel Evans) @item Some bugfixes @end itemize @node Contributors @section Contributors @itemize @item @uref{https://github.com/Fuco1,Matus Goljer} contributed lots of features and functions. @item @uref{https://github.com/tkf,Takafumi Arakaki} contributed @code{-group-by}. @item @uref{https://github.com/tali713,tali713} is the author of @code{-applify}. @item @uref{https://github.com/vemv,Víctor M. Valenzuela} contributed @code{-repeat}. @item @uref{https://github.com/nicferrier,Nic Ferrier} contributed @code{-cons*}. @item @uref{https://github.com/Wilfred,Wilfred Hughes} contributed @code{-slice}, @code{-first-item} and @code{-last-item}. @item @uref{https://github.com/shosti,Emanuel Evans} contributed @code{-if-let}, @code{-when-let} and @code{-insert-at}. @item @uref{https://github.com/rejeep,Johan Andersson} contributed @code{-sum}, @code{-product} and @code{-same-items?} @item @uref{https://github.com/kurisuwhyte,Christina Whyte} contributed @code{-compose} @item @uref{https://github.com/steventlamb,Steve Lamb} contributed @code{-cycle}, @code{-pad}, @code{-annotate}, @code{-zip-fill} and an n-ary version of @code{-zip}. @item @uref{https://github.com/fbergroth,Fredrik Bergroth} made the @code{-if-let} family use @code{-let} destructuring and improved script for generating documentation. @item @uref{https://github.com/holomorph,Mark Oteiza} contributed the script to create an info manual. @item @uref{https://github.com/wasamasa,Vasilij Schneidermann} contributed @code{-some}. @item @uref{https://github.com/occidens,William West} made @code{-fixfn} more robust at handling floats. @end itemize Thanks! @node Index @unnumbered Index @printindex cp @bye dash.el-2.12.1/dev/000077500000000000000000000000001261164447300137015ustar00rootroot00000000000000dash.el-2.12.1/dev/.nosearch000066400000000000000000000000001261164447300154720ustar00rootroot00000000000000dash.el-2.12.1/dev/ert.el000066400000000000000000003166561261164447300150360ustar00rootroot00000000000000;;; ert.el --- Emacs Lisp Regression Testing ;; Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc. ;; Author: Christian M. Ohler ;; Keywords: lisp, tools ;; This file is NOT part of GNU Emacs. ;; This program is free software: you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation, either version 3 of the ;; License, or (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see `http://www.gnu.org/licenses/'. ;;; Commentary: ;; ERT is a tool for automated testing in Emacs Lisp. Its main ;; features are facilities for defining and running test cases and ;; reporting the results as well as for debugging test failures ;; interactively. ;; ;; The main entry points are `ert-deftest', which is similar to ;; `defun' but defines a test, and `ert-run-tests-interactively', ;; which runs tests and offers an interactive interface for inspecting ;; results and debugging. There is also ;; `ert-run-tests-batch-and-exit' for non-interactive use. ;; ;; The body of `ert-deftest' forms resembles a function body, but the ;; additional operators `should', `should-not' and `should-error' are ;; available. `should' is similar to cl's `assert', but signals a ;; different error when its condition is violated that is caught and ;; processed by ERT. In addition, it analyzes its argument form and ;; records information that helps debugging (`assert' tries to do ;; something similar when its second argument SHOW-ARGS is true, but ;; `should' is more sophisticated). For information on `should-not' ;; and `should-error', see their docstrings. ;; ;; See ERT's info manual as well as the docstrings for more details. ;; To compile the manual, run `makeinfo ert.texinfo' in the ERT ;; directory, then C-u M-x info ert.info in Emacs to view it. ;; ;; To see some examples of tests written in ERT, see its self-tests in ;; ert-tests.el. Some of these are tricky due to the bootstrapping ;; problem of writing tests for a testing tool, others test simple ;; functions and are straightforward. ;;; Code: (eval-when-compile (require 'cl)) (require 'button) (require 'debug) (require 'easymenu) (require 'ewoc) (require 'find-func) (require 'help) ;;; UI customization options. (defgroup ert () "ERT, the Emacs Lisp regression testing tool." :prefix "ert-" :group 'lisp) (defface ert-test-result-expected '((((class color) (background light)) :background "green1") (((class color) (background dark)) :background "green3")) "Face used for expected results in the ERT results buffer." :group 'ert) (defface ert-test-result-unexpected '((((class color) (background light)) :background "red1") (((class color) (background dark)) :background "red3")) "Face used for unexpected results in the ERT results buffer." :group 'ert) ;;; Copies/reimplementations of cl functions. (defun ert--cl-do-remf (plist tag) "Copy of `cl-do-remf'. Modify PLIST by removing TAG." (let ((p (cdr plist))) (while (and (cdr p) (not (eq (car (cdr p)) tag))) (setq p (cdr (cdr p)))) (and (cdr p) (progn (setcdr p (cdr (cdr (cdr p)))) t)))) (defun ert--remprop (sym tag) "Copy of `cl-remprop'. Modify SYM's plist by removing TAG." (let ((plist (symbol-plist sym))) (if (and plist (eq tag (car plist))) (progn (setplist sym (cdr (cdr plist))) t) (ert--cl-do-remf plist tag)))) (defun ert--remove-if-not (ert-pred ert-list) "A reimplementation of `remove-if-not'. ERT-PRED is a predicate, ERT-LIST is the input list." (loop for ert-x in ert-list if (funcall ert-pred ert-x) collect ert-x)) (defun ert--intersection (a b) "A reimplementation of `intersection'. Intersect the sets A and B. Elements are compared using `eql'." (loop for x in a if (memql x b) collect x)) (defun ert--set-difference (a b) "A reimplementation of `set-difference'. Subtract the set B from the set A. Elements are compared using `eql'." (loop for x in a unless (memql x b) collect x)) (defun ert--set-difference-eq (a b) "A reimplementation of `set-difference'. Subtract the set B from the set A. Elements are compared using `eq'." (loop for x in a unless (memq x b) collect x)) (defun ert--union (a b) "A reimplementation of `union'. Compute the union of the sets A and B. Elements are compared using `eql'." (append a (ert--set-difference b a))) (eval-and-compile (defvar ert--gensym-counter 0)) (eval-and-compile (defun ert--gensym (&optional prefix) "Only allows string PREFIX, not compatible with CL." (unless prefix (setq prefix "G")) (make-symbol (format "%s%s" prefix (prog1 ert--gensym-counter (incf ert--gensym-counter)))))) (defun ert--coerce-to-vector (x) "Coerce X to a vector." (when (char-table-p x) (error "Not supported")) (if (vectorp x) x (vconcat x))) (defun* ert--remove* (x list &key key test) "Does not support all the keywords of remove*." (unless key (setq key #'identity)) (unless test (setq test #'eql)) (loop for y in list unless (funcall test x (funcall key y)) collect y)) (defun ert--string-position (c s) "Return the position of the first occurrence of C in S, or nil if none." (loop for i from 0 for x across s when (eql x c) return i)) (defun ert--mismatch (a b) "Return index of first element that differs between A and B. Like `mismatch'. Uses `equal' for comparison." (cond ((or (listp a) (listp b)) (ert--mismatch (ert--coerce-to-vector a) (ert--coerce-to-vector b))) ((> (length a) (length b)) (ert--mismatch b a)) (t (let ((la (length a)) (lb (length b))) (assert (arrayp a) t) (assert (arrayp b) t) (assert (<= la lb) t) (loop for i below la when (not (equal (aref a i) (aref b i))) return i finally (return (if (/= la lb) la (assert (equal a b) t) nil))))))) (defun ert--subseq (seq start &optional end) "Return a subsequence of SEQ from START to END." (when (char-table-p seq) (error "Not supported")) (let ((vector (substring (ert--coerce-to-vector seq) start end))) (etypecase seq (vector vector) (string (concat vector)) (list (append vector nil)) (bool-vector (loop with result = (make-bool-vector (length vector) nil) for i below (length vector) do (setf (aref result i) (aref vector i)) finally (return result))) (char-table (assert nil))))) (defun ert-equal-including-properties (a b) "Return t if A and B have similar structure and contents. This is like `equal-including-properties' except that it compares the property values of text properties structurally (by recursing) rather than with `eq'. Perhaps this is what `equal-including-properties' should do in the first place; see Emacs bug 6581 at URL `http://debbugs.gnu.org/cgi/bugreport.cgi?bug=6581'." ;; This implementation is inefficient. Rather than making it ;; efficient, let's hope bug 6581 gets fixed so that we can delete ;; it altogether. (not (ert--explain-not-equal-including-properties a b))) ;;; Defining and locating tests. ;; The data structure that represents a test case. (defstruct ert-test (name nil) (documentation nil) (body (assert nil)) (most-recent-result nil) (expected-result-type ':passed) (tags '())) (defun ert-test-boundp (symbol) "Return non-nil if SYMBOL names a test." (and (get symbol 'ert--test) t)) (defun ert-get-test (symbol) "If SYMBOL names a test, return that. Signal an error otherwise." (unless (ert-test-boundp symbol) (error "No test named `%S'" symbol)) (get symbol 'ert--test)) (defun ert-set-test (symbol definition) "Make SYMBOL name the test DEFINITION, and return DEFINITION." (when (eq symbol 'nil) ;; We disallow nil since `ert-test-at-point' and related functions ;; want to return a test name, but also need an out-of-band value ;; on failure. Nil is the most natural out-of-band value; using 0 ;; or "" or signalling an error would be too awkward. ;; ;; Note that nil is still a valid value for the `name' slot in ;; ert-test objects. It designates an anonymous test. (error "Attempt to define a test named nil")) (put symbol 'ert--test definition) definition) (defun ert-make-test-unbound (symbol) "Make SYMBOL name no test. Return SYMBOL." (ert--remprop symbol 'ert--test) symbol) (defun ert--parse-keys-and-body (keys-and-body) "Split KEYS-AND-BODY into keyword-and-value pairs and the remaining body. KEYS-AND-BODY should have the form of a property list, with the exception that only keywords are permitted as keys and that the tail -- the body -- is a list of forms that does not start with a keyword. Returns a two-element list containing the keys-and-values plist and the body." (let ((extracted-key-accu '()) (remaining keys-and-body)) (while (and (consp remaining) (keywordp (first remaining))) (let ((keyword (pop remaining))) (unless (consp remaining) (error "Value expected after keyword %S in %S" keyword keys-and-body)) (when (assoc keyword extracted-key-accu) (warn "Keyword %S appears more than once in %S" keyword keys-and-body)) (push (cons keyword (pop remaining)) extracted-key-accu))) (setq extracted-key-accu (nreverse extracted-key-accu)) (list (loop for (key . value) in extracted-key-accu collect key collect value) remaining))) ;;;###autoload (defmacro* ert-deftest (name () &body docstring-keys-and-body) "Define NAME (a symbol) as a test. BODY is evaluated as a `progn' when the test is run. It should signal a condition on failure or just return if the test passes. `should', `should-not' and `should-error' are useful for assertions in BODY. Use `ert' to run tests interactively. Tests that are expected to fail can be marked as such using :expected-result. See `ert-test-result-type-p' for a description of valid values for RESULT-TYPE. \(fn NAME () [DOCSTRING] [:expected-result RESULT-TYPE] \ \[:tags '(TAG...)] BODY...)" (declare (debug (&define :name test name sexp [&optional stringp] [&rest keywordp sexp] def-body)) (doc-string 3) (indent 2)) (let ((documentation nil) (documentation-supplied-p nil)) (when (stringp (first docstring-keys-and-body)) (setq documentation (pop docstring-keys-and-body) documentation-supplied-p t)) (destructuring-bind ((&key (expected-result nil expected-result-supplied-p) (tags nil tags-supplied-p)) body) (ert--parse-keys-and-body docstring-keys-and-body) `(progn (ert-set-test ',name (make-ert-test :name ',name ,@(when documentation-supplied-p `(:documentation ,documentation)) ,@(when expected-result-supplied-p `(:expected-result-type ,expected-result)) ,@(when tags-supplied-p `(:tags ,tags)) :body (lambda () ,@body))) ;; This hack allows `symbol-file' to associate `ert-deftest' ;; forms with files, and therefore enables `find-function' to ;; work with tests. However, it leads to warnings in ;; `unload-feature', which doesn't know how to undefine tests ;; and has no mechanism for extension. (push '(ert-deftest . ,name) current-load-list) ',name)))) ;; We use these `put' forms in addition to the (declare (indent)) in ;; the defmacro form since the `declare' alone does not lead to ;; correct indentation before the .el/.elc file is loaded. ;; Autoloading these `put' forms solves this. ;;;###autoload (progn ;; TODO(ohler): Figure out what these mean and make sure they are correct. (put 'ert-deftest 'lisp-indent-function 2) (put 'ert-info 'lisp-indent-function 1)) (defvar ert--find-test-regexp (concat "^\\s-*(ert-deftest" find-function-space-re "%s\\(\\s-\\|$\\)") "The regexp the `find-function' mechanisms use for finding test definitions.") (put 'ert-test-failed 'error-conditions '(error ert-test-failed)) (put 'ert-test-failed 'error-message "Test failed") (defun ert-pass () "Terminate the current test and mark it passed. Does not return." (throw 'ert--pass nil)) (defun ert-fail (data) "Terminate the current test and mark it failed. Does not return. DATA is displayed to the user and should state the reason of the failure." (signal 'ert-test-failed (list data))) ;;; The `should' macros. (defvar ert--should-execution-observer nil) (defun ert--signal-should-execution (form-description) "Tell the current `should' form observer (if any) about FORM-DESCRIPTION." (when ert--should-execution-observer (funcall ert--should-execution-observer form-description))) (defun ert--special-operator-p (thing) "Return non-nil if THING is a symbol naming a special operator." (and (symbolp thing) (let ((definition (indirect-function thing t))) (and (subrp definition) (eql (cdr (subr-arity definition)) 'unevalled))))) (defun ert--expand-should-1 (whole form inner-expander) "Helper function for the `should' macro and its variants." (let ((form ;; If `cl-macroexpand' isn't bound, the code that we're ;; compiling doesn't depend on cl and thus doesn't need an ;; environment arg for `macroexpand'. (if (fboundp 'cl-macroexpand) ;; Suppress warning about run-time call to cl funtion: we ;; only call it if it's fboundp. (with-no-warnings (cl-macroexpand form (and (boundp 'cl-macro-environment) cl-macro-environment))) (macroexpand form)))) (cond ((or (atom form) (ert--special-operator-p (car form))) (let ((value (ert--gensym "value-"))) `(let ((,value (ert--gensym "ert-form-evaluation-aborted-"))) ,(funcall inner-expander `(setq ,value ,form) `(list ',whole :form ',form :value ,value) value) ,value))) (t (let ((fn-name (car form)) (arg-forms (cdr form))) (assert (or (symbolp fn-name) (and (consp fn-name) (eql (car fn-name) 'lambda) (listp (cdr fn-name))))) (let ((fn (ert--gensym "fn-")) (args (ert--gensym "args-")) (value (ert--gensym "value-")) (default-value (ert--gensym "ert-form-evaluation-aborted-"))) `(let ((,fn (function ,fn-name)) (,args (list ,@arg-forms))) (let ((,value ',default-value)) ,(funcall inner-expander `(setq ,value (apply ,fn ,args)) `(nconc (list ',whole) (list :form `(,,fn ,@,args)) (unless (eql ,value ',default-value) (list :value ,value)) (let ((-explainer- (and (symbolp ',fn-name) (get ',fn-name 'ert-explainer)))) (when -explainer- (list :explanation (apply -explainer- ,args))))) value) ,value)))))))) (defun ert--expand-should (whole form inner-expander) "Helper function for the `should' macro and its variants. Analyzes FORM and returns an expression that has the same semantics under evaluation but records additional debugging information. INNER-EXPANDER should be a function and is called with two arguments: INNER-FORM and FORM-DESCRIPTION-FORM, where INNER-FORM is an expression equivalent to FORM, and FORM-DESCRIPTION-FORM is an expression that returns a description of FORM. INNER-EXPANDER should return code that calls INNER-FORM and performs the checks and error signalling specific to the particular variant of `should'. The code that INNER-EXPANDER returns must not call FORM-DESCRIPTION-FORM before it has called INNER-FORM." (lexical-let ((inner-expander inner-expander)) (ert--expand-should-1 whole form (lambda (inner-form form-description-form value-var) (let ((form-description (ert--gensym "form-description-"))) `(let (,form-description) ,(funcall inner-expander `(unwind-protect ,inner-form (setq ,form-description ,form-description-form) (ert--signal-should-execution ,form-description)) `,form-description value-var))))))) (defmacro* should (form) "Evaluate FORM. If it returns nil, abort the current test as failed. Returns the value of FORM." (ert--expand-should `(should ,form) form (lambda (inner-form form-description-form value-var) `(unless ,inner-form (ert-fail ,form-description-form))))) (defmacro* should-not (form) "Evaluate FORM. If it returns non-nil, abort the current test as failed. Returns nil." (ert--expand-should `(should-not ,form) form (lambda (inner-form form-description-form value-var) `(unless (not ,inner-form) (ert-fail ,form-description-form))))) (defun ert--should-error-handle-error (form-description-fn condition type exclude-subtypes) "Helper function for `should-error'. Determines whether CONDITION matches TYPE and EXCLUDE-SUBTYPES, and aborts the current test as failed if it doesn't." (let ((signalled-conditions (get (car condition) 'error-conditions)) (handled-conditions (etypecase type (list type) (symbol (list type))))) (assert signalled-conditions) (unless (ert--intersection signalled-conditions handled-conditions) (ert-fail (append (funcall form-description-fn) (list :condition condition :fail-reason (concat "the error signalled did not" " have the expected type"))))) (when exclude-subtypes (unless (member (car condition) handled-conditions) (ert-fail (append (funcall form-description-fn) (list :condition condition :fail-reason (concat "the error signalled was a subtype" " of the expected type")))))))) ;; FIXME: The expansion will evaluate the keyword args (if any) in ;; nonstandard order. (defmacro* should-error (form &rest keys &key type exclude-subtypes) "Evaluate FORM and check that it signals an error. The error signalled needs to match TYPE. TYPE should be a list of condition names. (It can also be a non-nil symbol, which is equivalent to a singleton list containing that symbol.) If EXCLUDE-SUBTYPES is nil, the error matches TYPE if one of its condition names is an element of TYPE. If EXCLUDE-SUBTYPES is non-nil, the error matches TYPE if it is an element of TYPE. If the error matches, returns (ERROR-SYMBOL . DATA) from the error. If not, or if no error was signalled, abort the test as failed." (unless type (setq type ''error)) (ert--expand-should `(should-error ,form ,@keys) form (lambda (inner-form form-description-form value-var) (let ((errorp (ert--gensym "errorp")) (form-description-fn (ert--gensym "form-description-fn-"))) `(let ((,errorp nil) (,form-description-fn (lambda () ,form-description-form))) (condition-case -condition- ,inner-form ;; We can't use ,type here because we want to evaluate it. (error (setq ,errorp t) (ert--should-error-handle-error ,form-description-fn -condition- ,type ,exclude-subtypes) (setq ,value-var -condition-))) (unless ,errorp (ert-fail (append (funcall ,form-description-fn) (list :fail-reason "did not signal an error"))))))))) ;;; Explanation of `should' failures. ;; TODO(ohler): Rework explanations so that they are displayed in a ;; similar way to `ert-info' messages; in particular, allow text ;; buttons in explanations that give more detail or open an ediff ;; buffer. Perhaps explanations should be reported through `ert-info' ;; rather than as part of the condition. (defun ert--proper-list-p (x) "Return non-nil if X is a proper list, nil otherwise." (loop for firstp = t then nil for fast = x then (cddr fast) for slow = x then (cdr slow) do (when (null fast) (return t)) (when (not (consp fast)) (return nil)) (when (null (cdr fast)) (return t)) (when (not (consp (cdr fast))) (return nil)) (when (and (not firstp) (eq fast slow)) (return nil)))) (defun ert--explain-format-atom (x) "Format the atom X for `ert--explain-not-equal'." (typecase x (fixnum (list x (format "#x%x" x) (format "?%c" x))) (t x))) (defun ert--explain-not-equal (a b) "Explainer function for `equal'. Returns a programmer-readable explanation of why A and B are not `equal', or nil if they are." (if (not (equal (type-of a) (type-of b))) `(different-types ,a ,b) (etypecase a (cons (let ((a-proper-p (ert--proper-list-p a)) (b-proper-p (ert--proper-list-p b))) (if (not (eql (not a-proper-p) (not b-proper-p))) `(one-list-proper-one-improper ,a ,b) (if a-proper-p (if (not (equal (length a) (length b))) `(proper-lists-of-different-length ,(length a) ,(length b) ,a ,b first-mismatch-at ,(ert--mismatch a b)) (loop for i from 0 for ai in a for bi in b for xi = (ert--explain-not-equal ai bi) do (when xi (return `(list-elt ,i ,xi))) finally (assert (equal a b) t))) (let ((car-x (ert--explain-not-equal (car a) (car b)))) (if car-x `(car ,car-x) (let ((cdr-x (ert--explain-not-equal (cdr a) (cdr b)))) (if cdr-x `(cdr ,cdr-x) (assert (equal a b) t) nil)))))))) (array (if (not (equal (length a) (length b))) `(arrays-of-different-length ,(length a) ,(length b) ,a ,b ,@(unless (char-table-p a) `(first-mismatch-at ,(ert--mismatch a b)))) (loop for i from 0 for ai across a for bi across b for xi = (ert--explain-not-equal ai bi) do (when xi (return `(array-elt ,i ,xi))) finally (assert (equal a b) t)))) (atom (if (not (equal a b)) (if (and (symbolp a) (symbolp b) (string= a b)) `(different-symbols-with-the-same-name ,a ,b) `(different-atoms ,(ert--explain-format-atom a) ,(ert--explain-format-atom b))) nil))))) (put 'equal 'ert-explainer 'ert--explain-not-equal) (defun ert--significant-plist-keys (plist) "Return the keys of PLIST that have non-null values, in order." (assert (zerop (mod (length plist) 2)) t) (loop for (key value . rest) on plist by #'cddr unless (or (null value) (memq key accu)) collect key into accu finally (return accu))) (defun ert--plist-difference-explanation (a b) "Return a programmer-readable explanation of why A and B are different plists. Returns nil if they are equivalent, i.e., have the same value for each key, where absent values are treated as nil. The order of key/value pairs in each list does not matter." (assert (zerop (mod (length a) 2)) t) (assert (zerop (mod (length b) 2)) t) ;; Normalizing the plists would be another way to do this but it ;; requires a total ordering on all lisp objects (since any object ;; is valid as a text property key). Perhaps defining such an ;; ordering is useful in other contexts, too, but it's a lot of ;; work, so let's punt on it for now. (let* ((keys-a (ert--significant-plist-keys a)) (keys-b (ert--significant-plist-keys b)) (keys-in-a-not-in-b (ert--set-difference-eq keys-a keys-b)) (keys-in-b-not-in-a (ert--set-difference-eq keys-b keys-a))) (flet ((explain-with-key (key) (let ((value-a (plist-get a key)) (value-b (plist-get b key))) (assert (not (equal value-a value-b)) t) `(different-properties-for-key ,key ,(ert--explain-not-equal-including-properties value-a value-b))))) (cond (keys-in-a-not-in-b (explain-with-key (first keys-in-a-not-in-b))) (keys-in-b-not-in-a (explain-with-key (first keys-in-b-not-in-a))) (t (loop for key in keys-a when (not (equal (plist-get a key) (plist-get b key))) return (explain-with-key key))))))) (defun ert--abbreviate-string (s len suffixp) "Shorten string S to at most LEN chars. If SUFFIXP is non-nil, returns a suffix of S, otherwise a prefix." (let ((n (length s))) (cond ((< n len) s) (suffixp (substring s (- n len))) (t (substring s 0 len))))) (defun ert--explain-not-equal-including-properties (a b) "Explainer function for `ert-equal-including-properties'. Returns a programmer-readable explanation of why A and B are not `ert-equal-including-properties', or nil if they are." (if (not (equal a b)) (ert--explain-not-equal a b) (assert (stringp a) t) (assert (stringp b) t) (assert (eql (length a) (length b)) t) (loop for i from 0 to (length a) for props-a = (text-properties-at i a) for props-b = (text-properties-at i b) for difference = (ert--plist-difference-explanation props-a props-b) do (when difference (return `(char ,i ,(substring-no-properties a i (1+ i)) ,difference context-before ,(ert--abbreviate-string (substring-no-properties a 0 i) 10 t) context-after ,(ert--abbreviate-string (substring-no-properties a (1+ i)) 10 nil)))) ;; TODO(ohler): Get `equal-including-properties' fixed in ;; Emacs, delete `ert-equal-including-properties', and ;; re-enable this assertion. ;;finally (assert (equal-including-properties a b) t) ))) (put 'ert-equal-including-properties 'ert-explainer 'ert--explain-not-equal-including-properties) ;;; Implementation of `ert-info'. ;; TODO(ohler): The name `info' clashes with ;; `ert--test-execution-info'. One or both should be renamed. (defvar ert--infos '() "The stack of `ert-info' infos that currently apply. Bound dynamically. This is a list of (PREFIX . MESSAGE) pairs.") (defmacro* ert-info ((message-form &key ((:prefix prefix-form) "Info: ")) &body body) "Evaluate MESSAGE-FORM and BODY, and report the message if BODY fails. To be used within ERT tests. MESSAGE-FORM should evaluate to a string that will be displayed together with the test result if the test fails. PREFIX-FORM should evaluate to a string as well and is displayed in front of the value of MESSAGE-FORM." (declare (debug ((form &rest [sexp form]) body)) (indent 1)) `(let ((ert--infos (cons (cons ,prefix-form ,message-form) ert--infos))) ,@body)) ;;; Facilities for running a single test. (defvar ert-debug-on-error nil "Non-nil means enter debugger when a test fails or terminates with an error.") ;; The data structures that represent the result of running a test. (defstruct ert-test-result (messages nil) (should-forms nil) ) (defstruct (ert-test-passed (:include ert-test-result))) (defstruct (ert-test-result-with-condition (:include ert-test-result)) (condition (assert nil)) (backtrace (assert nil)) (infos (assert nil))) (defstruct (ert-test-quit (:include ert-test-result-with-condition))) (defstruct (ert-test-failed (:include ert-test-result-with-condition))) (defstruct (ert-test-aborted-with-non-local-exit (:include ert-test-result))) (defun ert--record-backtrace () "Record the current backtrace (as a list) and return it." ;; Since the backtrace is stored in the result object, result ;; objects must only be printed with appropriate limits ;; (`print-level' and `print-length') in place. For interactive ;; use, the cost of ensuring this possibly outweighs the advantage ;; of storing the backtrace for ;; `ert-results-pop-to-backtrace-for-test-at-point' given that we ;; already have `ert-results-rerun-test-debugging-errors-at-point'. ;; For batch use, however, printing the backtrace may be useful. (loop ;; 6 is the number of frames our own debugger adds (when ;; compiled; more when interpreted). FIXME: Need to describe a ;; procedure for determining this constant. for i from 6 for frame = (backtrace-frame i) while frame collect frame)) (defun ert--print-backtrace (backtrace) "Format the backtrace BACKTRACE to the current buffer." ;; This is essentially a reimplementation of Fbacktrace ;; (src/eval.c), but for a saved backtrace, not the current one. (let ((print-escape-newlines t) (print-level 8) (print-length 50)) (dolist (frame backtrace) (ecase (first frame) ((nil) ;; Special operator. (destructuring-bind (special-operator &rest arg-forms) (cdr frame) (insert (format " %S\n" (list* special-operator arg-forms))))) ((t) ;; Function call. (destructuring-bind (fn &rest args) (cdr frame) (insert (format " %S(" fn)) (loop for firstp = t then nil for arg in args do (unless firstp (insert " ")) (insert (format "%S" arg))) (insert ")\n"))))))) ;; A container for the state of the execution of a single test and ;; environment data needed during its execution. (defstruct ert--test-execution-info (test (assert nil)) (result (assert nil)) ;; A thunk that may be called when RESULT has been set to its final ;; value and test execution should be terminated. Should not ;; return. (exit-continuation (assert nil)) ;; The binding of `debugger' outside of the execution of the test. next-debugger ;; The binding of `ert-debug-on-error' that is in effect for the ;; execution of the current test. We store it to avoid being ;; affected by any new bindings the test itself may establish. (I ;; don't remember whether this feature is important.) ert-debug-on-error) (defun ert--run-test-debugger (info debugger-args) "During a test run, `debugger' is bound to a closure that calls this function. This function records failures and errors and either terminates the test silently or calls the interactive debugger, as appropriate. INFO is the ert--test-execution-info corresponding to this test run. DEBUGGER-ARGS are the arguments to `debugger'." (destructuring-bind (first-debugger-arg &rest more-debugger-args) debugger-args (ecase first-debugger-arg ((lambda debug t exit nil) (apply (ert--test-execution-info-next-debugger info) debugger-args)) (error (let* ((condition (first more-debugger-args)) (type (case (car condition) ((quit) 'quit) (otherwise 'failed))) (backtrace (ert--record-backtrace)) (infos (reverse ert--infos))) (setf (ert--test-execution-info-result info) (ecase type (quit (make-ert-test-quit :condition condition :backtrace backtrace :infos infos)) (failed (make-ert-test-failed :condition condition :backtrace backtrace :infos infos)))) ;; Work around Emacs' heuristic (in eval.c) for detecting ;; errors in the debugger. (incf num-nonmacro-input-events) ;; FIXME: We should probably implement more fine-grained ;; control a la non-t `debug-on-error' here. (cond ((ert--test-execution-info-ert-debug-on-error info) (apply (ert--test-execution-info-next-debugger info) debugger-args)) (t)) (funcall (ert--test-execution-info-exit-continuation info))))))) (defun ert--run-test-internal (ert-test-execution-info) "Low-level function to run a test according to ERT-TEST-EXECUTION-INFO. This mainly sets up debugger-related bindings." (lexical-let ((info ert-test-execution-info)) (setf (ert--test-execution-info-next-debugger info) debugger (ert--test-execution-info-ert-debug-on-error info) ert-debug-on-error) (catch 'ert--pass ;; For now, each test gets its own temp buffer and its own ;; window excursion, just to be safe. If this turns out to be ;; too expensive, we can remove it. (with-temp-buffer (save-window-excursion (let ((debugger (lambda (&rest debugger-args) (ert--run-test-debugger info debugger-args))) (debug-on-error t) (debug-on-quit t) ;; FIXME: Do we need to store the old binding of this ;; and consider it in `ert--run-test-debugger'? (debug-ignored-errors nil) (ert--infos '())) (funcall (ert-test-body (ert--test-execution-info-test info)))))) (ert-pass)) (setf (ert--test-execution-info-result info) (make-ert-test-passed))) nil) (defun ert--force-message-log-buffer-truncation () "Immediately truncate *Messages* buffer according to `message-log-max'. This can be useful after reducing the value of `message-log-max'." (with-current-buffer (get-buffer-create "*Messages*") ;; This is a reimplementation of this part of message_dolog() in xdisp.c: ;; if (NATNUMP (Vmessage_log_max)) ;; { ;; scan_newline (Z, Z_BYTE, BEG, BEG_BYTE, ;; -XFASTINT (Vmessage_log_max) - 1, 0); ;; del_range_both (BEG, BEG_BYTE, PT, PT_BYTE, 0); ;; } (when (and (integerp message-log-max) (>= message-log-max 0)) (let ((begin (point-min)) (end (save-excursion (goto-char (point-max)) (forward-line (- message-log-max)) (point)))) (delete-region begin end))))) (defvar ert--running-tests nil "List of tests that are currently in execution. This list is empty while no test is running, has one element while a test is running, two elements while a test run from inside a test is running, etc. The list is in order of nesting, innermost test first. The elements are of type `ert-test'.") (defun ert-run-test (ert-test) "Run ERT-TEST. Returns the result and stores it in ERT-TEST's `most-recent-result' slot." (setf (ert-test-most-recent-result ert-test) nil) (block error (lexical-let ((begin-marker (with-current-buffer (get-buffer-create "*Messages*") (set-marker (make-marker) (point-max))))) (unwind-protect (lexical-let ((info (make-ert--test-execution-info :test ert-test :result (make-ert-test-aborted-with-non-local-exit) :exit-continuation (lambda () (return-from error nil)))) (should-form-accu (list))) (unwind-protect (let ((ert--should-execution-observer (lambda (form-description) (push form-description should-form-accu))) (message-log-max t) (ert--running-tests (cons ert-test ert--running-tests))) (ert--run-test-internal info)) (let ((result (ert--test-execution-info-result info))) (setf (ert-test-result-messages result) (with-current-buffer (get-buffer-create "*Messages*") (buffer-substring begin-marker (point-max)))) (ert--force-message-log-buffer-truncation) (setq should-form-accu (nreverse should-form-accu)) (setf (ert-test-result-should-forms result) should-form-accu) (setf (ert-test-most-recent-result ert-test) result)))) (set-marker begin-marker nil)))) (ert-test-most-recent-result ert-test)) (defun ert-running-test () "Return the top-level test currently executing." (car (last ert--running-tests))) ;;; Test selectors. (defun ert-test-result-type-p (result result-type) "Return non-nil if RESULT matches type RESULT-TYPE. Valid result types: nil -- Never matches. t -- Always matches. :failed, :passed -- Matches corresponding results. \(and TYPES...\) -- Matches if all TYPES match. \(or TYPES...\) -- Matches if some TYPES match. \(not TYPE\) -- Matches if TYPE does not match. \(satisfies PREDICATE\) -- Matches if PREDICATE returns true when called with RESULT." ;; It would be easy to add `member' and `eql' types etc., but I ;; haven't bothered yet. (etypecase result-type ((member nil) nil) ((member t) t) ((member :failed) (ert-test-failed-p result)) ((member :passed) (ert-test-passed-p result)) (cons (destructuring-bind (operator &rest operands) result-type (ecase operator (and (case (length operands) (0 t) (t (and (ert-test-result-type-p result (first operands)) (ert-test-result-type-p result `(and ,@(rest operands))))))) (or (case (length operands) (0 nil) (t (or (ert-test-result-type-p result (first operands)) (ert-test-result-type-p result `(or ,@(rest operands))))))) (not (assert (eql (length operands) 1)) (not (ert-test-result-type-p result (first operands)))) (satisfies (assert (eql (length operands) 1)) (funcall (first operands) result))))))) (defun ert-test-result-expected-p (test result) "Return non-nil if TEST's expected result type matches RESULT." (ert-test-result-type-p result (ert-test-expected-result-type test))) (defun ert-select-tests (selector universe) "Return the tests that match SELECTOR. UNIVERSE specifies the set of tests to select from; it should be a list of tests, or t, which refers to all tests named by symbols in `obarray'. Returns the set of tests as a list. Valid selectors: nil -- Selects the empty set. t -- Selects UNIVERSE. :new -- Selects all tests that have not been run yet. :failed, :passed -- Select tests according to their most recent result. :expected, :unexpected -- Select tests according to their most recent result. a string -- Selects all tests that have a name that matches the string, a regexp. a test -- Selects that test. a symbol -- Selects the test that the symbol names, errors if none. \(member TESTS...\) -- Selects TESTS, a list of tests or symbols naming tests. \(eql TEST\) -- Selects TEST, a test or a symbol naming a test. \(and SELECTORS...\) -- Selects the tests that match all SELECTORS. \(or SELECTORS...\) -- Selects the tests that match any SELECTOR. \(not SELECTOR\) -- Selects all tests that do not match SELECTOR. \(tag TAG) -- Selects all tests that have TAG on their tags list. \(satisfies PREDICATE\) -- Selects all tests that satisfy PREDICATE. Only selectors that require a superset of tests, such as (satisfies ...), strings, :new, etc. make use of UNIVERSE. Selectors that do not, such as \(member ...\), just return the set implied by them without checking whether it is really contained in UNIVERSE." ;; This code needs to match the etypecase in ;; `ert-insert-human-readable-selector'. (etypecase selector ((member nil) nil) ((member t) (etypecase universe (list universe) ((member t) (ert-select-tests "" universe)))) ((member :new) (ert-select-tests `(satisfies ,(lambda (test) (null (ert-test-most-recent-result test)))) universe)) ((member :failed) (ert-select-tests `(satisfies ,(lambda (test) (ert-test-result-type-p (ert-test-most-recent-result test) ':failed))) universe)) ((member :passed) (ert-select-tests `(satisfies ,(lambda (test) (ert-test-result-type-p (ert-test-most-recent-result test) ':passed))) universe)) ((member :expected) (ert-select-tests `(satisfies ,(lambda (test) (ert-test-result-expected-p test (ert-test-most-recent-result test)))) universe)) ((member :unexpected) (ert-select-tests `(not :expected) universe)) (string (etypecase universe ((member t) (mapcar #'ert-get-test (apropos-internal selector #'ert-test-boundp))) (list (ert--remove-if-not (lambda (test) (and (ert-test-name test) (string-match selector (ert-test-name test)))) universe)))) (ert-test (list selector)) (symbol (assert (ert-test-boundp selector)) (list (ert-get-test selector))) (cons (destructuring-bind (operator &rest operands) selector (ecase operator (member (mapcar (lambda (purported-test) (etypecase purported-test (symbol (assert (ert-test-boundp purported-test)) (ert-get-test purported-test)) (ert-test purported-test))) operands)) (eql (assert (eql (length operands) 1)) (ert-select-tests `(member ,@operands) universe)) (and ;; Do these definitions of AND, NOT and OR satisfy de ;; Morgan's laws? Should they? (case (length operands) (0 (ert-select-tests 't universe)) (t (ert-select-tests `(and ,@(rest operands)) (ert-select-tests (first operands) universe))))) (not (assert (eql (length operands) 1)) (let ((all-tests (ert-select-tests 't universe))) (ert--set-difference all-tests (ert-select-tests (first operands) all-tests)))) (or (case (length operands) (0 (ert-select-tests 'nil universe)) (t (ert--union (ert-select-tests (first operands) universe) (ert-select-tests `(or ,@(rest operands)) universe))))) (tag (assert (eql (length operands) 1)) (let ((tag (first operands))) (ert-select-tests `(satisfies ,(lambda (test) (member tag (ert-test-tags test)))) universe))) (satisfies (assert (eql (length operands) 1)) (ert--remove-if-not (first operands) (ert-select-tests 't universe)))))))) (defun ert--insert-human-readable-selector (selector) "Insert a human-readable presentation of SELECTOR into the current buffer." ;; This is needed to avoid printing the (huge) contents of the ;; `backtrace' slot of the result objects in the ;; `most-recent-result' slots of test case objects in (eql ...) or ;; (member ...) selectors. (labels ((rec (selector) ;; This code needs to match the etypecase in `ert-select-tests'. (etypecase selector ((or (member nil t :new :failed :passed :expected :unexpected) string symbol) selector) (ert-test (if (ert-test-name selector) (make-symbol (format "<%S>" (ert-test-name selector))) (make-symbol ""))) (cons (destructuring-bind (operator &rest operands) selector (ecase operator ((member eql and not or) `(,operator ,@(mapcar #'rec operands))) ((member tag satisfies) selector))))))) (insert (format "%S" (rec selector))))) ;;; Facilities for running a whole set of tests. ;; The data structure that contains the set of tests being executed ;; during one particular test run, their results, the state of the ;; execution, and some statistics. ;; ;; The data about results and expected results of tests may seem ;; redundant here, since the test objects also carry such information. ;; However, the information in the test objects may be more recent, it ;; may correspond to a different test run. We need the information ;; that corresponds to this run in order to be able to update the ;; statistics correctly when a test is re-run interactively and has a ;; different result than before. (defstruct ert--stats (selector (assert nil)) ;; The tests, in order. (tests (assert nil) :type vector) ;; A map of test names (or the test objects themselves for unnamed ;; tests) to indices into the `tests' vector. (test-map (assert nil) :type hash-table) ;; The results of the tests during this run, in order. (test-results (assert nil) :type vector) ;; The start times of the tests, in order, as reported by ;; `current-time'. (test-start-times (assert nil) :type vector) ;; The end times of the tests, in order, as reported by ;; `current-time'. (test-end-times (assert nil) :type vector) (passed-expected 0) (passed-unexpected 0) (failed-expected 0) (failed-unexpected 0) (start-time nil) (end-time nil) (aborted-p nil) (current-test nil) ;; The time at or after which the next redisplay should occur, as a ;; float. (next-redisplay 0.0)) (defun ert-stats-completed-expected (stats) "Return the number of tests in STATS that had expected results." (+ (ert--stats-passed-expected stats) (ert--stats-failed-expected stats))) (defun ert-stats-completed-unexpected (stats) "Return the number of tests in STATS that had unexpected results." (+ (ert--stats-passed-unexpected stats) (ert--stats-failed-unexpected stats))) (defun ert-stats-completed (stats) "Number of tests in STATS that have run so far." (+ (ert-stats-completed-expected stats) (ert-stats-completed-unexpected stats))) (defun ert-stats-total (stats) "Number of tests in STATS, regardless of whether they have run yet." (length (ert--stats-tests stats))) ;; The stats object of the current run, dynamically bound. This is ;; used for the mode line progress indicator. (defvar ert--current-run-stats nil) (defun ert--stats-test-key (test) "Return the key used for TEST in the test map of ert--stats objects. Returns the name of TEST if it has one, or TEST itself otherwise." (or (ert-test-name test) test)) (defun ert--stats-set-test-and-result (stats pos test result) "Change STATS by replacing the test at position POS with TEST and RESULT. Also changes the counters in STATS to match." (let* ((tests (ert--stats-tests stats)) (results (ert--stats-test-results stats)) (old-test (aref tests pos)) (map (ert--stats-test-map stats))) (flet ((update (d) (if (ert-test-result-expected-p (aref tests pos) (aref results pos)) (etypecase (aref results pos) (ert-test-passed (incf (ert--stats-passed-expected stats) d)) (ert-test-failed (incf (ert--stats-failed-expected stats) d)) (null) (ert-test-aborted-with-non-local-exit)) (etypecase (aref results pos) (ert-test-passed (incf (ert--stats-passed-unexpected stats) d)) (ert-test-failed (incf (ert--stats-failed-unexpected stats) d)) (null) (ert-test-aborted-with-non-local-exit))))) ;; Adjust counters to remove the result that is currently in stats. (update -1) ;; Put new test and result into stats. (setf (aref tests pos) test (aref results pos) result) (remhash (ert--stats-test-key old-test) map) (setf (gethash (ert--stats-test-key test) map) pos) ;; Adjust counters to match new result. (update +1) nil))) (defun ert--make-stats (tests selector) "Create a new `ert--stats' object for running TESTS. SELECTOR is the selector that was used to select TESTS." (setq tests (ert--coerce-to-vector tests)) (let ((map (make-hash-table :size (length tests)))) (loop for i from 0 for test across tests for key = (ert--stats-test-key test) do (assert (not (gethash key map))) (setf (gethash key map) i)) (make-ert--stats :selector selector :tests tests :test-map map :test-results (make-vector (length tests) nil) :test-start-times (make-vector (length tests) nil) :test-end-times (make-vector (length tests) nil)))) (defun ert-run-or-rerun-test (stats test listener) ;; checkdoc-order: nil "Run the single test TEST and record the result using STATS and LISTENER." (let ((ert--current-run-stats stats) (pos (ert--stats-test-pos stats test))) (ert--stats-set-test-and-result stats pos test nil) ;; Call listener after setting/before resetting ;; (ert--stats-current-test stats); the listener might refresh the ;; mode line display, and if the value is not set yet/any more ;; during this refresh, the mode line will flicker unnecessarily. (setf (ert--stats-current-test stats) test) (funcall listener 'test-started stats test) (setf (ert-test-most-recent-result test) nil) (setf (aref (ert--stats-test-start-times stats) pos) (current-time)) (unwind-protect (ert-run-test test) (setf (aref (ert--stats-test-end-times stats) pos) (current-time)) (let ((result (ert-test-most-recent-result test))) (ert--stats-set-test-and-result stats pos test result) (funcall listener 'test-ended stats test result)) (setf (ert--stats-current-test stats) nil)))) (defun ert-run-tests (selector listener) "Run the tests specified by SELECTOR, sending progress updates to LISTENER." (let* ((tests (ert-select-tests selector t)) (stats (ert--make-stats tests selector))) (setf (ert--stats-start-time stats) (current-time)) (funcall listener 'run-started stats) (let ((abortedp t)) (unwind-protect (let ((ert--current-run-stats stats)) (force-mode-line-update) (unwind-protect (progn (loop for test in tests do (ert-run-or-rerun-test stats test listener)) (setq abortedp nil)) (setf (ert--stats-aborted-p stats) abortedp) (setf (ert--stats-end-time stats) (current-time)) (funcall listener 'run-ended stats abortedp))) (force-mode-line-update)) stats))) (defun ert--stats-test-pos (stats test) ;; checkdoc-order: nil "Return the position (index) of TEST in the run represented by STATS." (gethash (ert--stats-test-key test) (ert--stats-test-map stats))) ;;; Formatting functions shared across UIs. (defun ert--format-time-iso8601 (time) "Format TIME in the variant of ISO 8601 used for timestamps in ERT." (format-time-string "%Y-%m-%d %T%z" time)) (defun ert-char-for-test-result (result expectedp) "Return a character that represents the test result RESULT. EXPECTEDP specifies whether the result was expected." (let ((s (etypecase result (ert-test-passed ".P") (ert-test-failed "fF") (null "--") (ert-test-aborted-with-non-local-exit "aA")))) (elt s (if expectedp 0 1)))) (defun ert-string-for-test-result (result expectedp) "Return a string that represents the test result RESULT. EXPECTEDP specifies whether the result was expected." (let ((s (etypecase result (ert-test-passed '("passed" "PASSED")) (ert-test-failed '("failed" "FAILED")) (null '("unknown" "UNKNOWN")) (ert-test-aborted-with-non-local-exit '("aborted" "ABORTED"))))) (elt s (if expectedp 0 1)))) (defun ert--pp-with-indentation-and-newline (object) "Pretty-print OBJECT, indenting it to the current column of point. Ensures a final newline is inserted." (let ((begin (point))) (pp object (current-buffer)) (unless (bolp) (insert "\n")) (save-excursion (goto-char begin) (indent-sexp)))) (defun ert--insert-infos (result) "Insert `ert-info' infos from RESULT into current buffer. RESULT must be an `ert-test-result-with-condition'." (check-type result ert-test-result-with-condition) (dolist (info (ert-test-result-with-condition-infos result)) (destructuring-bind (prefix . message) info (let ((begin (point)) (indentation (make-string (+ (length prefix) 4) ?\s)) (end nil)) (unwind-protect (progn (insert message "\n") (setq end (copy-marker (point))) (goto-char begin) (insert " " prefix) (forward-line 1) (while (< (point) end) (insert indentation) (forward-line 1))) (when end (set-marker end nil))))))) ;;; Running tests in batch mode. (defvar ert-batch-backtrace-right-margin 70 "*The maximum line length for printing backtraces in `ert-run-tests-batch'.") ;;;###autoload (defun ert-run-tests-batch (&optional selector) "Run the tests specified by SELECTOR, printing results to the terminal. SELECTOR works as described in `ert-select-tests', except if SELECTOR is nil, in which case all tests rather than none will be run; this makes the command line \"emacs -batch -l my-tests.el -f ert-run-tests-batch-and-exit\" useful. Returns the stats object." (unless selector (setq selector 't)) (ert-run-tests selector (lambda (event-type &rest event-args) (ecase event-type (run-started (destructuring-bind (stats) event-args (message "Running %s tests (%s)" (length (ert--stats-tests stats)) (ert--format-time-iso8601 (ert--stats-start-time stats))))) (run-ended (destructuring-bind (stats abortedp) event-args (let ((unexpected (ert-stats-completed-unexpected stats)) (expected-failures (ert--stats-failed-expected stats))) (message "\n%sRan %s tests, %s results as expected%s (%s)%s\n" (if (not abortedp) "" "Aborted: ") (ert-stats-total stats) (ert-stats-completed-expected stats) (if (zerop unexpected) "" (format ", %s unexpected" unexpected)) (ert--format-time-iso8601 (ert--stats-end-time stats)) (if (zerop expected-failures) "" (format "\n%s expected failures" expected-failures))) (unless (zerop unexpected) (message "%s unexpected results:" unexpected) (loop for test across (ert--stats-tests stats) for result = (ert-test-most-recent-result test) do (when (not (ert-test-result-expected-p test result)) (message "%9s %S" (ert-string-for-test-result result nil) (ert-test-name test)))) (message "%s" ""))))) (test-started ) (test-ended (destructuring-bind (stats test result) event-args (unless (ert-test-result-expected-p test result) (etypecase result (ert-test-passed (message "Test %S passed unexpectedly" (ert-test-name test))) (ert-test-result-with-condition (message "Test %S backtrace:" (ert-test-name test)) (with-temp-buffer (ert--print-backtrace (ert-test-result-with-condition-backtrace result)) (goto-char (point-min)) (while (not (eobp)) (let ((start (point)) (end (progn (end-of-line) (point)))) (setq end (min end (+ start ert-batch-backtrace-right-margin))) (message "%s" (buffer-substring-no-properties start end))) (forward-line 1))) (with-temp-buffer (ert--insert-infos result) (insert " ") (let ((print-escape-newlines t) (print-level 5) (print-length 10)) (let ((begin (point))) (ert--pp-with-indentation-and-newline (ert-test-result-with-condition-condition result)))) (goto-char (1- (point-max))) (assert (looking-at "\n")) (delete-char 1) (message "Test %S condition:" (ert-test-name test)) (message "%s" (buffer-string)))) (ert-test-aborted-with-non-local-exit (message "Test %S aborted with non-local exit" (ert-test-name test))))) (let* ((max (prin1-to-string (length (ert--stats-tests stats)))) (format-string (concat "%9s %" (prin1-to-string (length max)) "s/" max " %S"))) (message format-string (ert-string-for-test-result result (ert-test-result-expected-p test result)) (1+ (ert--stats-test-pos stats test)) (ert-test-name test))))))))) ;;;###autoload (defun ert-run-tests-batch-and-exit (&optional selector) "Like `ert-run-tests-batch', but exits Emacs when done. The exit status will be 0 if all test results were as expected, 1 on unexpected results, or 2 if the framework detected an error outside of the tests (e.g. invalid SELECTOR or bug in the code that runs the tests)." (unwind-protect (let ((stats (ert-run-tests-batch selector))) (kill-emacs (if (zerop (ert-stats-completed-unexpected stats)) 0 1))) (unwind-protect (progn (message "Error running tests") (backtrace)) (kill-emacs 2)))) ;;; Utility functions for load/unload actions. (defun ert--activate-font-lock-keywords () "Activate font-lock keywords for some of ERT's symbols." (font-lock-add-keywords nil '(("(\\(\\\\s *\\(\\sw+\\)?" (1 font-lock-keyword-face nil t) (2 font-lock-function-name-face nil t))))) (defun* ert--remove-from-list (list-var element &key key test) "Remove ELEMENT from the value of LIST-VAR if present. This can be used as an inverse of `add-to-list'." (unless key (setq key #'identity)) (unless test (setq test #'equal)) (setf (symbol-value list-var) (ert--remove* element (symbol-value list-var) :key key :test test))) ;;; Some basic interactive functions. (defun ert-read-test-name (prompt &optional default history add-default-to-prompt) "Read the name of a test and return it as a symbol. Prompt with PROMPT. If DEFAULT is a valid test name, use it as a default. HISTORY is the history to use; see `completing-read'. If ADD-DEFAULT-TO-PROMPT is non-nil, PROMPT will be modified to include the default, if any. Signals an error if no test name was read." (etypecase default (string (let ((symbol (intern-soft default))) (unless (and symbol (ert-test-boundp symbol)) (setq default nil)))) (symbol (setq default (if (ert-test-boundp default) (symbol-name default) nil))) (ert-test (setq default (ert-test-name default)))) (when add-default-to-prompt (setq prompt (if (null default) (format "%s: " prompt) (format "%s (default %s): " prompt default)))) (let ((input (completing-read prompt obarray #'ert-test-boundp t nil history default nil))) ;; completing-read returns an empty string if default was nil and ;; the user just hit enter. (let ((sym (intern-soft input))) (if (ert-test-boundp sym) sym (error "Input does not name a test"))))) (defun ert-read-test-name-at-point (prompt) "Read the name of a test and return it as a symbol. As a default, use the symbol at point, or the test at point if in the ERT results buffer. Prompt with PROMPT, augmented with the default (if any)." (ert-read-test-name prompt (ert-test-at-point) nil t)) (defun ert-find-test-other-window (test-name) "Find, in another window, the definition of TEST-NAME." (interactive (list (ert-read-test-name-at-point "Find test definition: "))) (find-function-do-it test-name 'ert-deftest 'switch-to-buffer-other-window)) (defun ert-delete-test (test-name) "Make the test TEST-NAME unbound. Nothing more than an interactive interface to `ert-make-test-unbound'." (interactive (list (ert-read-test-name-at-point "Delete test"))) (ert-make-test-unbound test-name)) (defun ert-delete-all-tests () "Make all symbols in `obarray' name no test." (interactive) (when (interactive-p) (unless (y-or-n-p "Delete all tests? ") (error "Aborted"))) ;; We can't use `ert-select-tests' here since that gives us only ;; test objects, and going from them back to the test name symbols ;; can fail if the `ert-test' defstruct has been redefined. (mapc #'ert-make-test-unbound (apropos-internal "" #'ert-test-boundp)) t) ;;; Display of test progress and results. ;; An entry in the results buffer ewoc. There is one entry per test. (defstruct ert--ewoc-entry (test (assert nil)) ;; If the result of this test was expected, its ewoc entry is hidden ;; initially. (hidden-p (assert nil)) ;; An ewoc entry may be collapsed to hide details such as the error ;; condition. ;; ;; I'm not sure the ability to expand and collapse entries is still ;; a useful feature. (expanded-p t) ;; By default, the ewoc entry presents the error condition with ;; certain limits on how much to print (`print-level', ;; `print-length'). The user can interactively switch to a set of ;; higher limits. (extended-printer-limits-p nil)) ;; Variables local to the results buffer. ;; The ewoc. (defvar ert--results-ewoc) ;; The stats object. (defvar ert--results-stats) ;; A string with one character per test. Each character represents ;; the result of the corresponding test. The string is displayed near ;; the top of the buffer and serves as a progress bar. (defvar ert--results-progress-bar-string) ;; The position where the progress bar button begins. (defvar ert--results-progress-bar-button-begin) ;; The test result listener that updates the buffer when tests are run. (defvar ert--results-listener) (defun ert-insert-test-name-button (test-name) "Insert a button that links to TEST-NAME." (insert-text-button (format "%S" test-name) :type 'ert--test-name-button 'ert-test-name test-name)) (defun ert--results-format-expected-unexpected (expected unexpected) "Return a string indicating EXPECTED expected results, UNEXPECTED unexpected." (if (zerop unexpected) (format "%s" expected) (format "%s (%s unexpected)" (+ expected unexpected) unexpected))) (defun ert--results-update-ewoc-hf (ewoc stats) "Update the header and footer of EWOC to show certain information from STATS. Also sets `ert--results-progress-bar-button-begin'." (let ((run-count (ert-stats-completed stats)) (results-buffer (current-buffer)) ;; Need to save buffer-local value. (font-lock font-lock-mode)) (ewoc-set-hf ewoc ;; header (with-temp-buffer (insert "Selector: ") (ert--insert-human-readable-selector (ert--stats-selector stats)) (insert "\n") (insert (format (concat "Passed: %s\n" "Failed: %s\n" "Total: %s/%s\n\n") (ert--results-format-expected-unexpected (ert--stats-passed-expected stats) (ert--stats-passed-unexpected stats)) (ert--results-format-expected-unexpected (ert--stats-failed-expected stats) (ert--stats-failed-unexpected stats)) run-count (ert-stats-total stats))) (insert (format "Started at: %s\n" (ert--format-time-iso8601 (ert--stats-start-time stats)))) ;; FIXME: This is ugly. Need to properly define invariants of ;; the `stats' data structure. (let ((state (cond ((ert--stats-aborted-p stats) 'aborted) ((ert--stats-current-test stats) 'running) ((ert--stats-end-time stats) 'finished) (t 'preparing)))) (ecase state (preparing (insert "")) (aborted (cond ((ert--stats-current-test stats) (insert "Aborted during test: ") (ert-insert-test-name-button (ert-test-name (ert--stats-current-test stats)))) (t (insert "Aborted.")))) (running (assert (ert--stats-current-test stats)) (insert "Running test: ") (ert-insert-test-name-button (ert-test-name (ert--stats-current-test stats)))) (finished (assert (not (ert--stats-current-test stats))) (insert "Finished."))) (insert "\n") (if (ert--stats-end-time stats) (insert (format "%s%s\n" (if (ert--stats-aborted-p stats) "Aborted at: " "Finished at: ") (ert--format-time-iso8601 (ert--stats-end-time stats)))) (insert "\n")) (insert "\n")) (let ((progress-bar-string (with-current-buffer results-buffer ert--results-progress-bar-string))) (let ((progress-bar-button-begin (insert-text-button progress-bar-string :type 'ert--results-progress-bar-button 'face (or (and font-lock (ert-face-for-stats stats)) 'button)))) ;; The header gets copied verbatim to the results buffer, ;; and all positions remain the same, so ;; `progress-bar-button-begin' will be the right position ;; even in the results buffer. (with-current-buffer results-buffer (set (make-local-variable 'ert--results-progress-bar-button-begin) progress-bar-button-begin)))) (insert "\n\n") (buffer-string)) ;; footer ;; ;; We actually want an empty footer, but that would trigger a bug ;; in ewoc, sometimes clearing the entire buffer. (It's possible ;; that this bug has been fixed since this has been tested; we ;; should test it again.) "\n"))) (defvar ert-test-run-redisplay-interval-secs .1 "How many seconds ERT should wait between redisplays while running tests. While running tests, ERT shows the current progress, and this variable determines how frequently the progress display is updated.") (defun ert--results-update-stats-display (ewoc stats) "Update EWOC and the mode line to show data from STATS." ;; TODO(ohler): investigate using `make-progress-reporter'. (ert--results-update-ewoc-hf ewoc stats) (force-mode-line-update) (redisplay t) (setf (ert--stats-next-redisplay stats) (+ (float-time) ert-test-run-redisplay-interval-secs))) (defun ert--results-update-stats-display-maybe (ewoc stats) "Call `ert--results-update-stats-display' if not called recently. EWOC and STATS are arguments for `ert--results-update-stats-display'." (when (>= (float-time) (ert--stats-next-redisplay stats)) (ert--results-update-stats-display ewoc stats))) (defun ert--tests-running-mode-line-indicator () "Return a string for the mode line that shows the test run progress." (let* ((stats ert--current-run-stats) (tests-total (ert-stats-total stats)) (tests-completed (ert-stats-completed stats))) (if (>= tests-completed tests-total) (format " ERT(%s/%s,finished)" tests-completed tests-total) (format " ERT(%s/%s):%s" (1+ tests-completed) tests-total (if (null (ert--stats-current-test stats)) "?" (format "%S" (ert-test-name (ert--stats-current-test stats)))))))) (defun ert--make-xrefs-region (begin end) "Attach cross-references to function names between BEGIN and END. BEGIN and END specify a region in the current buffer." (save-excursion (save-restriction (narrow-to-region begin (point)) ;; Inhibit optimization in `debugger-make-xrefs' that would ;; sometimes insert unrelated backtrace info into our buffer. (let ((debugger-previous-backtrace nil)) (debugger-make-xrefs))))) (defun ert--string-first-line (s) "Return the first line of S, or S if it contains no newlines. The return value does not include the line terminator." (substring s 0 (ert--string-position ?\n s))) (defun ert-face-for-test-result (expectedp) "Return a face that shows whether a test result was expected or unexpected. If EXPECTEDP is nil, returns the face for unexpected results; if non-nil, returns the face for expected results.." (if expectedp 'ert-test-result-expected 'ert-test-result-unexpected)) (defun ert-face-for-stats (stats) "Return a face that represents STATS." (cond ((ert--stats-aborted-p stats) 'nil) ((plusp (ert-stats-completed-unexpected stats)) (ert-face-for-test-result nil)) ((eql (ert-stats-completed-expected stats) (ert-stats-total stats)) (ert-face-for-test-result t)) (t 'nil))) (defun ert--print-test-for-ewoc (entry) "The ewoc print function for ewoc test entries. ENTRY is the entry to print." (let* ((test (ert--ewoc-entry-test entry)) (stats ert--results-stats) (result (let ((pos (ert--stats-test-pos stats test))) (assert pos) (aref (ert--stats-test-results stats) pos))) (hiddenp (ert--ewoc-entry-hidden-p entry)) (expandedp (ert--ewoc-entry-expanded-p entry)) (extended-printer-limits-p (ert--ewoc-entry-extended-printer-limits-p entry))) (cond (hiddenp) (t (let ((expectedp (ert-test-result-expected-p test result))) (insert-text-button (format "%c" (ert-char-for-test-result result expectedp)) :type 'ert--results-expand-collapse-button 'face (or (and font-lock-mode (ert-face-for-test-result expectedp)) 'button))) (insert " ") (ert-insert-test-name-button (ert-test-name test)) (insert "\n") (when (and expandedp (not (eql result 'nil))) (when (ert-test-documentation test) (insert " " (propertize (ert--string-first-line (ert-test-documentation test)) 'font-lock-face 'font-lock-doc-face) "\n")) (etypecase result (ert-test-passed (if (ert-test-result-expected-p test result) (insert " passed\n") (insert " passed unexpectedly\n")) (insert "")) (ert-test-result-with-condition (ert--insert-infos result) (let ((print-escape-newlines t) (print-level (if extended-printer-limits-p 12 6)) (print-length (if extended-printer-limits-p 100 10))) (insert " ") (let ((begin (point))) (ert--pp-with-indentation-and-newline (ert-test-result-with-condition-condition result)) (ert--make-xrefs-region begin (point))))) (ert-test-aborted-with-non-local-exit (insert " aborted\n"))) (insert "\n"))))) nil) (defun ert--results-font-lock-function (enabledp) "Redraw the ERT results buffer after font-lock-mode was switched on or off. ENABLEDP is true if font-lock-mode is switched on, false otherwise." (ert--results-update-ewoc-hf ert--results-ewoc ert--results-stats) (ewoc-refresh ert--results-ewoc) (font-lock-default-function enabledp)) (defun ert--setup-results-buffer (stats listener buffer-name) "Set up a test results buffer. STATS is the stats object; LISTENER is the results listener; BUFFER-NAME, if non-nil, is the buffer name to use." (unless buffer-name (setq buffer-name "*ert*")) (let ((buffer (get-buffer-create buffer-name))) (with-current-buffer buffer (setq buffer-read-only t) (let ((inhibit-read-only t)) (buffer-disable-undo) (erase-buffer) (ert-results-mode) ;; Erase buffer again in case switching out of the previous ;; mode inserted anything. (This happens e.g. when switching ;; from ert-results-mode to ert-results-mode when ;; font-lock-mode turns itself off in change-major-mode-hook.) (erase-buffer) (set (make-local-variable 'font-lock-function) 'ert--results-font-lock-function) (let ((ewoc (ewoc-create 'ert--print-test-for-ewoc nil nil t))) (set (make-local-variable 'ert--results-ewoc) ewoc) (set (make-local-variable 'ert--results-stats) stats) (set (make-local-variable 'ert--results-progress-bar-string) (make-string (ert-stats-total stats) (ert-char-for-test-result nil t))) (set (make-local-variable 'ert--results-listener) listener) (loop for test across (ert--stats-tests stats) do (ewoc-enter-last ewoc (make-ert--ewoc-entry :test test :hidden-p t))) (ert--results-update-ewoc-hf ert--results-ewoc ert--results-stats) (goto-char (1- (point-max))) buffer))))) (defvar ert--selector-history nil "List of recent test selectors read from terminal.") ;; Should OUTPUT-BUFFER-NAME and MESSAGE-FN really be arguments here? ;; They are needed only for our automated self-tests at the moment. ;; Or should there be some other mechanism? ;;;###autoload (defun ert-run-tests-interactively (selector &optional output-buffer-name message-fn) "Run the tests specified by SELECTOR and display the results in a buffer. SELECTOR works as described in `ert-select-tests'. OUTPUT-BUFFER-NAME and MESSAGE-FN should normally be nil; they are used for automated self-tests and specify which buffer to use and how to display message." (interactive (list (let ((default (if ert--selector-history ;; Can't use `first' here as this form is ;; not compiled, and `first' is not ;; defined without cl. (car ert--selector-history) "t"))) (read-from-minibuffer (if (null default) "Run tests: " (format "Run tests (default %s): " default)) nil nil t 'ert--selector-history default nil)) nil)) (unless message-fn (setq message-fn 'message)) (lexical-let ((output-buffer-name output-buffer-name) buffer listener (message-fn message-fn)) (setq listener (lambda (event-type &rest event-args) (ecase event-type (run-started (destructuring-bind (stats) event-args (setq buffer (ert--setup-results-buffer stats listener output-buffer-name)) (pop-to-buffer buffer))) (run-ended (destructuring-bind (stats abortedp) event-args (funcall message-fn "%sRan %s tests, %s results were as expected%s" (if (not abortedp) "" "Aborted: ") (ert-stats-total stats) (ert-stats-completed-expected stats) (let ((unexpected (ert-stats-completed-unexpected stats))) (if (zerop unexpected) "" (format ", %s unexpected" unexpected)))) (ert--results-update-stats-display (with-current-buffer buffer ert--results-ewoc) stats))) (test-started (destructuring-bind (stats test) event-args (with-current-buffer buffer (let* ((ewoc ert--results-ewoc) (pos (ert--stats-test-pos stats test)) (node (ewoc-nth ewoc pos))) (assert node) (setf (ert--ewoc-entry-test (ewoc-data node)) test) (aset ert--results-progress-bar-string pos (ert-char-for-test-result nil t)) (ert--results-update-stats-display-maybe ewoc stats) (ewoc-invalidate ewoc node))))) (test-ended (destructuring-bind (stats test result) event-args (with-current-buffer buffer (let* ((ewoc ert--results-ewoc) (pos (ert--stats-test-pos stats test)) (node (ewoc-nth ewoc pos))) (when (ert--ewoc-entry-hidden-p (ewoc-data node)) (setf (ert--ewoc-entry-hidden-p (ewoc-data node)) (ert-test-result-expected-p test result))) (aset ert--results-progress-bar-string pos (ert-char-for-test-result result (ert-test-result-expected-p test result))) (ert--results-update-stats-display-maybe ewoc stats) (ewoc-invalidate ewoc node)))))))) (ert-run-tests selector listener))) ;;;###autoload (defalias 'ert 'ert-run-tests-interactively) ;;; Simple view mode for auxiliary information like stack traces or ;;; messages. Mainly binds "q" for quit. (define-derived-mode ert-simple-view-mode fundamental-mode "ERT-View" "Major mode for viewing auxiliary information in ERT.") (loop for (key binding) in '(("q" quit-window) ) do (define-key ert-simple-view-mode-map key binding)) ;;; Commands and button actions for the results buffer. (define-derived-mode ert-results-mode fundamental-mode "ERT-Results" "Major mode for viewing results of ERT test runs.") (loop for (key binding) in '(;; Stuff that's not in the menu. ("\t" forward-button) ([backtab] backward-button) ("j" ert-results-jump-between-summary-and-result) ("q" quit-window) ("L" ert-results-toggle-printer-limits-for-test-at-point) ("n" ert-results-next-test) ("p" ert-results-previous-test) ;; Stuff that is in the menu. ("R" ert-results-rerun-all-tests) ("r" ert-results-rerun-test-at-point) ("d" ert-results-rerun-test-at-point-debugging-errors) ("." ert-results-find-test-at-point-other-window) ("b" ert-results-pop-to-backtrace-for-test-at-point) ("m" ert-results-pop-to-messages-for-test-at-point) ("l" ert-results-pop-to-should-forms-for-test-at-point) ("h" ert-results-describe-test-at-point) ("D" ert-delete-test) ("T" ert-results-pop-to-timings) ) do (define-key ert-results-mode-map key binding)) (easy-menu-define ert-results-mode-menu ert-results-mode-map "Menu for `ert-results-mode'." '("ERT Results" ["Re-run all tests" ert-results-rerun-all-tests] "--" ["Re-run test" ert-results-rerun-test-at-point] ["Debug test" ert-results-rerun-test-at-point-debugging-errors] ["Show test definition" ert-results-find-test-at-point-other-window] "--" ["Show backtrace" ert-results-pop-to-backtrace-for-test-at-point] ["Show messages" ert-results-pop-to-messages-for-test-at-point] ["Show `should' forms" ert-results-pop-to-should-forms-for-test-at-point] ["Describe test" ert-results-describe-test-at-point] "--" ["Delete test" ert-delete-test] "--" ["Show execution time of each test" ert-results-pop-to-timings] )) (define-button-type 'ert--results-progress-bar-button 'action #'ert--results-progress-bar-button-action 'help-echo "mouse-2, RET: Reveal test result") (define-button-type 'ert--test-name-button 'action #'ert--test-name-button-action 'help-echo "mouse-2, RET: Find test definition") (define-button-type 'ert--results-expand-collapse-button 'action #'ert--results-expand-collapse-button-action 'help-echo "mouse-2, RET: Expand/collapse test result") (defun ert--results-test-node-or-null-at-point () "If point is on a valid ewoc node, return it; return nil otherwise. To be used in the ERT results buffer." (let* ((ewoc ert--results-ewoc) (node (ewoc-locate ewoc))) ;; `ewoc-locate' will return an arbitrary node when point is on ;; header or footer, or when all nodes are invisible. So we need ;; to validate its return value here. ;; ;; Update: I'm seeing nil being returned in some cases now, ;; perhaps this has been changed? (if (and node (>= (point) (ewoc-location node)) (not (ert--ewoc-entry-hidden-p (ewoc-data node)))) node nil))) (defun ert--results-test-node-at-point () "If point is on a valid ewoc node, return it; signal an error otherwise. To be used in the ERT results buffer." (or (ert--results-test-node-or-null-at-point) (error "No test at point"))) (defun ert-results-next-test () "Move point to the next test. To be used in the ERT results buffer." (interactive) (ert--results-move (ewoc-locate ert--results-ewoc) 'ewoc-next "No tests below")) (defun ert-results-previous-test () "Move point to the previous test. To be used in the ERT results buffer." (interactive) (ert--results-move (ewoc-locate ert--results-ewoc) 'ewoc-prev "No tests above")) (defun ert--results-move (node ewoc-fn error-message) "Move point from NODE to the previous or next node. EWOC-FN specifies the direction and should be either `ewoc-prev' or `ewoc-next'. If there are no more nodes in that direction, an error is signalled with the message ERROR-MESSAGE." (loop (setq node (funcall ewoc-fn ert--results-ewoc node)) (when (null node) (error "%s" error-message)) (unless (ert--ewoc-entry-hidden-p (ewoc-data node)) (goto-char (ewoc-location node)) (return)))) (defun ert--results-expand-collapse-button-action (button) "Expand or collapse the test node BUTTON belongs to." (let* ((ewoc ert--results-ewoc) (node (save-excursion (goto-char (ert--button-action-position)) (ert--results-test-node-at-point))) (entry (ewoc-data node))) (setf (ert--ewoc-entry-expanded-p entry) (not (ert--ewoc-entry-expanded-p entry))) (ewoc-invalidate ewoc node))) (defun ert-results-find-test-at-point-other-window () "Find the definition of the test at point in another window. To be used in the ERT results buffer." (interactive) (let ((name (ert-test-at-point))) (unless name (error "No test at point")) (ert-find-test-other-window name))) (defun ert--test-name-button-action (button) "Find the definition of the test BUTTON belongs to, in another window." (let ((name (button-get button 'ert-test-name))) (ert-find-test-other-window name))) (defun ert--ewoc-position (ewoc node) ;; checkdoc-order: nil "Return the position of NODE in EWOC, or nil if NODE is not in EWOC." (loop for i from 0 for node-here = (ewoc-nth ewoc 0) then (ewoc-next ewoc node-here) do (when (eql node node-here) (return i)) finally (return nil))) (defun ert-results-jump-between-summary-and-result () "Jump back and forth between the test run summary and individual test results. From an ewoc node, jumps to the character that represents the same test in the progress bar, and vice versa. To be used in the ERT results buffer." ;; Maybe this command isn't actually needed much, but if it is, it ;; seems like an indication that the UI design is not optimal. If ;; jumping back and forth between a summary at the top of the buffer ;; and the error log in the remainder of the buffer is useful, then ;; the summary apparently needs to be easily accessible from the ;; error log, and perhaps it would be better to have it in a ;; separate buffer to keep it visible. (interactive) (let ((ewoc ert--results-ewoc) (progress-bar-begin ert--results-progress-bar-button-begin)) (cond ((ert--results-test-node-or-null-at-point) (let* ((node (ert--results-test-node-at-point)) (pos (ert--ewoc-position ewoc node))) (goto-char (+ progress-bar-begin pos)))) ((and (<= progress-bar-begin (point)) (< (point) (button-end (button-at progress-bar-begin)))) (let* ((node (ewoc-nth ewoc (- (point) progress-bar-begin))) (entry (ewoc-data node))) (when (ert--ewoc-entry-hidden-p entry) (setf (ert--ewoc-entry-hidden-p entry) nil) (ewoc-invalidate ewoc node)) (ewoc-goto-node ewoc node))) (t (goto-char progress-bar-begin))))) (defun ert-test-at-point () "Return the name of the test at point as a symbol, or nil if none." (or (and (eql major-mode 'ert-results-mode) (let ((test (ert--results-test-at-point-no-redefinition))) (and test (ert-test-name test)))) (let* ((thing (thing-at-point 'symbol)) (sym (intern-soft thing))) (and (ert-test-boundp sym) sym)))) (defun ert--results-test-at-point-no-redefinition () "Return the test at point, or nil. To be used in the ERT results buffer." (assert (eql major-mode 'ert-results-mode)) (if (ert--results-test-node-or-null-at-point) (let* ((node (ert--results-test-node-at-point)) (test (ert--ewoc-entry-test (ewoc-data node)))) test) (let ((progress-bar-begin ert--results-progress-bar-button-begin)) (when (and (<= progress-bar-begin (point)) (< (point) (button-end (button-at progress-bar-begin)))) (let* ((test-index (- (point) progress-bar-begin)) (test (aref (ert--stats-tests ert--results-stats) test-index))) test))))) (defun ert--results-test-at-point-allow-redefinition () "Look up the test at point, and check whether it has been redefined. To be used in the ERT results buffer. Returns a list of two elements: the test (or nil) and a symbol specifying whether the test has been redefined. If a new test has been defined with the same name as the test at point, replaces the test at point with the new test, and returns the new test and the symbol `redefined'. If the test has been deleted, returns the old test and the symbol `deleted'. If the test is still current, returns the test and the symbol nil. If there is no test at point, returns a list with two nils." (let ((test (ert--results-test-at-point-no-redefinition))) (cond ((null test) `(nil nil)) ((null (ert-test-name test)) `(,test nil)) (t (let* ((name (ert-test-name test)) (new-test (and (ert-test-boundp name) (ert-get-test name)))) (cond ((eql test new-test) `(,test nil)) ((null new-test) `(,test deleted)) (t (ert--results-update-after-test-redefinition (ert--stats-test-pos ert--results-stats test) new-test) `(,new-test redefined)))))))) (defun ert--results-update-after-test-redefinition (pos new-test) "Update results buffer after the test at pos POS has been redefined. Also updates the stats object. NEW-TEST is the new test definition." (let* ((stats ert--results-stats) (ewoc ert--results-ewoc) (node (ewoc-nth ewoc pos)) (entry (ewoc-data node))) (ert--stats-set-test-and-result stats pos new-test nil) (setf (ert--ewoc-entry-test entry) new-test (aref ert--results-progress-bar-string pos) (ert-char-for-test-result nil t)) (ewoc-invalidate ewoc node)) nil) (defun ert--button-action-position () "The buffer position where the last button action was triggered." (cond ((integerp last-command-event) (point)) ((eventp last-command-event) (posn-point (event-start last-command-event))) (t (assert nil)))) (defun ert--results-progress-bar-button-action (button) "Jump to details for the test represented by the character clicked in BUTTON." (goto-char (ert--button-action-position)) (ert-results-jump-between-summary-and-result)) (defun ert-results-rerun-all-tests () "Re-run all tests, using the same selector. To be used in the ERT results buffer." (interactive) (assert (eql major-mode 'ert-results-mode)) (let ((selector (ert--stats-selector ert--results-stats))) (ert-run-tests-interactively selector (buffer-name)))) (defun ert-results-rerun-test-at-point () "Re-run the test at point. To be used in the ERT results buffer." (interactive) (destructuring-bind (test redefinition-state) (ert--results-test-at-point-allow-redefinition) (when (null test) (error "No test at point")) (let* ((stats ert--results-stats) (progress-message (format "Running %stest %S" (ecase redefinition-state ((nil) "") (redefined "new definition of ") (deleted "deleted ")) (ert-test-name test)))) ;; Need to save and restore point manually here: When point is on ;; the first visible ewoc entry while the header is updated, point ;; moves to the top of the buffer. This is undesirable, and a ;; simple `save-excursion' doesn't prevent it. (let ((point (point))) (unwind-protect (unwind-protect (progn (message "%s..." progress-message) (ert-run-or-rerun-test stats test ert--results-listener)) (ert--results-update-stats-display ert--results-ewoc stats) (message "%s...%s" progress-message (let ((result (ert-test-most-recent-result test))) (ert-string-for-test-result result (ert-test-result-expected-p test result))))) (goto-char point)))))) (defun ert-results-rerun-test-at-point-debugging-errors () "Re-run the test at point with `ert-debug-on-error' bound to t. To be used in the ERT results buffer." (interactive) (let ((ert-debug-on-error t)) (ert-results-rerun-test-at-point))) (defun ert-results-pop-to-backtrace-for-test-at-point () "Display the backtrace for the test at point. To be used in the ERT results buffer." (interactive) (let* ((test (ert--results-test-at-point-no-redefinition)) (stats ert--results-stats) (pos (ert--stats-test-pos stats test)) (result (aref (ert--stats-test-results stats) pos))) (etypecase result (ert-test-passed (error "Test passed, no backtrace available")) (ert-test-result-with-condition (let ((backtrace (ert-test-result-with-condition-backtrace result)) (buffer (get-buffer-create "*ERT Backtrace*"))) (pop-to-buffer buffer) (setq buffer-read-only t) (let ((inhibit-read-only t)) (buffer-disable-undo) (erase-buffer) (ert-simple-view-mode) ;; Use unibyte because `debugger-setup-buffer' also does so. (set-buffer-multibyte nil) (setq truncate-lines t) (ert--print-backtrace backtrace) (debugger-make-xrefs) (goto-char (point-min)) (insert "Backtrace for test `") (ert-insert-test-name-button (ert-test-name test)) (insert "':\n"))))))) (defun ert-results-pop-to-messages-for-test-at-point () "Display the part of the *Messages* buffer generated during the test at point. To be used in the ERT results buffer." (interactive) (let* ((test (ert--results-test-at-point-no-redefinition)) (stats ert--results-stats) (pos (ert--stats-test-pos stats test)) (result (aref (ert--stats-test-results stats) pos))) (let ((buffer (get-buffer-create "*ERT Messages*"))) (pop-to-buffer buffer) (setq buffer-read-only t) (let ((inhibit-read-only t)) (buffer-disable-undo) (erase-buffer) (ert-simple-view-mode) (insert (ert-test-result-messages result)) (goto-char (point-min)) (insert "Messages for test `") (ert-insert-test-name-button (ert-test-name test)) (insert "':\n"))))) (defun ert-results-pop-to-should-forms-for-test-at-point () "Display the list of `should' forms executed during the test at point. To be used in the ERT results buffer." (interactive) (let* ((test (ert--results-test-at-point-no-redefinition)) (stats ert--results-stats) (pos (ert--stats-test-pos stats test)) (result (aref (ert--stats-test-results stats) pos))) (let ((buffer (get-buffer-create "*ERT list of should forms*"))) (pop-to-buffer buffer) (setq buffer-read-only t) (let ((inhibit-read-only t)) (buffer-disable-undo) (erase-buffer) (ert-simple-view-mode) (if (null (ert-test-result-should-forms result)) (insert "\n(No should forms during this test.)\n") (loop for form-description in (ert-test-result-should-forms result) for i from 1 do (insert "\n") (insert (format "%s: " i)) (let ((begin (point))) (ert--pp-with-indentation-and-newline form-description) (ert--make-xrefs-region begin (point))))) (goto-char (point-min)) (insert "`should' forms executed during test `") (ert-insert-test-name-button (ert-test-name test)) (insert "':\n") (insert "\n") (insert (concat "(Values are shallow copies and may have " "looked different during the test if they\n" "have been modified destructively.)\n")) (forward-line 1))))) (defun ert-results-toggle-printer-limits-for-test-at-point () "Toggle how much of the condition to print for the test at point. To be used in the ERT results buffer." (interactive) (let* ((ewoc ert--results-ewoc) (node (ert--results-test-node-at-point)) (entry (ewoc-data node))) (setf (ert--ewoc-entry-extended-printer-limits-p entry) (not (ert--ewoc-entry-extended-printer-limits-p entry))) (ewoc-invalidate ewoc node))) (defun ert-results-pop-to-timings () "Display test timings for the last run. To be used in the ERT results buffer." (interactive) (let* ((stats ert--results-stats) (start-times (ert--stats-test-start-times stats)) (end-times (ert--stats-test-end-times stats)) (buffer (get-buffer-create "*ERT timings*")) (data (loop for test across (ert--stats-tests stats) for start-time across (ert--stats-test-start-times stats) for end-time across (ert--stats-test-end-times stats) collect (list test (float-time (subtract-time end-time start-time)))))) (setq data (sort data (lambda (a b) (> (second a) (second b))))) (pop-to-buffer buffer) (setq buffer-read-only t) (let ((inhibit-read-only t)) (buffer-disable-undo) (erase-buffer) (ert-simple-view-mode) (if (null data) (insert "(No data)\n") (insert (format "%-3s %8s %8s\n" "" "time" "cumul")) (loop for (test time) in data for cumul-time = time then (+ cumul-time time) for i from 1 do (let ((begin (point))) (insert (format "%3s: %8.3f %8.3f " i time cumul-time)) (ert-insert-test-name-button (ert-test-name test)) (insert "\n")))) (goto-char (point-min)) (insert "Tests by run time (seconds):\n\n") (forward-line 1)))) ;;;###autoload (defun ert-describe-test (test-or-test-name) "Display the documentation for TEST-OR-TEST-NAME (a symbol or ert-test)." (interactive (list (ert-read-test-name-at-point "Describe test"))) (when (< emacs-major-version 24) (error "Requires Emacs 24")) (let (test-name test-definition) (etypecase test-or-test-name (symbol (setq test-name test-or-test-name test-definition (ert-get-test test-or-test-name))) (ert-test (setq test-name (ert-test-name test-or-test-name) test-definition test-or-test-name))) (help-setup-xref (list #'ert-describe-test test-or-test-name) (called-interactively-p 'interactive)) (save-excursion (with-help-window (help-buffer) (with-current-buffer (help-buffer) (insert (if test-name (format "%S" test-name) "")) (insert " is a test") (let ((file-name (and test-name (symbol-file test-name 'ert-deftest)))) (when file-name (insert " defined in `" (file-name-nondirectory file-name) "'") (save-excursion (re-search-backward "`\\([^`']+\\)'" nil t) (help-xref-button 1 'help-function-def test-name file-name))) (insert ".") (fill-region-as-paragraph (point-min) (point)) (insert "\n\n") (unless (and (ert-test-boundp test-name) (eql (ert-get-test test-name) test-definition)) (let ((begin (point))) (insert "Note: This test has been redefined or deleted, " "this documentation refers to an old definition.") (fill-region-as-paragraph begin (point))) (insert "\n\n")) (insert (or (ert-test-documentation test-definition) "It is not documented.") "\n"))))))) (defun ert-results-describe-test-at-point () "Display the documentation of the test at point. To be used in the ERT results buffer." (interactive) (ert-describe-test (ert--results-test-at-point-no-redefinition))) ;;; Actions on load/unload. (add-to-list 'find-function-regexp-alist '(ert-deftest . ert--find-test-regexp)) (add-to-list 'minor-mode-alist '(ert--current-run-stats (:eval (ert--tests-running-mode-line-indicator)))) (add-to-list 'emacs-lisp-mode-hook 'ert--activate-font-lock-keywords) (defun ert--unload-function () "Unload function to undo the side-effects of loading ert.el." (ert--remove-from-list 'find-function-regexp-alist 'ert-deftest :key #'car) (ert--remove-from-list 'minor-mode-alist 'ert--current-run-stats :key #'car) (ert--remove-from-list 'emacs-lisp-mode-hook 'ert--activate-font-lock-keywords) nil) (defvar ert-unload-hook '()) (add-hook 'ert-unload-hook 'ert--unload-function) (provide 'ert) ;;; ert.el ends here dash.el-2.12.1/dev/examples-to-docs.el000066400000000000000000000140531261164447300174120ustar00rootroot00000000000000;;; examples-to-docs.el --- Extract dash.el's doc from examples.el ;; Copyright (C) 2015 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; FIXME: Lots of duplication with examples-to-info.el. ;;; Code: (require 'dash) (require 'dash-functional) (require 'help-fns) (defvar functions '()) (defun example-to-string (example) (-let* (((actual sym expected) example) (comment (cond ((eq sym '=>) (format "=> %S" expected)) ((eq sym '~>) (format "~> %S" expected)) ((eq sym '!!>) (format "Error")) (t (error "Invalid test case: %S" `(,actual ,sym ,expected)))))) (--> comment (format "%S ;; %s" actual it) (replace-regexp-in-string "\\\\\\?" "?" it) (replace-regexp-in-string "\n" "\\n" it t t) (replace-regexp-in-string "\t" "\\t" it t t) (replace-regexp-in-string "\r" "\\r" it t t)))) (defun docs--signature (function) "Given FUNCTION (a symbol), return its argument list. FUNCTION may reference an elisp function, alias, macro or a subr." (let* ((function-value (indirect-function function)) (is-alias (not (eq function-value (symbol-function function)))) ;; if FUNCTION isn't an alias, function-symbol is simply FUNCTION (function-symbol function)) (when is-alias ;; find the last symbol in the alias chain (while (symbolp (symbol-function function-symbol)) (setq function-symbol (symbol-function function-symbol)))) (if (subrp function-value) ;; read the docstring to find the signature for subrs (let* ((docstring-args (car (help-split-fundoc (documentation function-value) function-symbol))) (fun-with-args (read (downcase docstring-args)))) (cdr fun-with-args)) ;; otherwise get the signature directly (help-function-arglist function-symbol)))) (defmacro defexamples (cmd &rest examples) `(add-to-list 'functions (list ',cmd (docs--signature ',cmd) (documentation ',cmd) (-map 'example-to-string (-partition 3 ',examples))))) (defmacro def-example-group (group desc &rest examples) `(progn (add-to-list 'functions ,(concat "### " group)) (when ,desc (add-to-list 'functions ,desc)) ,@examples)) (defun quote-and-downcase (string) (format "`%s`" (downcase string))) (defun unquote-and-link (string) (format-link (substring string 1 -1))) (defun format-link (string-name) (-let* ((name (intern string-name)) ((_ signature _ _) (assoc name functions))) (if signature (format "[`%s`](#%s)" name (github-id name signature)) (format "`%s`" name)))) (defun format-docstring (docstring) (let (case-fold-search) (--> docstring (replace-regexp-in-string "\\b\\([A-Z][A-Z-]*[0-9]*\\)\\b" 'quote-and-downcase it t) (replace-regexp-in-string "`\\([^ ]+\\)'" 'unquote-and-link it t) (replace-regexp-in-string "^ " " " it)))) (defun function-to-md (function) (if (stringp function) (concat "\n" (s-replace "### " "## " function) "\n") (-let [(command-name signature docstring examples) function] (format "#### %s `%s`\n\n%s\n\n```el\n%s\n```\n" command-name signature (format-docstring docstring) (mapconcat 'identity (-take 3 examples) "\n"))))) (defun docs--chop-prefix (prefix s) "Remove PREFIX if it is at the start of S." (let ((pos (length prefix))) (if (and (>= (length s) (length prefix)) (string= prefix (substring s 0 pos))) (substring s pos) s))) (defun docs--chop-suffix (suffix s) "Remove SUFFIX if it is at end of S." (let ((pos (- (length suffix)))) (if (and (>= (length s) (length suffix)) (string= suffix (substring s pos))) (substring s 0 pos) s))) (defun github-id (command-name signature) (docs--chop-suffix "-" (replace-regexp-in-string "[^a-zA-Z0-9-]+" "-" (docs--chop-prefix "!" (format "%S %S" command-name signature))))) (defun s-replace (old new s) "Replace OLD with NEW in S." (replace-regexp-in-string (regexp-quote old) new s t t)) (defun function-summary (function) (if (stringp function) (concat "\n" function "\n") (let ((command-name (car function)) (signature (cadr function))) (format "* [%s](#%s) `%s`" command-name (github-id command-name signature) signature)))) (defun simplify-quotes () (goto-char (point-min)) (while (search-forward "(quote nil)" nil t) (replace-match "'()")) (goto-char (point-min)) (while (search-forward "(quote " nil t) (forward-char -7) (let ((p (point))) (forward-sexp 1) (delete-char -1) (goto-char p) (delete-char 7) (insert "'")))) (defun goto-and-remove (s) (goto-char (point-min)) (search-forward s) (delete-char (- (length s)))) (defun create-docs-file () (let ((functions (nreverse functions))) (with-temp-file "./README.md" (insert-file-contents-literally "./readme-template.md") (goto-and-remove "[[ function-list ]]") (insert (mapconcat 'function-summary functions "\n")) (goto-and-remove "[[ function-docs ]]") (insert (mapconcat 'function-to-md functions "\n")) (simplify-quotes)))) ;;; examples-to-docs.el ends here dash.el-2.12.1/dev/examples-to-info.el000066400000000000000000000164051261164447300174200ustar00rootroot00000000000000;;; examples-to-info.el --- Extract dash.el's Info from examples.el ;; Copyright (C) 2015 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; FIXME: Lots of duplication with examples-to-docs.el. ;;; Code: (require 'dash) (require 'dash-functional) (require 'help-fns) (defvar functions '()) (defun example-to-string (example) (let ((actual (car example)) (expected (nth 2 example))) (--> (format "@group\n%S\n @result{} %S\n@end group" actual expected) (replace-regexp-in-string "\\\\\\?" "?" it) (replace-regexp-in-string "{\"" "@{\"" it t t) (replace-regexp-in-string "}\"" "@}\"" it t t) (replace-regexp-in-string " {" " @{" it t t) (replace-regexp-in-string "\"{" "\"@{" it t t) (replace-regexp-in-string "}," "@{," it t t) (replace-regexp-in-string "}@}" "@}@}" it t t)))) (defun docs--signature (function) "Given FUNCTION (a symbol), return its argument list. FUNCTION may reference an elisp function, alias, macro or a subr." (let* ((function-value (indirect-function function)) (is-alias (not (eq function-value (symbol-function function)))) ;; if FUNCTION isn't an alias, function-symbol is simply FUNCTION (function-symbol function)) (when is-alias ;; find the last symbol in the alias chain (while (symbolp (symbol-function function-symbol)) (setq function-symbol (symbol-function function-symbol)))) (if (subrp function-value) ;; read the docstring to find the signature for subrs (let* ((docstring-args (car (help-split-fundoc (documentation function-value) function-symbol))) (fun-with-args (read (downcase docstring-args)))) (cdr fun-with-args)) ;; otherwise get the signature directly (help-function-arglist function-symbol)))) (defmacro defexamples (cmd &rest examples) `(add-to-list 'functions (list ',cmd (docs--signature ',cmd) (documentation ',cmd) (-map 'example-to-string (-partition 3 ',examples))))) (defmacro def-example-group (group desc &rest examples) `(progn (add-to-list 'functions ,(concat "### " group)) (when ,desc (add-to-list 'functions ,desc)) ,@examples)) (defun quote-and-downcase (string) (format "@var{%s}" (downcase string))) (defun unquote-and-link (string) (format-link (substring string 1 -1))) (defun format-link (string-name) (-let* ((name (intern string-name)) ((_ signature _ _) (assoc name functions))) (if signature (format "@code{%s} (@pxref{%s})" name name) (format "@code{%s}" name)))) (defun format-docstring (docstring) (let (case-fold-search) (--> docstring (replace-regexp-in-string "\\b\\([A-Z][A-Z-]*[0-9]*\\)\\b" 'quote-and-downcase it t) (replace-regexp-in-string "`\\([^ ]+\\)'" 'unquote-and-link it t) (replace-regexp-in-string "{,@}" "{,@@}" it t) (replace-regexp-in-string "^ " " " it)))) (defun function-to-node (function) (when (and (stringp function) (string-match "^\\(### [[:upper:]][[:alpha:]- ]+\\)$" function)) (concat (s-replace "### " "* " (match-string 1 function)) "::"))) (defun function-to-info (function) (if (stringp function) (concat "\n" (s-replace "### " "@node " function) "\n" (when (string-match "^### " function) (s-replace "### " "@section " function)) "\n") (-let [(command-name signature docstring examples) function] (format (concat "@anchor{%s}\n" (if (macrop command-name) "@defmac" "@defun") " %s %s\n" "%s\n\n" "@example\n%s\n@end example\n" "@end " (if (macrop command-name) "defmac" "defun") "\n") command-name command-name signature (format-docstring docstring) (mapconcat 'identity (-take 3 examples) "\n"))))) (defun docs--chop-prefix (prefix s) "Remove PREFIX if it is at the start of S." (let ((pos (length prefix))) (if (and (>= (length s) (length prefix)) (string= prefix (substring s 0 pos))) (substring s pos) s))) (defun docs--chop-suffix (suffix s) "Remove SUFFIX if it is at end of S." (let ((pos (- (length suffix)))) (if (and (>= (length s) (length suffix)) (string= suffix (substring s pos))) (substring s 0 pos) s))) (defun github-id (command-name signature) (docs--chop-suffix "-" (replace-regexp-in-string "[^a-zA-Z0-9-]+" "-" (docs--chop-prefix "!" (format "%S %S" command-name signature))))) (defun s-replace (old new s) "Replace OLD with NEW in S." (replace-regexp-in-string (regexp-quote old) new s t t)) (defun function-summary (function) (if (stringp function) (concat "\n" function "\n") (let ((command-name (car function)) (signature (cadr function))) (format "* [%s](#%s) `%s`" command-name (github-id command-name signature) signature)))) (defun simplify-quotes () (goto-char (point-min)) (while (search-forward "(quote nil)" nil t) (replace-match "'()")) (goto-char (point-min)) (while (search-forward "(quote " nil t) (forward-char -7) (let ((p (point))) (forward-sexp 1) (delete-char -1) (goto-char p) (delete-char 7) (insert "'")))) (defun goto-and-remove (s) (goto-char (point-min)) (search-forward s) (delete-char (- (length s)))) (defun create-info-file () (let ((functions (nreverse functions))) (with-temp-file "./dash.texi" (insert-file-contents-literally "./dash-template.texi") (goto-and-remove "@c [[ function-nodes ]]") (insert (mapconcat 'function-to-node (-filter (lambda (s) (when (stringp s) (string-match "^### " s))) functions) "\n")) (goto-and-remove "@c [[ function-nodes ]]") (insert (mapconcat 'function-to-node (-filter (lambda (s) (when (stringp s) (string-match "^### " s))) functions) "\n")) (goto-and-remove "@c [[ function-docs ]]") (insert (mapconcat 'function-to-info functions "\n")) (simplify-quotes)))) ;;; examples-to-info.el ends here dash.el-2.12.1/dev/examples-to-tests.el000066400000000000000000000031261261164447300176230ustar00rootroot00000000000000;;; examples-to-tests.el --- Extract dash.el's tests from examples.el ;; Copyright (C) 2015 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; FIXME: Lots of duplication with examples-to-info.el. ;;; Code: (require 'ert) (defun example-to-should (actual sym expected) (cond ((eq sym '=>) `(should (equal ,actual ,expected))) ((eq sym '~>) `(should (approx-equal ,actual ,expected))) ((eq sym '!!>) `(should-error (eval ',actual) :type ',expected)) (t (error "Invalid test case: %S" `(,actual ,sym ,expected))))) (defmacro defexamples (cmd &rest examples) (let ((tests)) (while examples (push (example-to-should (pop examples) (pop examples) (pop examples)) tests)) `(ert-deftest ,cmd () ,@(nreverse tests)))) (defun def-example-group (&rest _)) ; ignore (provide 'examples-to-tests) ;;; examples-to-tests.el ends here dash.el-2.12.1/dev/examples.el000066400000000000000000001473261261164447300160560ustar00rootroot00000000000000;;; examples.el --- Examples/tests for dash.el's API -*- lexical-binding: t -*- ;; Copyright (C) 2015 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Only the first three examples per function are shown in the docs, ;; so make those good. ;;; Code: (require 'dash) (defun even? (num) (= 0 (% num 2))) (defun square (num) (* num num)) (defun three-letters () '("A" "B" "C")) ;; Allow approximate comparison of floating-point results, to work ;; around differences in implementation between systems. Use the `~>' ;; symbol instead of `=>' to test the expected and actual values with ;; `approx-equal' (defvar dash--epsilon 1e-15) (defun approx-equal (u v) (or (= u v) (< (/ (abs (- u v)) (max (abs u) (abs v))) dash--epsilon))) (def-example-group "Maps" "Functions in this category take a transforming function, which is then applied sequentially to each or selected elements of the input list. The results are collected in order and returned as new list." (defexamples -map (-map (lambda (num) (* num num)) '(1 2 3 4)) => '(1 4 9 16) (-map 'square '(1 2 3 4)) => '(1 4 9 16) (--map (* it it) '(1 2 3 4)) => '(1 4 9 16) (--map (concat it it) (three-letters)) => '("AA" "BB" "CC")) (defexamples -map-when (-map-when 'even? 'square '(1 2 3 4)) => '(1 4 3 16) (--map-when (> it 2) (* it it) '(1 2 3 4)) => '(1 2 9 16) (--map-when (= it 2) 17 '(1 2 3 4)) => '(1 17 3 4) (-map-when (lambda (n) (= n 3)) (lambda (n) 0) '(1 2 3 4)) => '(1 2 0 4)) (defexamples -map-first (-map-first 'even? 'square '(1 2 3 4)) => '(1 4 3 4) (--map-first (> it 2) (* it it) '(1 2 3 4)) => '(1 2 9 4) (--map-first (= it 2) 17 '(1 2 3 2)) => '(1 17 3 2) (-map-first 'even? 'square '(1 3 5 7)) => '(1 3 5 7) (-map-first 'even? 'square '(2)) => '(4) (-map-first 'even? 'square nil) => nil) (defexamples -map-last (-map-last 'even? 'square '(1 2 3 4)) => '(1 2 3 16) (--map-last (> it 2) (* it it) '(1 2 3 4)) => '(1 2 3 16) (--map-last (= it 2) 17 '(1 2 3 2)) => '(1 2 3 17) (-map-last 'even? 'square '(1 3 5 7)) => '(1 3 5 7) (-map-last 'even? 'square '(2)) => '(4) (-map-last 'even? 'square nil) => nil) (defexamples -map-indexed (-map-indexed (lambda (index item) (- item index)) '(1 2 3 4)) => '(1 1 1 1) (--map-indexed (- it it-index) '(1 2 3 4)) => '(1 1 1 1)) (defexamples -annotate (-annotate '1+ '(1 2 3)) => '((2 . 1) (3 . 2) (4 . 3)) (-annotate 'length '(("h" "e" "l" "l" "o") ("hello" "world"))) => '((5 . ("h" "e" "l" "l" "o")) (2 . ("hello" "world"))) (--annotate (< 1 it) '(0 1 2 3)) => '((nil . 0) (nil . 1) (t . 2) (t . 3))) (defexamples -splice (-splice 'even? (lambda (x) (list x x)) '(1 2 3 4)) => '(1 2 2 3 4 4) (--splice 't (list it it) '(1 2 3 4)) => '(1 1 2 2 3 3 4 4) (--splice (equal it :magic) '((list of) (magical) (code)) '((foo) (bar) :magic (baz))) => '((foo) (bar) (list of) (magical) (code) (baz))) (defexamples -splice-list (-splice-list 'keywordp '(a b c) '(1 :foo 2)) => '(1 a b c 2) (-splice-list 'keywordp nil '(1 :foo 2)) => '(1 2) (--splice-list (keywordp it) '(a b c) '(1 :foo 2)) => '(1 a b c 2)) (defexamples -mapcat (-mapcat 'list '(1 2 3)) => '(1 2 3) (-mapcat (lambda (item) (list 0 item)) '(1 2 3)) => '(0 1 0 2 0 3) (--mapcat (list 0 it) '(1 2 3)) => '(0 1 0 2 0 3)) (defexamples -copy (-copy '(1 2 3)) => '(1 2 3) (let ((a '(1 2 3))) (eq a (-copy a))) => nil)) (def-example-group "Sublist selection" "Functions returning a sublist of the original list." (defexamples -filter (-filter (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) => '(2 4) (-filter 'even? '(1 2 3 4)) => '(2 4) (--filter (= 0 (% it 2)) '(1 2 3 4)) => '(2 4)) (defexamples -remove (-remove (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) => '(1 3) (-remove 'even? '(1 2 3 4)) => '(1 3) (--remove (= 0 (% it 2)) '(1 2 3 4)) => '(1 3) (let ((mod 2)) (-remove (lambda (num) (= 0 (% num mod))) '(1 2 3 4))) => '(1 3) (let ((mod 2)) (--remove (= 0 (% it mod)) '(1 2 3 4))) => '(1 3)) (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)) (defexamples -remove-last (-remove-last 'even? '(1 3 5 4 7 8 10 11)) => '(1 3 5 4 7 8 11) (-remove-last 'stringp '(1 2 "last" "second" "third")) => '(1 2 "last" "second") (--remove-last (> it 3) '(1 2 3 4 5 6 7 8 9 10)) => '(1 2 3 4 5 6 7 8 9)) (defexamples -remove-item (-remove-item 3 '(1 2 3 2 3 4 5 3)) => '(1 2 2 4 5) (-remove-item 'foo '(foo bar baz foo)) => '(bar baz) (-remove-item "bob" '("alice" "bob" "eve" "bob" "dave")) => '("alice" "eve" "dave")) (defexamples -non-nil (-non-nil '(1 nil 2 nil nil 3 4 nil 5 nil)) => '(1 2 3 4 5)) (defexamples -slice (-slice '(1 2 3 4 5) 1) => '(2 3 4 5) (-slice '(1 2 3 4 5) 0 3) => '(1 2 3) (-slice '(1 2 3 4 5 6 7 8 9) 1 -1 2) => '(2 4 6 8) (-slice '(1 2 3 4 5) 0 10) => '(1 2 3 4 5) ;; "to > length" should not fill in nils! (-slice '(1 2 3 4 5) -3) => '(3 4 5) (-slice '(1 2 3 4 5) -3 -1) => '(3 4) (-slice '(1 2 3 4 5 6) 0 nil 1) => '(1 2 3 4 5 6) (-slice '(1 2 3 4 5 6) 0 nil 2) => '(1 3 5) (-slice '(1 2 3 4 5 6) 0 nil 3) => '(1 4) (-slice '(1 2 3 4 5 6) 0 nil 10) => '(1) (-slice '(1 2 3 4 5 6) 1 4 2) => '(2 4) (-slice '(1 2 3 4 5 6) 2 6 3) => '(3 6) (-slice '(1 2 3 4 5 6) 2 -1 2) => '(3 5) (-slice '(1 2 3 4 5 6) 0 -4 2) => '(1) (-slice '(1 2 3 4 5 6) -4 -1 2) => '(3 5) (-slice '(1 2 3 4 5 6) -4 5 2) => '(3 5) (-slice '(1 2 3 4 5 6) -3 5 1) => '(4 5) (-slice '(1 2 3 4 5 6) 1 2 10) => '(2)) (defexamples -take (-take 3 '(1 2 3 4 5)) => '(1 2 3) (-take 17 '(1 2 3 4 5)) => '(1 2 3 4 5)) (defexamples -drop (-drop 3 '(1 2 3 4 5)) => '(4 5) (-drop 17 '(1 2 3 4 5)) => '()) (defexamples -take-while (-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)) (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 (< it 4) '(1 2 3 4 3 2 1)) => '(4 3 2 1)) (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") (-select-by-indices '(2 1 0) '("a" "b" "c")) => '("c" "b" "a") (-select-by-indices '(0 1 2 0 1 3 3 1) '("f" "a" "r" "l")) => '("f" "a" "r" "f" "a" "l" "l" "a"))) (def-example-group "List to list" "Bag of various functions which modify input list." (defexamples -keep (-keep 'cdr '((1 2 3) (4 5) (6))) => '((2 3) (5)) (-keep (lambda (num) (when (> num 3) (* 10 num))) '(1 2 3 4 5 6)) => '(40 50 60) (--keep (when (> it 3) (* 10 it)) '(1 2 3 4 5 6)) => '(40 50 60)) (defexamples -concat (-concat '(1)) => '(1) (-concat '(1) '(2)) => '(1 2) (-concat '(1) '(2 3) '(4)) => '(1 2 3 4) (-concat) => nil) (defexamples -flatten (-flatten '((1))) => '(1) (-flatten '((1 (2 3) (((4 (5))))))) => '(1 2 3 4 5) (-flatten '(1 2 (3 . 4))) => '(1 2 (3 . 4)) (-flatten '(nil nil nil)) => nil (-flatten '(nil (1) nil)) => '(1) (-flatten '(nil (nil) nil)) => nil) (defexamples -flatten-n (-flatten-n 1 '((1 2) ((3 4) ((5 6))))) => '(1 2 (3 4) ((5 6))) (-flatten-n 2 '((1 2) ((3 4) ((5 6))))) => '(1 2 3 4 (5 6)) (-flatten-n 3 '((1 2) ((3 4) ((5 6))))) => '(1 2 3 4 5 6) (-flatten-n 0 '(3 4)) => '(3 4) (-flatten-n 0 '((1 2) (3 4))) => '((1 2) (3 4)) (-flatten-n 0 '(((1 2) (3 4)))) => '(((1 2) (3 4)))) (defexamples -replace (-replace 1 "1" '(1 2 3 4 3 2 1)) => '("1" 2 3 4 3 2 "1") (-replace "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) => '("a" "nice" "bar" "sentence" "about" "bar") (-replace 1 2 nil) => nil) (defexamples -replace-first (-replace-first 1 "1" '(1 2 3 4 3 2 1)) => '("1" 2 3 4 3 2 1) (-replace-first "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) => '("a" "nice" "bar" "sentence" "about" "foo") (-replace-first 1 2 nil) => nil) (defexamples -replace-last (-replace-last 1 "1" '(1 2 3 4 3 2 1)) => '(1 2 3 4 3 2 "1") (-replace-last "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) => '("a" "nice" "foo" "sentence" "about" "bar") (-replace-last 1 2 nil) => nil) (defexamples -insert-at (-insert-at 1 'x '(a b c)) => '(a x b c) (-insert-at 12 'x '(a b c)) => '(a b c x)) (defexamples -replace-at (-replace-at 0 9 '(0 1 2 3 4 5)) => '(9 1 2 3 4 5) (-replace-at 1 9 '(0 1 2 3 4 5)) => '(0 9 2 3 4 5) (-replace-at 4 9 '(0 1 2 3 4 5)) => '(0 1 2 3 9 5) (-replace-at 5 9 '(0 1 2 3 4 5)) => '(0 1 2 3 4 9)) (defexamples -update-at (-update-at 0 (lambda (x) (+ x 9)) '(0 1 2 3 4 5)) => '(9 1 2 3 4 5) (-update-at 1 (lambda (x) (+ x 8)) '(0 1 2 3 4 5)) => '(0 9 2 3 4 5) (--update-at 2 (length it) '("foo" "bar" "baz" "quux")) => '("foo" "bar" 3 "quux") (--update-at 2 (concat it "zab") '("foo" "bar" "baz" "quux")) => '("foo" "bar" "bazzab" "quux")) (defexamples -remove-at (-remove-at 0 '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4" "5") (-remove-at 1 '("0" "1" "2" "3" "4" "5")) => '("0" "2" "3" "4" "5") (-remove-at 2 '("0" "1" "2" "3" "4" "5")) => '("0" "1" "3" "4" "5") (-remove-at 3 '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "4" "5") (-remove-at 4 '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "3" "5") (-remove-at 5 '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "3" "4") (-remove-at 5 '((a b) (c d) (e f g) h i ((j) k) l (m))) => '((a b) (c d) (e f g) h i l (m)) (-remove-at 0 '(((a b) (c d) (e f g) h i ((j) k) l (m)))) => nil) (defexamples -remove-at-indices (-remove-at-indices '(0) '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4" "5") (-remove-at-indices '(0 2 4) '("0" "1" "2" "3" "4" "5")) => '("1" "3" "5") (-remove-at-indices '(0 5) '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4") (-remove-at-indices '(1 2 3) '("0" "1" "2" "3" "4" "5")) => '("0" "4" "5") (-remove-at-indices '(0 1 2 3 4 5) '("0" "1" "2" "3" "4" "5")) => nil (-remove-at-indices '(2 0 4) '("0" "1" "2" "3" "4" "5")) => '("1" "3" "5") (-remove-at-indices '(5 0) '("0" "1" "2" "3" "4" "5")) => '("1" "2" "3" "4") (-remove-at-indices '(1 3 2) '("0" "1" "2" "3" "4" "5")) => '("0" "4" "5") (-remove-at-indices '(0 3 4 2 5 1) '("0" "1" "2" "3" "4" "5")) => nil (-remove-at-indices '(1) '("0" "1" "2" "3" "4" "5")) => '("0" "2" "3" "4" "5") (-remove-at-indices '(2) '("0" "1" "2" "3" "4" "5")) => '("0" "1" "3" "4" "5") (-remove-at-indices '(3) '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "4" "5") (-remove-at-indices '(4) '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "3" "5") (-remove-at-indices '(5) '("0" "1" "2" "3" "4" "5")) => '("0" "1" "2" "3" "4") (-remove-at-indices '(1 2 4) '((a b) (c d) (e f g) h i ((j) k) l (m))) => '((a b) h ((j) k) l (m)) (-remove-at-indices '(5) '((a b) (c d) (e f g) h i ((j) k) l (m))) => '((a b) (c d) (e f g) h i l (m)) (-remove-at-indices '(0) '(((a b) (c d) (e f g) h i ((j) k) l (m)))) => nil (-remove-at-indices '(2 3) '((0) (1) (2) (3) (4) (5) (6))) => '((0) (1) (4) (5) (6)))) (def-example-group "Reductions" "Functions reducing lists into single value." (defexamples -reduce-from (-reduce-from '- 10 '(1 2 3)) => 4 (-reduce-from (lambda (memo item) (concat "(" memo " - " (int-to-string item) ")")) "10" '(1 2 3)) => "(((10 - 1) - 2) - 3)" (--reduce-from (concat acc " " it) "START" '("a" "b" "c")) => "START a b c" (-reduce-from '+ 7 '()) => 7 (-reduce-from '+ 7 '(1)) => 8) (defexamples -reduce-r-from (-reduce-r-from '- 10 '(1 2 3)) => -8 (-reduce-r-from (lambda (item memo) (concat "(" (int-to-string item) " - " memo ")")) "10" '(1 2 3)) => "(1 - (2 - (3 - 10)))" (--reduce-r-from (concat it " " acc) "END" '("a" "b" "c")) => "a b c END" (-reduce-r-from '+ 7 '()) => 7 (-reduce-r-from '+ 7 '(1)) => 8) (defexamples -reduce (-reduce '- '(1 2 3 4)) => -8 (-reduce (lambda (memo item) (format "%s-%s" memo item)) '(1 2 3)) => "1-2-3" (--reduce (format "%s-%s" acc it) '(1 2 3)) => "1-2-3" (-reduce '+ '()) => 0 (-reduce '+ '(1)) => 1 (--reduce (format "%s-%s" acc it) '()) => "nil-nil") (defexamples -reduce-r (-reduce-r '- '(1 2 3 4)) => -2 (-reduce-r (lambda (item memo) (format "%s-%s" memo item)) '(1 2 3)) => "3-2-1" (--reduce-r (format "%s-%s" acc it) '(1 2 3)) => "3-2-1" (-reduce-r '+ '()) => 0 (-reduce-r '+ '(1)) => 1 (--reduce-r (format "%s-%s" it acc) '()) => "nil-nil") (defexamples -count (-count 'even? '(1 2 3 4 5)) => 2 (--count (< it 4) '(1 2 3 4)) => 3) (defexamples -sum (-sum '()) => 0 (-sum '(1)) => 1 (-sum '(1 2 3 4)) => 10) (defexamples -product (-product '()) => 1 (-product '(1)) => 1 (-product '(1 2 3 4)) => 24) (defexamples -min (-min '(0)) => 0 (-min '(3 2 1)) => 1 (-min '(1 2 3)) => 1) (defexamples -min-by (-min-by '> '(4 3 6 1)) => 1 (--min-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) => '(1 2 3) (--min-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) => '(2)) (defexamples -max (-max '(0)) => 0 (-max '(3 2 1)) => 3 (-max '(1 2 3)) => 3) (defexamples -max-by (-max-by '> '(4 3 6 1)) => 6 (--max-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) => '(3 2) (--max-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) => '(1 2 3))) (def-example-group "Unfolding" "Operations dual to reductions, building lists from seed value rather than consuming a list to produce a single value." (defexamples -iterate (-iterate '1+ 1 10) => '(1 2 3 4 5 6 7 8 9 10) (-iterate (lambda (x) (+ x x)) 2 5) => '(2 4 8 16 32) (--iterate (* it it) 2 5) => '(2 4 16 256 65536)) (defexamples -unfold (-unfold (lambda (x) (unless (= x 0) (cons x (1- x)))) 10) => '(10 9 8 7 6 5 4 3 2 1) (--unfold (when it (cons it (cdr it))) '(1 2 3 4)) => '((1 2 3 4) (2 3 4) (3 4) (4)) (--unfold (when it (cons it (butlast it))) '(1 2 3 4)) => '((1 2 3 4) (1 2 3) (1 2) (1)))) (def-example-group "Predicates" nil (defexamples -any? (-any? 'even? '(1 2 3)) => t (-any? 'even? '(1 3 5)) => nil (--any? (= 0 (% it 2)) '(1 2 3)) => t) (defexamples -all? (-all? 'even? '(1 2 3)) => nil (-all? 'even? '(2 4 6)) => t (--all? (= 0 (% it 2)) '(2 4 6)) => t) (defexamples -none? (-none? 'even? '(1 2 3)) => nil (-none? 'even? '(1 3 5)) => t (--none? (= 0 (% it 2)) '(1 2 3)) => nil) (defexamples -only-some? (-only-some? 'even? '(1 2 3)) => t (-only-some? 'even? '(1 3 5)) => nil (-only-some? 'even? '(2 4 6)) => nil (--only-some? (> it 2) '(1 2 3)) => t) (defexamples -contains? (-contains? '(1 2 3) 1) => t (-contains? '(1 2 3) 2) => t (-contains? '(1 2 3) 4) => nil (-contains? '() 1) => nil (-contains? '() '()) => nil) (defexamples -same-items? (-same-items? '(1 2 3) '(1 2 3)) => t (-same-items? '(1 2 3) '(3 2 1)) => t (-same-items? '(1 2 3) '(1 2 3 4)) => nil (-same-items? '((a . 1) (b . 2)) '((a . 1) (b . 2))) => t (-same-items? '(1 2 3) '(2 3 1)) => t) (defexamples -is-prefix? (-is-prefix? '(1 2 3) '(1 2 3 4 5)) => t (-is-prefix? '(1 2 3 4 5) '(1 2 3)) => nil (-is-prefix? '(1 3) '(1 2 3 4 5)) => nil (-is-prefix? '(1 2 3) '(1 2 4 5)) => nil) (defexamples -is-suffix? (-is-suffix? '(3 4 5) '(1 2 3 4 5)) => t (-is-suffix? '(1 2 3 4 5) '(3 4 5)) => nil (-is-suffix? '(3 5) '(1 2 3 4 5)) => nil (-is-suffix? '(3 4 5) '(1 2 3 5)) => nil (let ((l '(1 2 3))) (list (-is-suffix? '(3) l) l)) => '(t (1 2 3))) (defexamples -is-infix? (-is-infix? '(1 2 3) '(1 2 3 4 5)) => t (-is-infix? '(2 3 4) '(1 2 3 4 5)) => t (-is-infix? '(3 4 5) '(1 2 3 4 5)) => t (-is-infix? '(2 3 4) '(1 2 4 5)) => nil (-is-infix? '(2 4) '(1 2 3 4 5)) => nil)) (def-example-group "Partitioning" "Functions partitioning the input list into a list of lists." (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)) (defexamples -split-with (-split-with 'even? '(1 2 3 4)) => '(() (1 2 3 4)) (-split-with 'even? '(2 4 5 6)) => '((2 4) (5 6)) (--split-with (< it 4) '(1 2 3 4 3 2 1)) => '((1 2 3) (4 3 2 1))) (defexamples -split-on (-split-on '| '(Nil | Leaf a | Node [Tree a])) => '((Nil) (Leaf a) (Node [Tree a])) (-split-on ':endgroup '("a" "b" :endgroup "c" :endgroup "d" "e")) => '(("a" "b") ("c") ("d" "e")) (-split-on ':endgroup '("a" "b" :endgroup :endgroup "d" "e")) => '(("a" "b") ("d" "e")) (-split-on ':endgroup '("a" "b" :endgroup "c" :endgroup)) => '(("a" "b") ("c")) (-split-on ':endgroup '("a" "b" :endgroup :endgroup :endgroup "d" "e")) => '(("a" "b") ("d" "e")) (-split-on ':endgroup '(:endgroup "c" :endgroup "d" "e")) => '(("c") ("d" "e")) (-split-on '| '(Nil | | Node [Tree a])) => '((Nil) (Node [Tree a]))) (defexamples -split-when (-split-when 'even? '(1 2 3 4 5 6)) => '((1) (3) (5)) (-split-when 'even? '(1 2 3 4 6 8 9)) => '((1) (3) (9)) (--split-when (memq it '(&optional &rest)) '(a b &optional c d &rest args)) => '((a b) (c d) (args)) (-split-when 'even? '(1 2 3 5 6)) => '((1) (3 5)) (-split-when 'even? '(1 2 3 5)) => '((1) (3 5)) (-split-when 'even? '(1 3 4 5 6)) => '((1 3) (5)) (-split-when 'even? '(1 2 3 4 5 6 8 10)) => '((1) (3) (5)) (-split-when 'even? '(1 2 3 5 7 6)) => '((1) (3 5 7))) (defexamples -separate (-separate (lambda (num) (= 0 (% num 2))) '(1 2 3 4 5 6 7)) => '((2 4 6) (1 3 5 7)) (--separate (< it 5) '(3 7 5 9 3 2 1 4 6)) => '((3 3 2 1 4) (7 5 9 6)) (-separate 'cdr '((1 2) (1) (1 2 3) (4))) => '(((1 2) (1 2 3)) ((1) (4)))) (defexamples -partition (-partition 2 '(1 2 3 4 5 6)) => '((1 2) (3 4) (5 6)) (-partition 2 '(1 2 3 4 5 6 7)) => '((1 2) (3 4) (5 6)) (-partition 3 '(1 2 3 4 5 6 7)) => '((1 2 3) (4 5 6))) (defexamples -partition-all (-partition-all 2 '(1 2 3 4 5 6)) => '((1 2) (3 4) (5 6)) (-partition-all 2 '(1 2 3 4 5 6 7)) => '((1 2) (3 4) (5 6) (7)) (-partition-all 3 '(1 2 3 4 5 6 7)) => '((1 2 3) (4 5 6) (7))) (defexamples -partition-in-steps (-partition-in-steps 2 1 '(1 2 3 4)) => '((1 2) (2 3) (3 4)) (-partition-in-steps 3 2 '(1 2 3 4)) => '((1 2 3)) (-partition-in-steps 3 2 '(1 2 3 4 5)) => '((1 2 3) (3 4 5)) (-partition-in-steps 2 1 '(1)) => '()) (defexamples -partition-all-in-steps (-partition-all-in-steps 2 1 '(1 2 3 4)) => '((1 2) (2 3) (3 4) (4)) (-partition-all-in-steps 3 2 '(1 2 3 4)) => '((1 2 3) (3 4)) (-partition-all-in-steps 3 2 '(1 2 3 4 5)) => '((1 2 3) (3 4 5) (5)) (-partition-all-in-steps 2 1 '(1)) => '((1))) (defexamples -partition-by (-partition-by 'even? '()) => '() (-partition-by 'even? '(1 1 2 2 2 3 4 6 8)) => '((1 1) (2 2 2) (3) (4 6 8)) (--partition-by (< it 3) '(1 2 3 4 3 2 1)) => '((1 2) (3 4 3) (2 1))) (defexamples -partition-by-header (--partition-by-header (= it 1) '(1 2 3 1 2 1 2 3 4)) => '((1 2 3) (1 2) (1 2 3 4)) (--partition-by-header (> it 0) '(1 2 0 1 0 1 2 3 0)) => '((1 2 0) (1 0) (1 2 3 0)) (-partition-by-header 'even? '(2 1 1 1 4 1 3 5 6 6 1)) => '((2 1 1 1) (4 1 3 5) (6 6 1))) (defexamples -group-by (-group-by 'even? '()) => '() (-group-by 'even? '(1 1 2 2 2 3 4 6 8)) => '((nil . (1 1 3)) (t . (2 2 2 4 6 8))) (--group-by (car (split-string it "/")) '("a/b" "c/d" "a/e")) => '(("a" . ("a/b" "a/e")) ("c" . ("c/d"))))) (def-example-group "Indexing" "Return indices of elements based on predicates, sort elements by indices etc." (defexamples -elem-index (-elem-index 2 '(6 7 8 2 3 4)) => 3 (-elem-index "bar" '("foo" "bar" "baz")) => 1 (-elem-index '(1 2) '((3) (5 6) (1 2) nil)) => 2) (defexamples -elem-indices (-elem-indices 2 '(6 7 8 2 3 4 2 1)) => '(3 6) (-elem-indices "bar" '("foo" "bar" "baz")) => '(1) (-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) => '(1 3)) (defexamples -find-index (-find-index 'even? '(2 4 1 6 3 3 5 8)) => 0 (--find-index (< 5 it) '(2 4 1 6 3 3 5 8)) => 3 (-find-index (-partial 'string-lessp "baz") '("bar" "foo" "baz")) => 1) (defexamples -find-last-index (-find-last-index 'even? '(2 4 1 6 3 3 5 8)) => 7 (--find-last-index (< 5 it) '(2 7 1 6 3 8 5 2)) => 5 (-find-last-index (-partial 'string-lessp "baz") '("q" "foo" "baz")) => 1) (defexamples -find-indices (-find-indices 'even? '(2 4 1 6 3 3 5 8)) => '(0 1 3 7) (--find-indices (< 5 it) '(2 4 1 6 3 3 5 8)) => '(3 7) (-find-indices (-partial 'string-lessp "baz") '("bar" "foo" "baz")) => '(1)) (defexamples -grade-up (-grade-up '< '(3 1 4 2 1 3 3)) => '(1 4 3 0 5 6 2) (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-up '< l) l)) => '(1 1 2 3 3 3 4)) (defexamples -grade-down (-grade-down '< '(3 1 4 2 1 3 3)) => '(2 0 5 6 3 1 4) (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-down '< l) l)) => '(4 3 3 3 2 1 1))) (def-example-group "Set operations" "Operations pretending lists are sets." (defexamples -union (-union '(1 2 3) '(3 4 5)) => '(1 2 3 4 5) (-union '(1 2 3 4) '()) => '(1 2 3 4) (-union '(1 1 2 2) '(3 2 1)) => '(1 1 2 2 3)) (defexamples -difference (-difference '() '()) => '() (-difference '(1 2 3) '(4 5 6)) => '(1 2 3) (-difference '(1 2 3 4) '(3 4 5 6)) => '(1 2)) (defexamples -intersection (-intersection '() '()) => '() (-intersection '(1 2 3) '(4 5 6)) => '() (-intersection '(1 2 3 4) '(3 4 5 6)) => '(3 4)) (defexamples -distinct (-distinct '()) => '() (-distinct '(1 2 2 4)) => '(1 2 4))) (def-example-group "Other list operations" "Other list functions not fit to be classified elsewhere." (defexamples -rotate (-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)) (defexamples -repeat (-repeat 3 :a) => '(:a :a :a) (-repeat 1 :a) => '(:a) (-repeat 0 :a) => nil (-repeat -1 :a) => nil) (defexamples -cons* (-cons* 1 2) => '(1 . 2) (-cons* 1 2 3) => '(1 2 . 3) (-cons* 1) => 1 (-cons* 1 2 3 4) => '(1 2 3 . 4) (apply '-cons* (number-sequence 1 10)) => '(1 2 3 4 5 6 7 8 9 . 10)) (defexamples -snoc (-snoc '(1 2 3) 4) => '(1 2 3 4) (-snoc '(1 2 3) 4 5 6) => '(1 2 3 4 5 6) (-snoc '(1 2 3) '(4 5 6)) => '(1 2 3 (4 5 6))) (defexamples -interpose (-interpose "-" '()) => '() (-interpose "-" '("a")) => '("a") (-interpose "-" '("a" "b" "c")) => '("a" "-" "b" "-" "c")) (defexamples -interleave (-interleave '(1 2) '("a" "b")) => '(1 "a" 2 "b") (-interleave '(1 2) '("a" "b") '("A" "B")) => '(1 "a" "A" 2 "b" "B") (-interleave '(1 2 3) '("a" "b")) => '(1 "a" 2 "b") (-interleave '(1 2 3) '("a" "b" "c" "d")) => '(1 "a" 2 "b" 3 "c")) (defexamples -zip-with (-zip-with '+ '(1 2 3) '(4 5 6)) => '(5 7 9) (-zip-with 'cons '(1 2 3) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) (--zip-with (concat it " and " other) '("Batman" "Jekyll") '("Robin" "Hyde")) => '("Batman and Robin" "Jekyll and Hyde")) (defexamples -zip (-zip '(1 2 3) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) (-zip '(1 2 3) '(4 5 6 7)) => '((1 . 4) (2 . 5) (3 . 6)) (-zip '(1 2 3 4) '(4 5 6)) => '((1 . 4) (2 . 5) (3 . 6)) (-zip '(1 2 3) '(4 5 6) '(7 8 9)) => '((1 4 7) (2 5 8) (3 6 9)) (-zip '(1 2) '(3 4 5) '(6)) => '((1 3 6))) (defexamples -zip-fill (-zip-fill 0 '(1 2 3 4 5) '(6 7 8 9)) => '((1 . 6) (2 . 7) (3 . 8) (4 . 9) (5 . 0))) (defexamples -cycle (-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))) (defexamples -pad (-pad 0 '()) => '(()) (-pad 0 '(1)) => '((1)) (-pad 0 '(1 2 3) '(4 5)) => '((1 2 3) (4 5 0)) (-pad nil '(1 2 3) '(4 5) '(6 7 8 9 10)) => '((1 2 3 nil nil) (4 5 nil nil nil) (6 7 8 9 10)) (-pad 0 '(1 2) '(3 4)) => '((1 2) (3 4))) (defexamples -table (-table '* '(1 2 3) '(1 2 3)) => '((1 2 3) (2 4 6) (3 6 9)) (-table (lambda (a b) (-sum (-zip-with '* a b))) '((1 2) (3 4)) '((1 3) (2 4))) => '((7 15) (10 22)) (apply '-table 'list (-repeat 3 '(1 2))) => '((((1 1 1) (2 1 1)) ((1 2 1) (2 2 1))) (((1 1 2) (2 1 2)) ((1 2 2) (2 2 2))))) (defexamples -table-flat (-table-flat 'list '(1 2 3) '(a b c)) => '((1 a) (2 a) (3 a) (1 b) (2 b) (3 b) (1 c) (2 c) (3 c)) (-table-flat '* '(1 2 3) '(1 2 3)) => '(1 2 3 2 4 6 3 6 9) (apply '-table-flat 'list (-repeat 3 '(1 2))) => '((1 1 1) (2 1 1) (1 2 1) (2 2 1) (1 1 2) (2 1 2) (1 2 2) (2 2 2)) ;; flatten law tests (-flatten-n 1 (-table 'list '(1 2 3) '(a b c))) => '((1 a) (2 a) (3 a) (1 b) (2 b) (3 b) (1 c) (2 c) (3 c)) (-flatten-n 1 (-table '* '(1 2 3) '(1 2 3))) => '(1 2 3 2 4 6 3 6 9) (-flatten-n 2 (apply '-table 'list (-repeat 3 '(1 2)))) => '((1 1 1) (2 1 1) (1 2 1) (2 2 1) (1 1 2) (2 1 2) (1 2 2) (2 2 2))) (defexamples -first (-first 'even? '(1 2 3)) => 2 (-first 'even? '(1 3 5)) => nil (--first (> it 2) '(1 2 3)) => 3) (defexamples -some (-some 'even? '(1 2 3)) => t (--some (member 'foo it) '((foo bar) (baz))) => '(foo bar) (--some (plist-get it :bar) '((:foo 1 :bar 2) (:baz 3))) => 2) (defexamples -last (-last 'even? '(1 2 3 4 5 6 3 3 3)) => 6 (-last 'even? '(1 3 7 5 9)) => nil (--last (> (length it) 3) '("a" "looong" "word" "and" "short" "one")) => "short") (defexamples -first-item (-first-item '(1 2 3)) => 1 (-first-item nil) => nil) (defexamples -last-item (-last-item '(1 2 3)) => 3 (-last-item nil) => nil) (defexamples -butlast (-butlast '(1 2 3)) => '(1 2) (-butlast '(1 2)) => '(1) (-butlast '(1)) => nil (-butlast nil) => nil) (defexamples -sort (-sort '< '(3 1 2)) => '(1 2 3) (-sort '> '(3 1 2)) => '(3 2 1) (--sort (< it other) '(3 1 2)) => '(1 2 3) (let ((l '(3 1 2))) (-sort '> l) l) => '(3 1 2)) (defexamples -list (-list 1) => '(1) (-list 1 2 3) => '(1 2 3) (-list '(1 2 3)) => '(1 2 3) (-list '((1) (2))) => '((1) (2))) (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"))) (def-example-group "Tree operations" "Functions pretending lists are trees." (defexamples -tree-seq (-tree-seq 'listp 'identity '(1 (2 3) 4 (5 (6 7)))) => '((1 (2 3) 4 (5 (6 7))) 1 (2 3) 2 3 4 (5 (6 7)) 5 (6 7) 6 7) (-tree-seq 'listp 'reverse '(1 (2 3) 4 (5 (6 7)))) => '((1 (2 3) 4 (5 (6 7))) (5 (6 7)) (6 7) 7 6 5 4 (2 3) 3 2 1) (--tree-seq (vectorp it) (append it nil) [1 [2 3] 4 [5 [6 7]]]) => '([1 [2 3] 4 [5 [6 7]]] 1 [2 3] 2 3 4 [5 [6 7]] 5 [6 7] 6 7)) (defexamples -tree-map (-tree-map '1+ '(1 (2 3) (4 (5 6) 7))) => '(2 (3 4) (5 (6 7) 8)) (-tree-map '(lambda (x) (cons x (expt 2 x))) '(1 (2 3) 4)) => '((1 . 2) ((2 . 4) (3 . 8)) (4 . 16)) (--tree-map (length it) '("" ("

" "text" "

") "")) => '(6 (3 4 4) 7) (--tree-map 1 '(1 2 (3 4) (5 6))) => '(1 1 (1 1) (1 1)) (--tree-map (cdr it) '((1 . 2) (3 . 4) (5 . 6))) => '(2 4 6)) (defexamples -tree-map-nodes (-tree-map-nodes 'vectorp (lambda (x) (-sum (append x nil))) '(1 [2 3] 4 (5 [6 7] 8))) => '(1 5 4 (5 13 8)) (-tree-map-nodes 'keywordp (lambda (x) (symbol-name x)) '(1 :foo 4 ((5 6 :bar) :baz 8))) => '(1 ":foo" 4 ((5 6 ":bar") ":baz" 8)) (--tree-map-nodes (eq (car-safe it) 'add-mode) (-concat it (list :mode 'emacs-lisp-mode)) '(with-mode emacs-lisp-mode (foo bar) (add-mode a b) (baz (add-mode c d)))) => '(with-mode emacs-lisp-mode (foo bar) (add-mode a b :mode emacs-lisp-mode) (baz (add-mode c d :mode emacs-lisp-mode)))) (defexamples -tree-reduce (-tree-reduce '+ '(1 (2 3) (4 5))) => 15 (-tree-reduce 'concat '("strings" (" on" " various") ((" levels")))) => "strings on various levels" (--tree-reduce (cond ((stringp it) (concat it " " acc)) (t (let ((sn (symbol-name it))) (concat "<" sn ">" acc "")))) '(body (p "some words") (div "more" (b "bold") "words"))) => "

some words

more bold words
") (defexamples -tree-reduce-from (-tree-reduce-from '+ 1 '(1 (1 1) ((1)))) => 8 (--tree-reduce-from (-concat acc (list it)) nil '(1 (2 3 (4 5)) (6 7))) => '((7 6) ((5 4) 3 2) 1)) (defexamples -tree-mapreduce (-tree-mapreduce 'list 'append '(1 (2 (3 4) (5 6)) (7 (8 9)))) => '(1 2 3 4 5 6 7 8 9) (--tree-mapreduce 1 (+ it acc) '(1 (2 (4 9) (2 1)) (7 (4 3)))) => 9 (--tree-mapreduce 0 (max acc (1+ it)) '(1 (2 (4 9) (2 1)) (7 (4 3)))) => 3 (--tree-mapreduce (-value-to-list it) (-concat it acc) '((1 . 2) (3 . 4) (5 (6 7) 8))) => '(1 2 3 4 5 6 7 8) (--tree-mapreduce (if (-cons-pair? it) (cdr it) it) (concat it " " acc) '("foo" (bar . "bar") ((baz . "baz")) "quux" (qwop . "qwop"))) => "foo bar baz quux qwop" (--tree-mapreduce (if (-cons-pair? it) (list (cdr it)) nil) (append it acc) '((elips-mode (foo (bar . booze)) (baz . qux)) (c-mode (foo . bla) (bum . bam)))) => '(booze qux bla bam)) (defexamples -tree-mapreduce-from (-tree-mapreduce-from 'identity '* 1 '(1 (2 (3 4) (5 6)) (7 (8 9)))) => 362880 (--tree-mapreduce-from (+ it it) (cons it acc) nil '(1 (2 (4 9) (2 1)) (7 (4 3)))) => '(2 (4 (8 18) (4 2)) (14 (8 6))) (concat "{" (--tree-mapreduce-from (cond ((-cons-pair? it) (concat (symbol-name (car it)) " -> " (symbol-name (cdr it)))) (t (concat (symbol-name it) " : {"))) (concat it (unless (or (equal acc "}") (equal (substring it (1- (length it))) "{")) ", ") acc) "}" '((elips-mode (foo (bar . booze)) (baz . qux)) (c-mode (foo . bla) (bum . bam))))) => "{elips-mode : {foo : {bar -> booze}, baz -> qux}, c-mode : {foo -> bla, bum -> bam}}") (defexamples -clone (let* ((a '(1 2 3)) (b (-clone a))) (nreverse a) b) => '(1 2 3))) (def-example-group "Threading macros" nil (defexamples -> (-> '(2 3 5)) => '(2 3 5) (-> '(2 3 5) (append '(8 13))) => '(2 3 5 8 13) (-> '(2 3 5) (append '(8 13)) (-slice 1 -1)) => '(3 5 8) (-> 5 square) => 25 (-> 5 (+ 3) square) => 64) (defexamples ->> (->> '(1 2 3) (-map 'square)) => '(1 4 9) (->> '(1 2 3) (-map 'square) (-remove 'even?)) => '(1 9) (->> '(1 2 3) (-map 'square) (-reduce '+)) => 14 (->> 5 (- 8)) => 3 (->> 5 (- 3) square) => 4) (defexamples --> (--> "def" (concat "abc" it "ghi")) => "abcdefghi" (--> "def" (concat "abc" it "ghi") (upcase it)) => "ABCDEFGHI" (--> "def" (concat "abc" it "ghi") upcase) => "ABCDEFGHI") (defexamples -some-> (-some-> '(2 3 5)) => '(2 3 5) (-some-> 5 square) => 25 (-some-> 5 even? square) => nil (-some-> nil square) => nil) (defexamples -some->> (-some->> '(1 2 3) (-map 'square)) => '(1 4 9) (-some->> '(1 3 5) (-last 'even?) (+ 100)) => nil (-some->> '(2 4 6) (-last 'even?) (+ 100)) => 106 (-some->> '("A" "B" :c) (-filter 'stringp) (-reduce 'concat)) => "AB" (-some->> '(:a :b :c) (-filter 'stringp) (-reduce 'concat)) => nil) (defexamples -some--> (-some--> "def" (concat "abc" it "ghi")) => "abcdefghi" (-some--> nil (concat "abc" it "ghi")) => nil (-some--> '(1 3 5) (-filter 'even? it) (append it it) (-map 'square it)) => nil (-some--> '(2 4 6) (-filter 'even? it) (append it it) (-map 'square it)) => '(4 16 36 4 16 36))) (def-example-group "Binding" "Convenient versions of `let` and `let*` constructs combined with flow control." (defexamples -when-let (-when-let (match-index (string-match "d" "abcd")) (+ match-index 2)) => 5 (-when-let ((&plist :foo foo) (list :foo "foo")) foo) => "foo" (-when-let ((&plist :foo foo) (list :bar "bar")) foo) => nil (--when-let (member :b '(:a :b :c)) (cons :d it)) => '(:d :b :c) (--when-let (even? 3) (cat it :a)) => nil) (defexamples -when-let* (-when-let* ((x 5) (y 3) (z (+ y 4))) (+ x y z)) => 15 (-when-let* ((x 5) (y nil) (z 7)) (+ x y z)) => nil) (defexamples -if-let (-if-let (match-index (string-match "d" "abc")) (+ match-index 3) 7) => 7 (--if-let (even? 4) it nil) => t) (defexamples -if-let* (-if-let* ((x 5) (y 3) (z 7)) (+ x y z) "foo") => 15 (-if-let* ((x 5) (y nil) (z 7)) (+ x y z) "foo") => "foo" (-if-let* (((_ _ x) '(nil nil 7))) x) => 7) (defexamples -let (-let (([a (b c) d] [1 (2 3) 4])) (list a b c d)) => '(1 2 3 4) (-let [(a b c . d) (list 1 2 3 4 5 6)] (list a b c d)) => '(1 2 3 (4 5 6)) (-let [(&plist :foo foo :bar bar) (list :baz 3 :foo 1 :qux 4 :bar 2)] (list foo bar)) => '(1 2) (let ((a (list 1 2 3)) (b (list 'a 'b 'c))) (-let (((a . b) a) ((c . d) b)) (list a b c d))) => '(1 (2 3) a (b c)) (-let ((a "foo") (b "bar")) (list a b)) => '("foo" "bar") (-let [foo (list 1 2 3)] foo) => '(1 2 3) (-let [(&plist :foo foo :bar bar) (list :foo 1 :bar 2)] (list foo bar)) => '(1 2) (-let [(&plist :foo (a b) :bar c) (list :foo (list 1 2) :bar 3)] (list a b c)) => '(1 2 3) ;; nil value in plist means subsequent cons matches are nil, because ;; (car nil) => nil (-let [(&plist :foo (a b)) (list :bar 1)] (list a b)) => '(nil nil) (-let [(&plist :foo (&plist :baz baz) :bar bar) (list :foo (list 1 2 :baz 2 :bar 4) :bar 3)] (list baz bar)) => '(2 3) (-let [(_ (&plist :level level :title title)) (list 'paragraph (list :title "foo" :level 2))] (list level title)) => '(2 "foo") (-let [(&alist :foo (&plist 'face face 'invisible inv) :bar bar) (list (cons :bar 2) (cons :foo (list 'face 'foo-face 'invisible t)))] (list bar face inv)) => '(2 foo-face t) (-let [(a (b c) d) (list 1 (list 2 3) 4 5 6)] (list a b c d)) => '(1 2 3 4) (-let [[a _ c] [1 2 3 4]] (list a c)) => '(1 3) (-let [[_ _ _ a] (vector 1 2 3 4)] a) => 4 (-let [[a _ _ _ b] (vector 1 2 3 4 5)] (list a b)) => '(1 5) (-let [[a (b c) d] [1 (2 3) 4]] (list a b c d)) => '(1 2 3 4) (-let [[a b c] (string ?f ?o ?b ?a ?r)] (list a b c)) => '(?f ?o ?b) (-let [[a b c] "abcdef"] (list a b c)) => '(?a ?b ?c) (-let [[a (b [c]) d] [1 (2 [3 4]) 5 6]] (list a b c d)) => '(1 2 3 5) (-let [(a b c d) (list 1 2 3 4 5 6)] (list a b c d)) => '(1 2 3 4) (-let [([a b]) (list (vector 1 2 3))] (list a b)) => '(1 2) ;; d is bound to nil. I don't think we want to error in such a case. ;; After all (car nil) => nil (-let [(a b c d) (list 1 2 3)] (list a b c d)) => '(1 2 3 nil) (-let [[a b c] [1 2 3 4]] (list a b c)) => '(1 2 3) (-let [[a] [1 2 3 4]] a) => 1 (-let [[a b &rest c] "abcdef"] (list a b c)) => '(?a ?b "cdef") (-let [[a b &rest c] [1 2 3 4 5 6]] (list a b c)) => '(1 2 [3 4 5 6]) (-let [[a b &rest [c d]] [1 2 3 4 5 6]] (list a b c d)) => '(1 2 3 4) ;; here we error, because "vectors" are rigid, immutable structures, ;; so we should know how many elements there are (-let [[a b c d] [1 2 3]] t) !!> args-out-of-range (-let [(a . (b . c)) (cons 1 (cons 2 3))] (list a b c)) => '(1 2 3) (-let [(_ _ . [a b]) (cons 1 (cons 2 (vector 3 4)))] (list a b)) => '(3 4) (-let [(_ _ . (a b)) (cons 1 (cons 2 (list 3 4)))] (list a b)) => '(3 4) (-let [([a b] _ _ c) (list (vector 1 2) 3 4 5)] (list a b c)) => '(1 2 5) ;; final cdr optimization (-let [(((a))) (list (list (list 1 2) 3) 4)] a) => 1 (-let [(((a b) c) d) (list (list (list 1 2) 3) 4)] (list a b c d)) => '(1 2 3 4) (-let [(((a b) . c) . d) (list (list (list 1 2) 3) 4)] (list a b c d)) => '(1 2 (3) (4)) (-let [(((a b) c)) (list (list (list 1 2) 3) 4)] (list a b c)) => '(1 2 3) (-let [(((a b) . c)) (list (list (list 1 2) 3) 4)] (list a b c)) => '(1 2 (3)) ;; cdr-skip optimization (-let [(_ (_ (_ a))) (list 1 (list 2 (list 3 4)))] a) => 4 (-let [(_ (a)) (list 1 (list 2))] a) => 2 (-let [(_ _ _ a) (list 1 2 3 4 5)] a) => 4 (-let [(_ _ _ (a b)) (list 1 2 3 (list 4 5))] (list a b)) => '(4 5) (-let [(_ a _ b) (list 1 2 3 4 5)] (list a b)) => '(2 4) (-let [(_ a _ b _ c) (list 1 2 3 4 5 6)] (list a b c)) => '(2 4 6) (-let [(_ a _ b _ _ _ c) (list 1 2 3 4 5 6 7 8)] (list a b c)) => '(2 4 8) (-let [(_ a _ _ _ b _ c) (list 1 2 3 4 5 6 7 8)] (list a b c)) => '(2 6 8) (-let [(_ _ _ a _ _ _ b _ _ _ c) (list 1 2 3 4 5 6 7 8 9 10 11 12)] (list a b c)) => '(4 8 12) (-let [(_ (a b) _ c) (list 1 (list 2 3) 4 5)] (list a b c)) => '(2 3 5) (-let [(_ (a b) _ . c) (list 1 (list 2 3) 4 5)] (list a b c)) => '(2 3 (5)) (-let [(_ (a b) _ (c d)) (list 1 (list 2 3) 4 (list 5 6))] (list a b c d)) => '(2 3 5 6) (-let [(_ (a b) _ _ _ (c d)) (list 1 (list 2 3) 4 5 6 (list 7 8))] (list a b c d)) => '(2 3 7 8) (-let [(_ (a b) _ . (c d)) (list 1 (list 2 3) 4 5 6)] (list a b c d)) => '(2 3 5 6) (-let [(_ (a b) _ _ _ [c d]) (list 1 (list 2 3) 4 5 6 (vector 7 8))] (list a b c d)) => '(2 3 7 8) (-let [(_ [a b] _ _ _ [c d]) (list 1 (vector 2 3) 4 5 6 (vector 7 8))] (list a b c d)) => '(2 3 7 8) (-let [(_ _ _ . a) (list 1 2 3 4 5)] a) => '(4 5) (-let [(_ a _ _) (list 1 2 3 4 5)] a) => 2 (-let [(_ . b) (cons 1 2)] b) => 2 (-let [([a b c d] . e) (cons (vector 1 2 3 4) 5)] (list a b c d e)) => '(1 2 3 4 5) (-let [([a b c d] _ . e) (cons (vector 1 2 3 4) (cons 5 6))] (list a b c d e)) => '(1 2 3 4 6) ;; late-binding optimization (-let [(((a))) (list (list (list 1 2) 3) 4)] a) => 1 (-let [(((&plist :foo a :bar b))) (list (list (list :bar 1 :foo 2) 3) 4)] (list a b)) => '(2 1) (-let [(((a b) c) d) (list (list (list 1 2) 3) 4)] (list a b c d)) => '(1 2 3 4) (-let [(((a b) c) . d) (list (list (list 1 2) 3) 4)] (list a b c d)) => '(1 2 3 (4)) (-let [(((a b) c)) (list (list (list 1 2) 3) 4)] (list a b c)) => '(1 2 3) (-let [(a b c d) (list 1 2 3 4)] (list a b c d)) => '(1 2 3 4) (-let [(a) (list 1 2 3 4)] (list a)) => '(1) (-let [(_ a) (list 1 2 3 4)] (list a)) => '(2) (-let [(_ _ a) (list 1 2 3 4)] (list a)) => '(3) (-let [(_ _ . a) (list 1 2 3 4)] a) => '(3 4) (-let [(_ _ [a b]) (list 1 2 (vector 3 4))] (list a b)) => '(3 4) (-let [(a _ _ b) (list 1 2 3 4 5 6 7 8)] (list a b)) => '(1 4) (-let [(_ _ a _ _ b) (list 1 2 3 4 5 6 7 8)] (list a b)) => '(3 6) (-let [(_ _ a _ _ . b) (list 1 2 3 4 5 6 7 8)] (cons a b)) => '(3 6 7 8) (-let [(_ a _ b) (list 1 2 3 4)] (list a b)) => '(2 4) (-let [(a b c (d e)) (list 1 2 3 (list 4 5))] (list a b c d e)) => '(1 2 3 4 5) (-let [(_ _ (_ _ (_ _ a))) (list 1 2 (list 3 4 (list 5 6 7)))] a) => 7 (-let [(_ (_ (_ a))) (list 1 (list 2 (list 3 4)))] a) => 4 (-let [(_ _ . (&plist :foo a :bar b)) (list 1 2 :bar 2 :foo 1)] (list a b)) => '(1 2) ;; &keys support (-let [(_ _ &keys :foo a :bar b) (list 1 2 :bar 4 :foo 3)] (list a b)) => '(3 4) (-let [(a _ &keys :foo b :bar c) (list 1 2 :bar 4 :foo 3)] (list a b c)) => '(1 3 4) (-let [(a _ _ _ &keys :foo b :bar c) (list 1 2 3 4 :bar 6 :foo 5)] (list a b c)) => '(1 5 6) (-let [(a b &keys :foo c :bar d) (list 1 2 :bar 4 :foo 3)] (list a b c d)) => '(1 2 3 4) (-let [(a b &keys) (list 1 2 :bar 4 :foo 3)] (list a b)) => '(1 2) (-let [(&keys :foo a :bar b) (list 1 2 :bar 4 :foo 3)] (list a b)) => '(3 4) (-let [(a b (c _ _ &keys :foo [d _ (&alist :bar (e &keys :baz f) :qux (&plist :fux g))] :mux h) i) (list 1 2 (list 3 'skip 'skip :foo (vector 4 'skip (list (cons :bar (list 5 :baz 6)) (cons :qux (list :fux 7)))) :mux 8) 9)] (list a b c d e f g h i)) => '(1 2 3 4 5 6 7 8 9) ;; single-binding optimization for vectors and kv (-let [[_ [_ [_ a]]] (vector 1 (vector 2 (vector 3 4)))] a) => 4 (-let [[a _ _ _] (vector 1 2 3 4)] a) => 1 (-let [[_ _ _ a] (vector 1 2 3 4)] a) => 4 (-let [[_ _ a _] (vector 1 2 3 4)] a) => 3 (-let [[a [_ [_ b]]] (vector 1 (vector 2 (vector 3 4)))] (list a b)) => '(1 4) (-let [[(a _ b)] (vector (list 1 2 3 4))] (list a b)) => '(1 3) (-let [(&plist 'a a) (list 'a 1 'b 2)] a) => 1 (-let [(&plist 'a [a b]) (list 'a [1 2] 'b 3)] (list a b)) => '(1 2) (-let [(&plist 'a [a b] 'c c) (list 'a [1 2] 'c 3)] (list a b c)) => '(1 2 3) ;; mixing dot and &alist (-let (((x y &alist 'a a 'c c) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list x y a c)) => '(1 2 b d) (-let (((_ _ &alist 'a a 'c c) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list a c)) => '(b d) (-let (((x y . (&alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list x y a c)) => '(1 2 b d) (-let (((_ _ . (&alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h) '(c . d)))) (list a c)) => '(b d) (-let (((x y (&alist 'a a 'c c)) (list 1 2 '((a . b) (e . f) (g . h) (c . d))))) (list x y a c)) => '(1 2 b d) (-let (((_ _ . ((&alist 'a a 'c c))) (list 1 2 '((a . b) (e . f) (g . h) (c . d))))) (list a c)) => '(b d) ;; test the &as form (-let (((items &as first . rest) (list 1 2 3))) (list first rest items)) => '(1 (2 3) (1 2 3)) (-let [(all &as [vect &as a b] bar) (list [1 2] 3)] (list a b bar vect all)) => '(1 2 3 [1 2] ([1 2] 3)) (-let [(all &as (list &as a b) bar) (list (list 1 2) 3)] (list a b bar list all)) => '(1 2 3 (1 2) ((1 2) 3)) (-let [(x &as [a b]) (list (vector 1 2 3))] (list a b x)) => '(1 2 ([1 2 3])) (-let [(result &as [_ a] [_ b]) (list [1 2] [3 4])] (list a b result)) => '(2 4 ([1 2] [3 4])) (-let [(result &as [fst &as _ a] [snd &as _ b]) (list [1 2] [3 4])] (list a b fst snd result)) => '(2 4 [1 2] [3 4] ([1 2] [3 4])) (-let [[x &as a b &rest r] (vector 1 2 3)] (list a b r x)) => '(1 2 [3] [1 2 3]) (-let [[x &as a] (vector 1 2 3)] (list a x)) => '(1 [1 2 3]) (-let [[x &as _ _ a] (vector 1 2 3)] (list a x)) => '(3 [1 2 3]) (-let [[x &as _ _ a] (vector 1 2 (list 3 4))] (list a x)) => '((3 4) [1 2 (3 4)]) (-let [[x &as _ _ (a b)] (vector 1 2 (list 3 4))] (list a b x)) => '(3 4 [1 2 (3 4)]) (-let [(b &as beg . end) (cons 1 2)] (list beg end b)) => '(1 2 (1 . 2)) (-let [(plist &as &plist :a a :b b) (list :a 1 :b 2)] (list a b plist)) => '(1 2 (:a 1 :b 2)) (-let [(alist &as &alist :a a :b b) (list (cons :a 1) (cons :b 2))] (list a b alist)) => '(1 2 ((:a . 1) (:b . 2))) (-let [(list &as _ _ _ a _ _ _ b _ _ _ c) (list 1 2 3 4 5 6 7 8 9 10 11 12)] (list a b c list)) => '(4 8 12 (1 2 3 4 5 6 7 8 9 10 11 12)) (-let (((x &as a b) (list 1 2)) ((y &as c d) (list 3 4))) (list a b c d x y)) => '(1 2 3 4 (1 2) (3 4))) (defexamples -let* (-let* (((a . b) (cons 1 2)) ((c . d) (cons 3 4))) (list a b c d)) => '(1 2 3 4) (-let* (((a . b) (cons 1 (cons 2 3))) ((c . d) b)) (list a b c d)) => '(1 (2 . 3) 2 3) (-let* (((&alist "foo" foo "bar" bar) (list (cons "foo" 1) (cons "bar" (list 'a 'b 'c)))) ((a b c) bar)) (list foo a b c bar)) => '(1 a b c (a b c)) (let ((a (list 1 2 3)) (b (list 'a 'b 'c))) (-let* (((a . b) a) ((c . d) b)) ;; b here comes from above binding (list a b c d))) => '(1 (2 3) 2 (3)) (-let* ((a "foo") (b a)) (list a b)) => '("foo" "foo")) (defexamples -lambda (-map (-lambda ((x y)) (+ x y)) '((1 2) (3 4) (5 6))) => '(3 7 11) (-map (-lambda ([x y]) (+ x y)) '([1 2] [3 4] [5 6])) => '(3 7 11) (funcall (-lambda ((_ . a) (_ . b)) (-concat a b)) '(1 2 3) '(4 5 6)) => '(2 3 5 6) (-map (-lambda ((&plist :a a :b b)) (+ a b)) '((:a 1 :b 2) (:a 3 :b 4) (:a 5 :b 6))) => '(3 7 11) (-map (-lambda (x) (let ((k (car x)) (v (cadr x))) (+ k v))) '((1 2) (3 4) (5 6))) => '(3 7 11) (funcall (-lambda ((a) (b)) (+ a b)) '(1 2 3) '(4 5 6)) => 5 (-lambda a t) !!> wrong-type-argument (funcall (-lambda (a b) (+ a b)) 1 2) => 3 (funcall (-lambda (a (b c)) (+ a b c)) 1 (list 2 3)) => 6)) (def-example-group "Side-effects" "Functions iterating over lists for side-effect only." (defexamples -each (let (s) (-each '(1 2 3) (lambda (item) (setq s (cons item s))))) => nil (let (s) (-each '(1 2 3) (lambda (item) (setq s (cons item s)))) s) => '(3 2 1) (let (s) (--each '(1 2 3) (setq s (cons it s))) s) => '(3 2 1) (let (s) (--each (reverse (three-letters)) (setq s (cons it s))) s) => '("A" "B" "C")) (defexamples -each-while (let (s) (-each-while '(2 4 5 6) 'even? (lambda (item) (!cons item s))) s) => '(4 2) (let (s) (--each-while '(1 2 3 4) (< it 3) (!cons it s)) s) => '(2 1)) (defexamples -dotimes (let (s) (-dotimes 3 (lambda (n) (!cons n s))) s) => '(2 1 0) (let (s) (--dotimes 5 (!cons it s)) s) => '(4 3 2 1 0))) (def-example-group "Destructive operations" nil (defexamples !cons (let (l) (!cons 5 l) l) => '(5) (let ((l '(3))) (!cons 5 l) l) => '(5 3)) (defexamples !cdr (let ((l '(3))) (!cdr l) l) => '() (let ((l '(3 5))) (!cdr l) l) => '(5))) (def-example-group "Function combinators" "These combinators require Emacs 24 for its lexical scope. So they are offered in a separate package: `dash-functional`." (defexamples -partial (funcall (-partial '- 5) 3) => 2 (funcall (-partial '+ 5 2) 3) => 10) (unless (version< emacs-version "24") (defexamples -rpartial (funcall (-rpartial '- 5) 8) => 3 (funcall (-rpartial '- 5 2) 10) => 3) (defexamples -juxt (funcall (-juxt '+ '-) 3 5) => '(8 -2) (-map (-juxt 'identity 'square) '(1 2 3)) => '((1 1) (2 4) (3 9))) (defexamples -compose (funcall (-compose 'square '+) 2 3) => (square (+ 2 3)) (funcall (-compose 'identity 'square) 3) => (square 3) (funcall (-compose 'square 'identity) 3) => (square 3) (funcall (-compose (-compose 'not 'even?) 'square) 3) => (funcall (-compose 'not (-compose 'even? 'square)) 3))) (defexamples -applify (-map (-applify '+) '((1 1 1) (1 2 3) (5 5 5))) => '(3 6 15) (-map (-applify (lambda (a b c) `(,a (,b (,c))))) '((1 1 1) (1 2 3) (5 5 5))) => '((1 (1 (1))) (1 (2 (3))) (5 (5 (5)))) (funcall (-applify '<) '(3 6)) => t) (unless (version< emacs-version "24") (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 'int-to-string) '(2 100 22)) => 22 (-max-by (-on '> 'car) '((2 2 3) (3) (1 2))) => '(3) (-sort (-on 'string-lessp 'int-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))) (defexamples -flip (funcall (-flip '<) 2 1) => t (funcall (-flip '-) 3 8) => 5 (-sort (-flip '<) '(4 3 6 1)) => '(6 4 3 1)) (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) (defexamples -cut (funcall (-cut list 1 <> 3 <> 5) 2 4) => '(1 2 3 4 5) (-map (-cut funcall <> 5) '(1+ 1- (lambda (x) (/ 1.0 x)))) => '(6 4 0.2) (-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)) (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) (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)) (defexamples -iteratefn (funcall (-iteratefn (lambda (x) (* x x)) 3) 2) => 256 (funcall (-iteratefn '1+ 3) 1) => 4 (funcall (-iteratefn 'cdr 3) '(1 2 3 4 5)) => '(4 5) (let ((init '(1 2 3 4 5)) (fn 'cdr)) (and (equal (funcall (-iteratefn fn 0) init) (-last-item (-iterate fn init (1+ 0)))) (equal (funcall (-iteratefn fn 3) init) (-last-item (-iterate fn init (1+ 3)))) (equal (funcall (-iteratefn fn 5) init) (-last-item (-iterate fn init (1+ 5)))))) => t) (defexamples -fixfn ;; Find solution to cos(x) = x (may not converge without fuzzy comparison) (funcall (-fixfn 'cos 'approx-equal) 0.7) ~> 0.7390851332151607 ;; Find solution to x^4 - x - 10 = 0 (converges using 'equal comparison) (funcall (-fixfn (lambda (x) (expt (+ x 10) 0.25))) 2.0) => 1.8555845286409378 ;; The sin function has a fixpoint at zero, but it converges too slowly and is halted (funcall (-fixfn 'sin 'approx-equal) 0.1) => '(halted . t)) (defexamples -prodfn (funcall (-prodfn '1+ '1- 'int-to-string) '(1 2 3)) => '(2 1 "3") (-map (-prodfn '1+ '1-) '((1 2) (3 4) (5 6) (7 8))) => '((2 1) (4 3) (6 5) (8 7)) (apply '+ (funcall (-prodfn 'length 'string-to-int) '((1 2 3) "15"))) => 18 (let ((f '1+) (g '1-) (ff 'string-to-int) (gg 'length) (input '(1 2)) (input2 "foo") (input3 '("10" '(1 2 3)))) (and (equal (funcall (-prodfn f g) input) (funcall (-juxt (-compose f (-partial 'nth 0)) (-compose g (-partial 'nth 1))) input)) (equal (funcall (-compose (-prodfn f g) (-juxt ff gg)) input2) (funcall (-juxt (-compose f ff) (-compose g gg)) input2)) (equal (funcall (-compose (-partial 'nth 0) (-prodfn f g)) input) (funcall (-compose f (-partial 'nth 0)) input)) (equal (funcall (-compose (-partial 'nth 1) (-prodfn f g)) input) (funcall (-compose g (-partial 'nth 1)) input)) (equal (funcall (-compose (-prodfn f g) (-prodfn ff gg)) input3) (funcall (-prodfn (-compose f ff) (-compose g gg)) input3)))) => t))) ;; Local Variables: ;; eval: (font-lock-add-keywords nil '(("defexamples\\|def-example-group\\| => \\| !!> \\| ~>" (0 'font-lock-keyword-face)) ("(defexamples[[:blank:]]+\\(.*\\)" (1 'font-lock-function-name-face)))) ;; End: ;;; examples.el ends here dash.el-2.12.1/pre-commit.sh000077500000000000000000000002561261164447300155410ustar00rootroot00000000000000#!/bin/sh git stash -q --keep-index ./run-tests.sh RESULT=$? [ $RESULT == 0 ] && ./create-docs.sh && git add ./README.md git stash pop -q [ $RESULT -ne 0 ] && exit 1 exit 0 dash.el-2.12.1/rainbow-dash.png000066400000000000000000000221611261164447300162110ustar00rootroot00000000000000PNG  IHDRx:>tEXtSoftwareAdobe ImageReadyqe<"iTXtXML:com.adobe.xmp vPLTEmMXkfsOOek1ޅەvQ:}ꈶҵn3ĘqkW&DI^[hpUjc.~y謔/ieuPu`.կf2KMHxyf{˃ATlEgs{@)ȫc==ntuՒշCzEV噏t7nH|¾{>)!@8i{n6[}09w WD269ךc)ԲZ"|asc`I[}Lz/(>N~ɇFHGIDATxܜ\wM̤4 ^ĸIU"5XTJ#$Fo`Bh8J~J׳u mf =Zl-)vmSqYPvJvػM;Qk z^‹y>w&΋?e!n0᠎Pasv톉SoZ.ol=]=:uB s$:$q]t\ii]+Ľ!g=}rlw:(80uEoօq5euIk`'u\ #Qvv$fGB)nGR'PGJt<"p4 H 0 t8 I_Fo۴_ b8<seCY 3q(""-bG2IH7< E"1>$4Ў5Ŷh5BD  F J>;eO,K6b'%AZm84Zd\@BjklN[;"$=/"ㆈaaY҄y> #Fo  jMH1="Avތ Ŋ44%!0"׈~1>G,PʬP& yH"xn*77C XAn$7]$F,P el 7c1@"x;6DzÊYA92fMp"}m&v$w%`H1Dg_w0[09)d^CfuK'/ dBy> !8 JC*& ]/%pmmS8Uc@):YR>4`%j*=lꀿ?9{ٔvBD#.!5p(yCӎL(uh&i>5Na?+_Z޳J ZLMieGXo@!U Jg*%8yn@rF1L)uG@WаX~}A"Z}0ABha}YŴЋ 7a7獼q4tT4m$f[Z*fVdl˸?5>nm"4%_ӵBؘ_wW09@+ ־N^)hVb3YL& a x+:FxR ֶ?i=Aap :;0r |ԟx@a,6[qxA಩hA$Y79@}< +_] 8 h\}tzeZZJ+d[Lȟ f1q 2]o!f gaTovS*PDA=3L1ă|7CaC탤&(1z>/Q̲9u8Gp$G?g^m$ Èٶn?Pjs7xP˺xu4~ #gfe TP?+iu>M170w )(pbgr8m:?Xs\L]g$h&_mk+=¼XI8ۍp=iM[e47eX7 t댧Z >M0x]6E~#xC?~wGG:?Yx &mZ3Y.avSj%le,I~rɝfQGemU) ~FxFPw-K`wWV.]͆%f3"Qhk1Ls,44b$<[\qZ|mh‡ڇ_QQ}Oc7ToL{ a+QpSHs -YT` o(ǰn $ w1*z cW\#T0[mQcaTtWݮ3;E"beIg1X]A+xߧa2Px-:qQsiE .M8a,x$)rs"Q(^;b&;?_FehA1mBà0jU Th,FiTC9"іNFՂ:@ImF W V=GF lx*w0Rr%r(]Cz (ڲn u٬@#|-̏yi# #yW%Tn@rT`4\1w޳yw:8d-R 6PK;,%rgWna-]{O̴<5Ov B0ou% e޹'z' S}`e񬚞ȦFF 316VTRo4f$AU#+RSӟ<ŠDǩY'^wޱP6;PǏ=oإ:.I9^e^P ۜbC^l8*6 p-KWE5{kG hB/ۯO\ oo-f1H+=pl. y] [RZZ5taRmB%C^(/*,UM,#{7# lUdJvGQ[:̳N/O6< {8{H~v`.[|S8K% FlErMX\}bAX,i3eL_`BY[>%`@jeW-;o=>?|ĩwD{6霒K- &;))ۑA6rHbu?Wo4{}OlO~׀%=,`x:=s=km?{SltI-1&jFFev\ -`4}z_ffVdO>tӌaWn֩mO\z׬Y|٘zݏy`H ǚ /u T&GII huT%JQ0Y,Gb8+ 2 #VX>:~y[Gv>b Yt˝G2N.K2SJYDћbGgCcmE& (nqd*Kg,ErS li71TiQd*05=7-gC?߯n2G>pO'C)>}yTPryWFrs3<6m}z~ }6PN{7FZoZJDGƤ )v/m'WW[2r!+[wL(Ha &-74久C.Z>71h!hÛo%? +'J  `jJ!bܡ/If?(0Z2/W}>֯C)v %'kҙ-*HS!29`90NǒXܠ2ermi`O[;3sv<|Uvtu]8UVjʳi>峧bDԗ_ ;b4ZT%bM1@2N?Cɵ@kO{n"熊"~?B"H^)`푱W+$"~&i/+vQ,8E&o1Ԣj2;n_K8D"FvWwP׶+!:D Dcc^b⢏v,O ԤoKN~JGu '~Љ TŅE Tyd.zY(4H>~uxmMX=#UQ=I8m֓KK5 #{7->/>׭)7qiw}ށ&c4-M SAO_]4GaCd=~k,1S8m 3$\q`XuHcTW˼yWW*Q Hkٮ4H,, JC qjd*0 &Z]-X ^CQɁw6+JP=z,*Ƌ.?㸄%~NR1a  USN ԫ-Tm.izR:1O"Dǽ٪u6>F-XL+8ISdΰ|r&yuSW0*ILa,mpJ0Ĥ)O(l(O{]$|8D]2jz:5KH3A @9:z;_LPǢ0v+? hy~bbntYC)mUVBX>2Mɚ qrߏ-PW(MZmY?@IѠvNp;3ZP@z}#}NtC8.Y] D ʬLRpՅq[ |8[CxxK\ Sȕ‘aisprx`sgP'R%v)Gy:qAIUBURxP(n9΁jܮPY9CF2%t@87gɢ[S 2UU)e!kWzz(OY\L\Јm-Mkת@WEof 'b X&'}6L N)Lg8B", '=Vӫސr|{`2QGhyJԊuNeH K(!lV'eC9j(@ӟJ;T(j=cs Y\\Z3Sf\,mo@mnp`hDcҐ}VE␭_AJNӹzէZ)&ykz}:v$ (lΗT.áë(G7ުsTtQ4Wi=V΍\,Zm &ܭ!9^'+x,@ .d$&juW?*9):#JfӲP( 5g"K^mԟ7j7"Y;!;`NgnSn$!U#km/q#/ݜ)hcj'qąI4J՝Ur( >zS1d.:( F /!SKs:(S(/qW 霋Q,67й <XjszT*kteR K^? PxjCΫЇWw_K{Z;;9(ܾ֚v~68 "G%p7*y<>4G_ә×5֨s8ΞYNqEkj{QՃrо>)GoOpszDv dash.el [![Build Status](https://secure.travis-ci.org/magnars/dash.el.png)](http://travis-ci.org/magnars/dash.el) A modern list api for Emacs. No 'cl required. ## Installation It's available on [marmalade](http://marmalade-repo.org/) and [Melpa](http://melpa.milkbox.net/): M-x package-install dash Or you can just dump `dash.el` in your load path somewhere. If you want the function combinators, then also: M-x package-install dash-functional ## Using in a package Add this to the big comment block at the top: ;; Package-Requires: ((dash "2.12.1")) To get function combinators: ;; Package-Requires: ((dash "2.12.1") (dash-functional "1.2.0") (emacs "24")) ## Upcoming breaking change! - For backward compatibility reasons `-zip` return a cons-cell instead of a list with two elements when called on two lists. This is a clunky API, and in an upcoming 3.0 release of Dash it will always return a list. If you rely on the cons-cell return value, use `-zip-pair` instead. ## Syntax highlighting of dash functions Font lock of dash functions in emacs lisp buffers is now optional. Include this in your emacs settings to get syntax highlighting: (eval-after-load "dash" '(dash-enable-font-lock)) ## Functions All functions and constructs in the library are prefixed with a dash (-). There are also anaphoric versions of functions where that makes sense, prefixed with two dashes instead of one. While `-map` takes a function to map over the list, you can also use the anaphoric form with double dashes - which will then be executed with `it` exposed as the list item. Here's an example: ```el (-map (lambda (n) (* n n)) '(1 2 3 4)) ;; normal version (--map (* it it) '(1 2 3 4)) ;; anaphoric version ``` of course the original can also be written like ```el (defun square (n) (* n n)) (-map 'square '(1 2 3 4)) ``` which demonstrates the usefulness of both versions. [[ function-list ]] [[ function-docs ]] ## Contribute Yes, please do. Pure functions in the list manipulation realm only, please. There's a suite of tests in `dev/examples.el`, so remember to add tests for your function, or I might break it later. You'll find the repo at: https://github.com/magnars/dash.el Run the tests with ./run-tests.sh Create the docs with ./create-docs.sh I highly recommend that you install these as a pre-commit hook, so that the tests are always running and the docs are always in sync: cp pre-commit.sh .git/hooks/pre-commit Oh, and don't edit `README.md` directly, it is auto-generated. Change `readme-template.md` or `examples-to-docs.el` instead. ## Changelist - Added lexical binding pragma to dash.el ### From 2.11 to 2.12 - Add GNU ELPA support. (Phillip Lord) - Add `-some->`, `-some->>`, and `-some-->` macros. (Cam Saul) - `-is-suffix?` no longer destroys input list. - Faster hashtable implementation for `-union`. - Improvements to docstrings and examples ### From 2.10 to 2.11 - Lots of clean up wrt byte compilation, debug macros and tests ### From 2.9 to 2.10 - Add `-let` destructuring to `-if-let` and `-when-let` (Fredrik Bergroth) ### From 2.8 to 2.9 - Add `-let`, `-let*` and `-lambda` with destructuring - Add `-tree-seq` and `-tree-map-nodes` - Add `-non-nil` - Add `-fix` - Add `-fixfn` (dash-functional 1.2) - Add `-copy` (Wilfred Hughes) ### From 2.7 to 2.8 - Add `-butlast` ### From 2.6 to 2.7 - `-zip` now supports more than two lists (Steve Lamb) - Add `-cycle` , `-pad` , `-annotate` , `-zip-fill` (Steve Lamb) - Add `-table`, `-table-flat` (finite cartesian product) - Add `-flatten-n` - `-slice` now supports "step" argument - Add functional combinators `-iteratefn`, `-prodfn` - Add `-replace`, `-splice`, `-splice-list` which generalize `-replace-at` and `-insert-at` - Add `-compose`, `-iteratefn` and `-prodfn` (dash-functional 1.1) ### From 2.5 to 2.6 - Add `-is-prefix-p`, `-is-suffix-p`, `-is-infix-p` (Matus Goljer) - Add `-iterate`, `-unfold` (Matus Goljer) - Add `-split-on`, `-split-when` (Matus Goljer) - Add `-find-last-index` (Matus Goljer) - Add `-list` (Johan Andersson) ### From 2.4 to 2.5 - Add `-same-items?` (Johan Andersson) - A few bugfixes ### From 2.3 to 2.4 - Add `-snoc` (Matus Goljer) - Add `-replace-at`, `-update-at`, `-remove-at`, and `-remove-at-indices` (Matus Goljer) ### From 2.2 to 2.3 - Add tree operations (Matus Goljer) - Make font-lock optional ### From 2.1 to 2.2 - Add `-compose` (Christina Whyte) ### From 2.0 to 2.1 - Add indexing operations (Matus Goljer) ### From 1.8 to 2.0 - Split out `dash-functional.el` (Matus Goljer) - Add `-andfn`, `-orfn`, `-not`, `-cut`, `-const`, `-flip` and `-on`. (Matus Goljer) - Fix `-min`, `-max`, `-min-by` and `-max-by` (Matus Goljer) ### From 1.7 to 1.8 - Add `-first-item` and `-last-item` (Wilfred Hughes) ### From 1.6 to 1.7 - Add `-rotate` (Matus Goljer) ### From 1.5 to 1.6 - Add `-min`, `-max`, `-min-by` and `-max-by` (Johan Andersson) ### From 1.4 to 1.5 - Add `-sum` and `-product` (Johan Andersson) ### From 1.3 to 1.4 - Add `-sort` - Add `-reduce-r` (Matus Goljer) - Add `-reduce-r-from` (Matus Goljer) ### From 1.2 to 1.3 - Add `-partition-in-steps` - Add `-partition-all-in-steps` ### From 1.1 to 1.2 - Add `-last` (Matus Goljer) - Add `-insert-at` (Emanuel Evans) - Add `-when-let` and `-if-let` (Emanuel Evans) - Add `-when-let*` and `-if-let*` (Emanuel Evans) - Some bugfixes ## Contributors - [Matus Goljer](https://github.com/Fuco1) contributed lots of features and functions. - [Takafumi Arakaki](https://github.com/tkf) contributed `-group-by`. - [tali713](https://github.com/tali713) is the author of `-applify`. - [Víctor M. Valenzuela](https://github.com/vemv) contributed `-repeat`. - [Nic Ferrier](https://github.com/nicferrier) contributed `-cons*`. - [Wilfred Hughes](https://github.com/Wilfred) contributed `-slice`, `-first-item` and `-last-item`. - [Emanuel Evans](https://github.com/shosti) contributed `-if-let`, `-when-let` and `-insert-at`. - [Johan Andersson](https://github.com/rejeep) contributed `-sum`, `-product` and `-same-items?` - [Christina Whyte](https://github.com/kurisuwhyte) contributed `-compose` - [Steve Lamb](https://github.com/steventlamb) contributed `-cycle`, `-pad`, `-annotate`, `-zip-fill` and an n-ary version of `-zip`. - [Fredrik Bergroth](https://github.com/fbergroth) made the `-if-let` family use `-let` destructuring and improved script for generating documentation. - [Mark Oteiza](https://github.com/holomorph) contributed the script to create an info manual. - [Vasilij Schneidermann](https://github.com/wasamasa) contributed `-some`. - [William West](https://github.com/occidens) made `-fixfn` more robust at handling floats. - [Cam Saül](https://github.com/cammsaul) contributed `-some->`, `-some->>`, and `-some-->`. Thanks! ## License Copyright (C) 2012-2014 Magnar Sveen Authors: Magnar Sveen Keywords: lists This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . dash.el-2.12.1/run-tests.sh000077500000000000000000000014021261164447300154230ustar00rootroot00000000000000#!/usr/bin/env bash set -e if [ -z "$EMACS" ] ; then EMACS="emacs" fi # Run all tests by default. # To only run certain tests, set $ERT_SELECTOR as required. # For example, to skip the test "-fixfn", run the following command: # # ERT_SELECTOR='(not "-fixfn")' ./run-tests.sh # if [ -z "$ERT_SELECTOR" ] ; then ERT_SELECTOR="nil" fi $EMACS -batch \ $([[ $EMACS == "emacs23" ]] && echo -l dev/ert.el) \ -l dash.el \ -l dash-functional.el \ -l dev/examples-to-tests.el \ -l dev/examples.el \ --eval "(ert-run-tests-batch-and-exit (quote ${ERT_SELECTOR}))" if [[ $EMACS != "emacs23" ]]; then $EMACS -Q --batch \ --eval '(setq byte-compile-error-on-warn t)' \ -f batch-byte-compile dash.el fi dash.el-2.12.1/run-travis-ci.sh000077500000000000000000000003531261164447300161660ustar00rootroot00000000000000#!/bin/sh cd "$(dirname "$0")" set_default () { eval " if [ -z \$$1 ]; then $1=$2 fi " } set_default EMACS "$(which emacs)" echo "*** Emacs version ***" echo "EMACS =" $(which $EMACS) $EMACS --version echo exec ./run-tests.sh dash.el-2.12.1/watch-tests.watchr000066400000000000000000000011441261164447300166030ustar00rootroot00000000000000ENV["WATCHR"] = "1" system 'clear' def run(cmd) `#{cmd}` end def run_all_tests system('clear') result = run "./run-tests.sh" puts result end run_all_tests watch('.*.el') { run_all_tests } # Ctrl-\ Signal.trap 'QUIT' do puts " --- Running all tests ---\n\n" run_all_tests end @interrupted = false # Ctrl-C Signal.trap 'INT' do if @interrupted then @wants_to_quit = true abort("\n") else puts "Interrupt a second time to quit" @interrupted = true Kernel.sleep 1.5 # raise Interrupt, nil # let the run loop catch it run_all_tests @interrupted = false end end