├── .gitignore ├── bench ├── utils.lisp ├── unfolding.lisp ├── zipping.lisp ├── infinite.lisp ├── scans.lisp ├── transformations.lisp ├── basic.lisp ├── accumulating.lisp ├── indexing-lists.lisp ├── reducing-folds.lisp ├── predicates.lisp ├── searching-lists.lisp ├── special-folds.lisp └── extracting-sublists.lisp ├── .travis.yml ├── listopia.asd ├── listopia-bench.ros ├── listopia-bench.asd ├── tests └── listopia.lisp ├── src └── listopia.lisp └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.FASL 2 | *.fasl 3 | *.lisp-temp 4 | *.dfsl 5 | *.pfsl 6 | *.d64fsl 7 | *.p64fsl 8 | *.lx64fsl 9 | *.lx32fsl 10 | *.dx64fsl 11 | *.dx32fsl 12 | *.fx64fsl 13 | *.fx32fsl 14 | *.sx64fsl 15 | *.sx32fsl 16 | *.wx64fsl 17 | *.wx32fsl 18 | -------------------------------------------------------------------------------- /bench/utils.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.utils 2 | (:use :cl) 3 | (:export :bench)) 4 | (in-package :listopia-bench.utils) 5 | 6 | 7 | (defmacro bench (title &body forms) 8 | `(progn 9 | (format t "~%Benchmarking: ~S" ,title) 10 | (time 11 | (benchmark:with-timing (1000) 12 | ,@forms)) 13 | t)) 14 | -------------------------------------------------------------------------------- /bench/unfolding.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.unfolding 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :unfoldr)) 7 | 8 | (in-package :listopia-bench.unfolding) 9 | 10 | 11 | (plan nil) 12 | 13 | (ok (bench "unfoldr" (unfoldr 14 | (lambda (x) (if (= x 0) nil (list x (1- x)))) 15 | 5))) 16 | 17 | (finalize) 18 | -------------------------------------------------------------------------------- /bench/zipping.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.zipping 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :zip 7 | :zip-with 8 | :unzip)) 9 | 10 | (in-package :listopia-bench.zipping) 11 | 12 | 13 | (plan nil) 14 | 15 | 16 | (ok (bench "zip" (zip '(1 2 3) '(4 5 6) '(7 8 9)))) 17 | (ok (bench "zip-with" (zip-with #'list '(1 2 3) '(4 5 6) '(7 8 9)))) 18 | (ok (bench "unzip" (unzip '((1 2 3) (4 5 6) (7 8 9))))) 19 | 20 | 21 | (finalize) 22 | -------------------------------------------------------------------------------- /bench/infinite.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.infinite 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :iterate 7 | :repeat 8 | :replicate 9 | :cycle)) 10 | 11 | (in-package :listopia-bench.infinite) 12 | 13 | 14 | (plan nil) 15 | 16 | (ok (bench "iterate" (iterate #'1+ 0))) 17 | (ok (bench "repeat" (repeat 1))) 18 | (ok (bench "replicate" (replicate 4 1))) 19 | (ok (bench "cycle" (cycle '(1 2 3)))) 20 | 21 | (finalize) 22 | -------------------------------------------------------------------------------- /bench/scans.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.scans 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :scanl 7 | :scanl1 8 | :scanr 9 | :scanr1)) 10 | 11 | (in-package :listopia-bench.scans) 12 | 13 | 14 | (plan nil) 15 | 16 | (ok (bench "scanl" (scanl #'+ 1 '(1 2 3)))) 17 | (ok (bench "scanl1" (scanl1 #'+ '(1 2 3)))) 18 | (ok (bench "scanr" (scanr #'+ 1 '(1 2 3)))) 19 | (ok (bench "scanr1" (scanr1 #'+ '(1 2 3)))) 20 | 21 | (finalize) 22 | -------------------------------------------------------------------------------- /bench/transformations.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.transformations 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :intersperse 7 | :intercalate) 8 | (:shadowing-import-from :listopia :map)) 9 | 10 | (in-package :listopia-bench.transformations) 11 | 12 | 13 | (plan nil) 14 | 15 | (ok (bench "map" (map #'1+ '(1 2 3)))) 16 | (ok (bench "intersperse" (intersperse 0 '(1 2 3)))) 17 | (ok (bench "intercalate" (intercalate '(0) '((1) (2) (3))))) 18 | 19 | (finalize) 20 | -------------------------------------------------------------------------------- /bench/basic.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.basic 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :head 7 | :tail 8 | :init 9 | :uncons) 10 | (:shadowing-import-from :listopia :last)) 11 | 12 | (in-package :listopia-bench.basic) 13 | 14 | 15 | (plan nil) 16 | 17 | (ok (bench "head" (head '(1 2 3)))) 18 | (ok (bench "last" (last '(1 2 3)))) 19 | (ok (bench "tail" (tail '(1 2 3)))) 20 | (ok (bench "init" (init '(1 2 3)))) 21 | (ok (bench "uncons" (uncons '(1 2 3)))) 22 | 23 | (finalize) 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: common-lisp 2 | sudo: false 3 | 4 | env: 5 | global: 6 | - PATH=$HOME/.roswell/bin:$PATH 7 | - ROSWELL_INSTALL_DIR=$HOME/.roswell 8 | - COVERAGE_EXCLUDE=t 9 | matrix: 10 | - LISP=sbcl-bin 11 | 12 | install: 13 | # Install Roswell 14 | - curl -L https://raw.githubusercontent.com/snmsts/roswell/release/scripts/install-for-ci.sh | sh 15 | - ros install fukamachi/rove 16 | 17 | cache: 18 | directories: 19 | - $HOME/.roswell 20 | - $HOME/.config/common-lisp 21 | 22 | before_script: 23 | - ros --version 24 | - ros config 25 | 26 | script: 27 | - rove listopia.asd 28 | -------------------------------------------------------------------------------- /bench/accumulating.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.accumulating 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :map-accum-l 7 | :map-accum-r)) 8 | 9 | (in-package :listopia-bench.accumulating) 10 | 11 | 12 | (plan nil) 13 | 14 | (ok (bench "map-accum-l" 15 | (map-accum-l 16 | (lambda (acc x) (list acc (+ x acc))) 17 | 1 18 | '(1 2 3)))) 19 | 20 | (ok (bench "map-accum-r" 21 | (map-accum-r 22 | (lambda (acc x) (list acc (+ x acc))) 23 | 1 24 | '(1 2 3)))) 25 | 26 | (finalize) 27 | -------------------------------------------------------------------------------- /bench/indexing-lists.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.indexing-lists 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :elem-index 7 | :find-index 8 | :find-indices 9 | :elem-indices)) 10 | (in-package :listopia-bench.indexing-lists) 11 | 12 | 13 | (plan nil) 14 | 15 | (ok (bench "elem-index" (elem-index 2 '(1 2 3)))) 16 | (ok (bench "find-index" (find-index #'keywordp '(1 :foo 3)))) 17 | (ok (bench "find-indices" (find-indices #'keywordp '(1 :foo 3 :bar)))) 18 | (ok (bench "elem-indices" (elem-indices 42 '(1 42 3 42)))) 19 | 20 | (finalize) 21 | -------------------------------------------------------------------------------- /bench/reducing-folds.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.reducing-folds 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :foldl 7 | :foldl1 8 | :foldr 9 | :foldr1)) 10 | 11 | (in-package :listopia-bench.reducing-folds) 12 | 13 | 14 | (plan nil) 15 | 16 | (ok (bench "foldl" (foldl (lambda (acc x) (cons (1+ x) acc)) nil '(1 2 3)))) 17 | (ok (bench "foldl1" (foldl1 (lambda (acc x) (cons (1+ x) acc)) '(1 2 3)))) 18 | (ok (bench "foldr" (foldr (lambda (x acc) (cons (1+ x) acc)) nil '(1 2 3)))) 19 | (ok (bench "foldr1" (foldr1 (lambda (x acc) (cons (1+ x) acc)) '(1 2 3)))) 20 | 21 | (finalize) 22 | -------------------------------------------------------------------------------- /listopia.asd: -------------------------------------------------------------------------------- 1 | (defsystem "listopia" 2 | :version "0.12.0" 3 | :author "Ito Dimercel" 4 | :license "LLGPL" 5 | :depends-on () 6 | :components ((:module "src" 7 | :components 8 | ((:file "listopia")))) 9 | :description "List manipulation library" 10 | :long-description 11 | #.(read-file-string 12 | (subpathname *load-pathname* "README.md")) 13 | :in-order-to ((test-op (test-op "listopia/tests")))) 14 | 15 | (defsystem "listopia/tests" 16 | :class :package-inferred-system 17 | :depends-on ("listopia" 18 | "rove") 19 | :components ((:module "tests" 20 | :components 21 | ((:file "listopia")))) 22 | :perform (test-op (op c) (symbol-call :rove '#:run c))) 23 | -------------------------------------------------------------------------------- /bench/predicates.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.predicates 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :is-prefix-of 7 | :is-suffix-of 8 | :is-infix-of 9 | :is-subsequence-of)) 10 | (in-package :listopia-bench.predicates) 11 | 12 | 13 | (plan nil) 14 | 15 | 16 | (ok (bench "is-prefix-of" 17 | (is-prefix-of '(1 2 3) '(1 2 3 4 5)))) 18 | 19 | (ok (bench "is-suffix-of" 20 | (is-suffix-of '(3 2 1) '(5 4 3 2 1)))) 21 | 22 | (ok (bench "is-infix-of" 23 | (is-infix-of '(1 2) '(3 3 1 2 3 3)))) 24 | 25 | (ok (bench "is-subsequence-of" 26 | (is-subsequence-of '(1 2 3) '(1 0 2 0 3 0)))) 27 | 28 | (finalize) 29 | -------------------------------------------------------------------------------- /bench/searching-lists.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.searching-lists 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :elem 7 | :not-elem 8 | :filter 9 | :partition) 10 | (:shadowing-import-from :listopia :find)) 11 | 12 | (in-package :listopia-bench.searching-lists) 13 | 14 | 15 | (plan nil) 16 | 17 | 18 | (ok (bench "elem" 19 | (elem 1 '(1 2 3)))) 20 | 21 | (ok (bench "not-elem" 22 | (not-elem 1 '(2 3 4)))) 23 | 24 | (ok (bench "find" 25 | (find #'numberp '(:foo :bar 1)))) 26 | 27 | (ok (bench "filter" 28 | (filter #'numberp '(:foo 1 :bar 2)))) 29 | 30 | (ok (bench "partition" 31 | (partition #'numberp '(:foo 1 :bar 2)))) 32 | 33 | (finalize) 34 | -------------------------------------------------------------------------------- /bench/special-folds.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.special-folds 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :concat 7 | :concat-map 8 | :any 9 | :all 10 | :sum 11 | :product 12 | :maximum 13 | :minimum) 14 | (:shadowing-import-from :listopia :and :or)) 15 | 16 | (in-package :listopia-bench.special-folds) 17 | 18 | 19 | (plan nil) 20 | 21 | (ok (bench "concat" (concat '((1) (1 2) (1 2 3))))) 22 | (ok (bench "concat-map" (concat-map #'list '(1 2 3)))) 23 | (ok (bench "and" (and '(t nil t)))) 24 | (ok (bench "or" (or '(t nil t)))) 25 | (ok (bench "any" (any #'numberp '("1" "2" 3)))) 26 | (ok (bench "all" (all #'numberp '(1 2 3)))) 27 | (ok (bench "sum" (sum '(1 2 3)))) 28 | (ok (bench "product" (product '(1 2 3)))) 29 | (ok (bench "maximum" (maximum '(1 2 3)))) 30 | (ok (bench "minimum" (minimum '(1 2 3)))) 31 | 32 | (finalize) 33 | -------------------------------------------------------------------------------- /listopia-bench.ros: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #|-*- mode:lisp -*-|# 3 | #| 4 | exec ros -Q -- $0 "$@" 5 | |# 6 | (progn ;;init forms 7 | (ros:ensure-asdf) 8 | ;;#+quicklisp (ql:quickload '() :silent t) 9 | ) 10 | 11 | (defpackage :ros.script.listopia-bench.3740831638 12 | (:use :cl)) 13 | (in-package :ros.script.listopia-bench.3740831638) 14 | 15 | (ql:quickload :listopia-bench :silent t) 16 | 17 | (defun main (&rest argv) 18 | (declare (ignorable argv)) 19 | (prove:run #P"bench/accumulating.lisp") 20 | (prove:run #P"bench/basic.lisp") 21 | (prove:run #P"bench/extracting-sublists.lisp") 22 | (prove:run #P"bench/indexing-lists.lisp") 23 | (prove:run #P"bench/infinite.lisp") 24 | (prove:run #P"bench/predicates.lisp") 25 | (prove:run #P"bench/reducing-folds.lisp") 26 | (prove:run #P"bench/scans.lisp") 27 | (prove:run #P"bench/searching-lists.lisp") 28 | (prove:run #P"bench/special-folds.lisp") 29 | (prove:run #P"bench/transformations.lisp") 30 | (prove:run #P"bench/unfolding.lisp") 31 | (prove:run #P"bench/zipping.lisp")) 32 | ;;; vim: set ft=lisp lisp: 33 | -------------------------------------------------------------------------------- /bench/extracting-sublists.lisp: -------------------------------------------------------------------------------- 1 | (defpackage listopia-bench.extracting-sublists 2 | (:use :cl 3 | :prove 4 | :listopia-bench.utils) 5 | (:import-from :listopia 6 | :take 7 | :drop 8 | :split-at 9 | :take-while 10 | :drop-while 11 | :drop-while-end 12 | :span 13 | :strip-prefix 14 | :inits 15 | :tails) 16 | (:shadowing-import-from :listopia :break)) 17 | 18 | (in-package :listopia-bench.extracting-sublists) 19 | 20 | 21 | (plan nil) 22 | 23 | (ok (bench "take" (take 2 '(1 2 3)))) 24 | (ok (bench "drop" (drop 3 '(1 2 3 4 5)))) 25 | (ok (bench "split-at" (split-at 3 '(1 2 3 4 5)))) 26 | (ok (bench "take-while" (take-while #'evenp '(1 2 3 4)))) 27 | (ok (bench "drop-while" (drop-while #'numberp '(1 nil 3)))) 28 | (ok (bench "drop-while-end" (drop-while-end #'numberp '(nil nil 3)))) 29 | (ok (bench "span" (span #'evenp '(2 4 1 3)))) 30 | (ok (bench "break" (break #'evenp '(2 4 1 3)))) 31 | (ok (bench "strip-prefix" (strip-prefix '(1 2) '(1 2 3 4)))) 32 | (ok (bench "inits" (inits '(1 2 3)))) 33 | (ok (bench "tails" (tails '(1 2 3)))) 34 | 35 | (finalize) 36 | -------------------------------------------------------------------------------- /listopia-bench.asd: -------------------------------------------------------------------------------- 1 | #| 2 | This file is a part of listopia project. 3 | Copyright (c) 2019 Ito Dimercel (xolcman@gmail.com) 4 | |# 5 | 6 | (defsystem "listopia-bench" 7 | :defsystem-depends-on ("prove-asdf") 8 | :author "Ito Dimercel" 9 | :license "LLGPL" 10 | :depends-on ("listopia" 11 | "prove" 12 | "trivial-benchmark") 13 | :components ((:module "bench" 14 | :components 15 | ((:file "utils") 16 | (:test-file "basic") 17 | (:test-file "transformations") 18 | (:test-file "reducing-folds") 19 | (:test-file "special-folds") 20 | (:test-file "scans") 21 | (:test-file "accumulating") 22 | (:test-file "infinite") 23 | (:test-file "unfolding") 24 | (:test-file "extracting-sublists") 25 | (:test-file "predicates") 26 | (:test-file "searching-lists") 27 | (:test-file "indexing-lists") 28 | (:test-file "zipping") 29 | ))) 30 | :description "Benchmark system for listopia" 31 | 32 | :perform (test-op (op c) (symbol-call :prove-asdf :run-test-system c :reporter :dot))) 33 | -------------------------------------------------------------------------------- /tests/listopia.lisp: -------------------------------------------------------------------------------- 1 | (defpackage #:listopia/tests 2 | (:use #:cl 3 | #:listopia 4 | #:rove) 5 | (:shadowing-import-from #:listopia 6 | #:and 7 | #:break 8 | #:find 9 | #:last 10 | #:map 11 | #:or)) 12 | 13 | (in-package #:listopia/tests) 14 | 15 | 16 | (deftest general-cases 17 | (testing "Basic functions" 18 | (ok (eql nil (head '()))) 19 | (ok (eql 666 (head '() 666))) 20 | (ok (eql 1 (head '(1)))) 21 | 22 | 23 | (ok (eql nil (last '()))) 24 | (ok (eql 666 (last '() 666))) 25 | (ok (eql 1 (last '(1)))) 26 | 27 | 28 | (ok (eql nil (tail '()))) 29 | (ok (eql 666 (tail '() 666))) 30 | (ok (eql nil (tail '(1)))) 31 | 32 | 33 | (ok (eql nil (init '()))) 34 | (ok (eql 666 (init '() 666))) 35 | (ok (eql nil (init '(1)))) 36 | 37 | 38 | (ok (eql nil (uncons '()))) 39 | (ok (eql 666 (uncons '() 666))) 40 | (ok (eql nil (uncons '(1)))) 41 | (ok (eql 2 (length (uncons '(1 2 3)))))) 42 | 43 | (testing "List transformations" 44 | (ok (eql '() (map #'1+ '()))) 45 | (ok (equal '(2 3 4) (map #'1+ '(1 2 3)))) 46 | 47 | (ok (eql '() (intersperse 0 '()))) 48 | (ok (equal '(1) (intersperse 0 '(1)))) 49 | (ok (eql 5 (length (intersperse #\, '(#\f #\o \#o))))) 50 | 51 | (ok (eql '() (intercalate '(0) '()))) 52 | (ok (eql '(1) (intercalate '(0) '((1))))) 53 | (ok (eql 3 (length (intercalate '(" ") '(("foo") ("bar"))))))) 54 | 55 | 56 | (testing "Reducing lists(folds)" 57 | (ok (eql '() (foldl #'null '() '()))) 58 | (ok (eql '(1 2) (foldl #'null '(1 2) '()))) 59 | (ok (eql 5 (length (foldl (lambda (acc x) 60 | (cons (1+ x) acc)) 61 | '(2 1) 62 | '(2 3 4))))) 63 | 64 | (ok (signals (foldl1 #'+ '()) 'simple-error)) 65 | (ok (numberp (foldl1 #'+ '(1 2 3)))) 66 | (ok (eql (foldl #'- 1 '(2 3)) (foldl1 #'- '(1 2 3)))) 67 | 68 | (ok (eql '() (foldr #'null '() '()))) 69 | (ok (eql '(1 2) (foldr #'null '(1 2) '()))) 70 | (ok (eql 5 (length (foldr (lambda (x acc) 71 | (cons (1+ x) acc)) 72 | '(2 1) 73 | '(4 3 2))))) 74 | 75 | (ok (signals (foldr1 #'+ '()) 'simple-error)) 76 | (ok (numberp (foldr1 #'+ '(1 2 3)))) 77 | (ok (eql (foldr #'- 3 '(1 2)) (foldr1 #'- '(1 2 3))))) 78 | 79 | 80 | (testing "Special folds" 81 | (ok (eql 6 (length (concat '((1) (2 3) (4 5 6)))))) 82 | (ok (eql '() (concat '(() () ())))) 83 | 84 | (ok (eql 3 (length (concat-map #'list '(1 2 3))))) 85 | (ok (eql '() (concat-map #'list '()))) 86 | 87 | (ok (and '(t t t))) 88 | (ok (not (and '(nil nil)))) 89 | (ok (and '())) 90 | 91 | (ok (not (or '(nil nil)))) 92 | (ok (not (or '()))) 93 | 94 | (ok (any #'numberp '(1 2 3))) 95 | (ok (any #'numberp '("1" "2" 3))) 96 | (ok (not (any #'numberp '("1" "2")))) 97 | (ok (not (any #'numberp '()))) 98 | 99 | (ok (all #'numberp '(1 2 3))) 100 | (ok (not (all #'numberp '(1 "2")))) 101 | (ok (not (all #'numberp '("1" "2")))) 102 | (ok (all #'numberp '())) 103 | 104 | (ok (eql 0 (sum '()))) 105 | (ok (eql 7 (sum '(7)))) 106 | 107 | (ok (eql 1 (product '()))) 108 | (ok (eql 7 (product '(7)))) 109 | 110 | (ok (signals (maximum '()) 'error)) 111 | (ok (eql 7 (maximum '(7)))) 112 | 113 | (ok (signals (minimum '()) 'error)) 114 | (ok (eql 7 (minimum '(7))))) 115 | 116 | 117 | (testing "Scans" 118 | (ok (equal '(1) (scanl #'+ 1 '()))) 119 | (ok (eql 4 (length (scanl #'+ 1 '(2 3 4))))) 120 | 121 | (ok (eql '() (scanl1 #'+ '()))) 122 | (ok (eql 4 (length (scanl1 #'+ '(1 2 3 4))))) 123 | 124 | (ok (equal '(1) (scanr #'+ 1 '()))) 125 | (ok (eql 4 (length (scanr #'+ 1 '(2 3 4))))) 126 | 127 | (ok (eql '() (scanr1 #'+ '()))) 128 | (ok (eql 4 (length (scanr1 #'+ '(1 2 3 4)))))) 129 | 130 | 131 | (testing "Accumulating maps" 132 | (ok (equal '(1 nil) (map-accum-l #'null 1 '()))) 133 | (ok (eql 2 (length (map-accum-l (lambda (acc x) 134 | (list acc (+ acc x))) 135 | 1 136 | '(2 3 4))))) 137 | 138 | (ok (equal '(1 nil) (map-accum-r #'null 1 '()))) 139 | (ok (eql 2 (length (map-accum-r (lambda (acc x) 140 | (list acc (+ acc x))) 141 | 1 142 | '(2 3 4)))))) 143 | 144 | 145 | (testing "Infinite lists" 146 | (ok (eql 4 (length (take 4 (iterate #'1+ 1))))) 147 | 148 | (ok (eql 4 (length (take 4 (repeat 1))))) 149 | 150 | (ok (equal '() (replicate 0 1))) 151 | (ok (eql 4 (length (replicate 4 1)))) 152 | 153 | (ok (signals (cycle '()) 'error)) 154 | (ok (eql 5 (length (take 5 (cycle '(1 2 3))))))) 155 | 156 | 157 | (testing "Unfolding" 158 | (ok (equal (take 4 (iterate #'1+ 0)) (unfoldr (lambda (x) 159 | (if (< x 4) 160 | (list x (1+ x)))) 161 | 0))) 162 | (ok (equal '() (unfoldr (lambda (x) (declare (ignore x)) 163 | nil) 0)))) 164 | 165 | 166 | 167 | (testing "Extracting sublists" 168 | (ok (equal '() (take 0 '(1 2 3)))) 169 | (ok (equal '() (take -1 '(1 2 3)))) 170 | (ok (eql 4 (length (take 10 '(1 2 3 4))))) 171 | (ok (eql 4 (length (take 4 (cycle '(1 2)))))) 172 | (ok (eql 4 (length (take 4 (iterate #'identity nil))))) 173 | 174 | (ok (equal '() (drop 3 '(1 2)))) 175 | (ok (equal '() (drop 3 '()))) 176 | (ok (equal '(1 2) (drop -1 '(1 2)))) 177 | (ok (equal '(1 2) (drop 0 '(1 2)))) 178 | 179 | (ok (eql 2 (length (split-at 3 '(1 2 3 4 5))))) 180 | (ok (equal (split-at 3 '(1 2 3)) (split-at 5 '(1 2 3)))) 181 | (ok (equal (split-at 0 '(1 2 3)) (split-at -1 '(1 2 3)))) 182 | 183 | (ok (eql 2 (length (take-while #'null '(nil nil 1 2 nil))))) 184 | (ok (equal '() (take-while #'null '()))) 185 | 186 | (ok (eql 2 (length (drop-while #'numberp '(1 2 3 "foo" "bar"))))) 187 | (ok (equal '() (drop-while #'numberp '(1 2 3)))) 188 | 189 | (ok (eql 2 (length (drop-while-end #'numberp '("foo" "bar" 1 2 3))))) 190 | (ok (equal '() (drop-while-end #'numberp '(1 2 3)))) 191 | 192 | (ok (eql 2 (length (span #'numberp '(1 2 "three" "four"))))) 193 | (ok (equal '(nil nil) (span #'numberp '()))) 194 | (ok (equal (list (take-while #'numberp '(1 2 "three" "four")) 195 | (drop-while #'numberp '(1 2 "three" "four"))) 196 | (span #'numberp '(1 2 "three" "four")))) 197 | 198 | (ok (eql 2 (length (break #'numberp '(1 2 "three" "four"))))) 199 | (ok (equal '(nil nil) (break #'numberp '()))) 200 | 201 | (ok (eql 3 (length (strip-prefix '(1 2) '(1 2 3 4 5))))) 202 | (ok (eql 42 (strip-prefix '(1 2) '() 42))) 203 | (ok (eql 3 (length (strip-prefix '() '(1 2 3))))) 204 | 205 | (ok (eql 4 (length (inits '(1 2 3))))) 206 | (ok (equal '(nil) (inits '()))) 207 | (ok (null (first (inits '(1 2 3))))) 208 | 209 | (ok (eql 4 (length (tails '(1 2 3))))) 210 | (ok (equal '(nil) (tails '()))) 211 | (ok (null (last (tails '(1 2 3)))))) 212 | 213 | 214 | (testing "Predicates" 215 | (ok (is-prefix-of '(1 2) '(1 2 3))) 216 | (ok (is-prefix-of '() '(1 2 3))) 217 | (ok (not (is-prefix-of '(1 2) '()))) 218 | (ok (is-prefix-of '() '())) 219 | 220 | (ok (is-suffix-of '(2 1) '(3 2 1))) 221 | (ok (is-suffix-of '() '(1 2 3))) 222 | (ok (not (is-suffix-of '(1 2) '()))) 223 | (ok (is-suffix-of '() '())) 224 | 225 | (ok (is-infix-of '(1 2) '(3 4 1 2 3 4))) 226 | (ok (is-infix-of '() '(1 2 3))) 227 | (ok (not (is-infix-of '(1 2) '()))) 228 | (ok (is-infix-of '() '())) 229 | 230 | (ok (is-subsequence-of '(1 2 3) '(1 0 2 0 3 0))) 231 | (ok (is-subsequence-of '() '(1 2 3))) 232 | (ok (not (is-subsequence-of '(1 2 3) '())))) 233 | 234 | 235 | (testing "Searching by equality" 236 | (ok (elem 1 '(1 2 3))) 237 | (ok (not (elem 1 '()))) 238 | 239 | (ok (not-elem :foo '(:bar 1 2))) 240 | (ok (not (not-elem 1 '(1 2 3))))) 241 | 242 | 243 | (testing "Searching with a predicate" 244 | (ok (eql 666 (find #'numberp '(:foo :bar) 666))) 245 | (ok (eql nil (find #'numberp '(:foo bar)))) 246 | 247 | (ok (eql 3 (length (filter #'numberp '(:foo 1 :bar 2 3))))) 248 | (ok (null (filter #'numberp '(:foo :bar)))) 249 | (ok (null (filter #'numberp '()))) 250 | 251 | (ok (eql 2 (length (partition #'numberp '(:foo 1 :bar 2 3))))) 252 | (ok (null (first (partition #'numberp '(:foo :bar))))) 253 | (ok (null (second (partition #'keywordp '(:foo :bar)))))) 254 | 255 | 256 | (testing "Indexing lists" 257 | (ok (eql nil (elem-index 0 '(1 2 3)))) 258 | (ok (eql 42 (elem-index 0 '(1 2 3) 42))) 259 | 260 | (ok (eql nil (find-index #'keywordp '(1 2 3)))) 261 | (ok (eql 42 (find-index #'keywordp '(1 2 3) 42))) 262 | 263 | (ok (eql 0 (length (find-indices #'keywordp '(1 2 3))))) 264 | (ok (not (= 0 (length (find-indices #'keywordp '(1 :foo 3 :bar)))))) 265 | (ok (equal '(0 2) (find-indices #'null '(nil 1 nil 2 3)))) 266 | 267 | (ok (eql 0 (length (elem-indices 42 '(1 2 3))))) 268 | (ok (not (= 0 (length (elem-indices 42 '(1 42 3 42)))))) 269 | (ok (equal '(1 3) (elem-indices 7 '(0 7 2 7 4 5))))) 270 | 271 | 272 | (testing "Zipping and unzipping lists" 273 | (ok (eql 2 (length (zip '(1 2) '(3 4) '(5 6))))) 274 | (ok (equal '() (zip '(1 2) '() '(3 4)))) 275 | (ok (equal '() (zip '() '()))) 276 | 277 | (ok (eql 2 (length (zip-with #'+ '(1 2) '(3 4) '(5 6))))) 278 | (ok (equal '() (zip-with #'+ '(1 2) '() '(3 4)))) 279 | (ok (equal '() (zip-with #'+ '() '()))) 280 | 281 | (ok (eql 2 (length (unzip '((1 2) '(3 4) '(5 6)))))) 282 | (ok (eql 2 (length (unzip '((1 2) '(3 4) '(5 6 7)))))) 283 | (ok (equal '() (unzip '((1 2) () (3 4))))) 284 | (ok (equal '() (unzip '(() ())))))) 285 | 286 | 287 | -------------------------------------------------------------------------------- /src/listopia.lisp: -------------------------------------------------------------------------------- 1 | (defpackage :listopia 2 | (:use :cl) 3 | (:export :head 4 | :last 5 | :tail 6 | :init 7 | :uncons 8 | 9 | ;; List transformations 10 | :map 11 | :intersperse 12 | :intercalate 13 | 14 | ;; Reducing lists (folds) 15 | :foldl 16 | :foldl1 17 | :foldr 18 | :foldr1 19 | 20 | ;; Special folds 21 | :concat 22 | :concat-map 23 | :and 24 | :or 25 | :any 26 | :all 27 | :sum 28 | :product 29 | :maximum 30 | :minimum 31 | 32 | ;; Building lists 33 | ;; Scans 34 | :scanl 35 | :scanl1 36 | :scanr 37 | :scanr1 38 | 39 | ;; Accumulating maps 40 | :map-accum-l 41 | :map-accum-r 42 | 43 | ;; Infinite lists 44 | :iterate 45 | :repeat 46 | :replicate 47 | :cycle 48 | 49 | ;; Unfolding 50 | :unfoldr 51 | 52 | ;; Sublists 53 | ;; Extracting sublists 54 | :take 55 | :drop 56 | :split-at 57 | :take-while 58 | :drop-while 59 | :drop-while-end 60 | :span 61 | :break 62 | :strip-prefix 63 | :inits 64 | :tails 65 | 66 | ;; Predicates 67 | :is-prefix-of 68 | :is-suffix-of 69 | :is-infix-of 70 | :is-subsequence-of 71 | 72 | ;; Searching lists 73 | ;; Searching by equality 74 | :elem 75 | :not-elem 76 | 77 | ;; Searching with a predicate 78 | :find 79 | :filter 80 | :partition 81 | 82 | ;; Indexing lists 83 | :elem-index 84 | :elem-indices 85 | :find-index 86 | :find-indices 87 | 88 | ;; Zipping and unzipping lists 89 | :zip 90 | :zip-with 91 | :unzip) 92 | (:shadow :and 93 | :break 94 | :find 95 | :last 96 | :map 97 | :or)) 98 | 99 | (in-package :listopia) 100 | 101 | 102 | ;;; Laziness 103 | 104 | 105 | (defstruct lazy-list 106 | (part nil) 107 | (fn nil)) 108 | 109 | 110 | ;;; Basic functions 111 | 112 | 113 | (defun head (list &optional (default nil)) 114 | (if (null list) 115 | default 116 | (car list))) 117 | 118 | (defun last (list &optional (default nil)) 119 | (if (null list) 120 | default 121 | (car (cl:last list)))) 122 | 123 | (defun tail (list &optional (default nil)) 124 | (if (null list) 125 | default 126 | (rest list))) 127 | 128 | (defun init (list &optional (default nil)) 129 | (if (rest list) 130 | (butlast list) 131 | default)) 132 | 133 | (defun uncons (list &optional (default nil)) 134 | (if (rest list) 135 | (list (first list) 136 | (rest list)) 137 | default)) 138 | 139 | 140 | ;;; List transformations 141 | 142 | 143 | (defun map (fn list) 144 | (cl:map 'list fn list)) 145 | 146 | (defun intersperse (sep list) 147 | (init (concat-map (lambda (x) (list x sep)) list))) 148 | 149 | (defun intercalate (sep list) 150 | (concat (intersperse sep list))) 151 | 152 | 153 | ;;; Reducing lists (folds) 154 | 155 | 156 | (defun foldl (fn init-val list) 157 | (reduce fn 158 | list 159 | :initial-value init-val)) 160 | 161 | (defun foldl1 (fn list) 162 | (if (rest list) 163 | (foldl fn (car list) (cdr list)) 164 | (error "empty list"))) 165 | 166 | (defun foldr (fn init-val list) 167 | (reduce fn 168 | list 169 | :from-end t 170 | :initial-value init-val)) 171 | 172 | (defun foldr1 (fn list) 173 | (if (rest list) 174 | (foldr fn (last list) (init list)) 175 | (error "empty list"))) 176 | 177 | 178 | ;;; Special folds 179 | 180 | 181 | (defun concat (list) 182 | (foldl #'append nil list)) 183 | 184 | (defun concat-map (fn list) 185 | (concat (map fn list))) 186 | 187 | (defun and (list) 188 | (notany #'null list)) 189 | 190 | (defun or (list) 191 | (notevery #'null list)) 192 | 193 | (defun any (fn list) 194 | (some fn list)) 195 | 196 | (defun all (fn list) 197 | (every fn list)) 198 | 199 | (defun sum (list) 200 | (apply #'+ list)) 201 | 202 | (defun product (list) 203 | (apply #'* list)) 204 | 205 | (defun maximum (list) 206 | (apply #'max list)) 207 | 208 | (defun minimum (list) 209 | (apply #'min list)) 210 | 211 | 212 | ;;; Building lists 213 | 214 | 215 | ;;; Scans 216 | 217 | 218 | (defun scanl (fn init-val list) 219 | (if (null list) 220 | (list init-val) 221 | (let ((acc init-val) 222 | (res nil)) 223 | (dolist (item list) 224 | (setf acc (funcall fn acc item)) 225 | (push acc res)) 226 | (cons init-val (nreverse res))))) 227 | 228 | (defun scanl1 (fn list) 229 | (unless (null list) 230 | (scanl fn (car list) (cdr list)))) 231 | 232 | (defun scanr (fn init-val list) 233 | (if (null list) 234 | (list init-val) 235 | (append 236 | (scanr fn 237 | (funcall fn init-val (last list)) 238 | (init list)) 239 | (list init-val)))) 240 | 241 | (defun scanr1 (fn list) 242 | (unless (null list) 243 | (scanr fn (last list) (init list)))) 244 | 245 | 246 | ;; Accumulating maps 247 | 248 | 249 | (defun map-accum-l (fn init-val list) 250 | (foldl 251 | (lambda (acc x) 252 | (let ((iter (funcall fn (first acc) x))) 253 | (list (first iter) 254 | (append (second acc) 255 | (list (second iter)))))) 256 | (list init-val nil) 257 | list)) 258 | 259 | (defun map-accum-r (fn init-val list) 260 | (foldr 261 | (lambda (x acc) 262 | (let ((iter (funcall fn (first acc) x))) 263 | (list (first iter) 264 | (cons (second iter) 265 | (second acc))))) 266 | (list init-val nil) 267 | list)) 268 | 269 | 270 | ;; Infinite lists 271 | 272 | 273 | (defun iterate (fn init-val) 274 | (make-lazy-list :part (list init-val) 275 | :fn (lambda () (iterate fn (funcall fn init-val))))) 276 | 277 | (defun repeat (init-val) 278 | (make-lazy-list :part (list init-val) 279 | :fn (lambda () (repeat init-val)))) 280 | 281 | (defun replicate (size init-val) 282 | (make-list size :initial-element init-val)) 283 | 284 | (defun cycle (list) 285 | (when (null list) 286 | (error "empty list")) 287 | (make-lazy-list :part (copy-list list) 288 | :fn (lambda () (cycle list)))) 289 | 290 | 291 | ;; Unfolding 292 | 293 | 294 | (defun unfoldr (fn init-val) 295 | (let ((iter-val (funcall fn init-val))) 296 | (when iter-val 297 | (cons (first iter-val) (unfoldr fn (second iter-val)))))) 298 | 299 | 300 | ;; Sublists 301 | 302 | 303 | ;; Extracting sublists 304 | 305 | 306 | (defun take (count list) 307 | (cond ((< count 0) (return-from take nil)) 308 | ((lazy-list-p list) (return-from take (take-lazy count list)))) 309 | (if (<= count (length list)) 310 | (subseq list 0 count) 311 | list)) 312 | 313 | (defun take-lazy (count list) 314 | (let ((part (lazy-list-part list)) 315 | (fn (lazy-list-fn list))) 316 | (if (>= (length part) count) 317 | (subseq part 0 count) 318 | (nconc part (take-lazy (- count (length part)) (funcall fn)))))) 319 | 320 | (defun drop (count list) 321 | (when (< count 0) (return-from drop list)) 322 | (nthcdr count list)) 323 | 324 | (defun split-at (count list) 325 | (list (take count list) 326 | (drop count list))) 327 | 328 | (defun take-while (pred list) 329 | (loop for item in list 330 | while (funcall pred item) 331 | collect item)) 332 | 333 | (defun drop-while (pred list) 334 | (unless (null list) 335 | (if (funcall pred (first list)) 336 | (drop-while pred (cdr list)) 337 | list))) 338 | 339 | (defun drop-while-end (pred list) 340 | (foldr (lambda (x acc) 341 | (if (cl:and (funcall pred x) (null acc)) 342 | '() 343 | (cons x acc))) 344 | '() 345 | list)) 346 | 347 | (defun span (pred list) 348 | (list (take-while pred list) (drop-while pred list))) 349 | 350 | (defun break (pred list) 351 | (span (lambda (x) (not (funcall pred x))) list)) 352 | 353 | (defun strip-prefix (prefix list &optional (default nil)) 354 | (if (equal (take (length prefix) list) prefix) 355 | (drop (length prefix) list) 356 | default)) 357 | 358 | (defun inits (list) 359 | (let ((result nil)) 360 | (dotimes (n (length list)) 361 | (setf result (cons (subseq list 0 n) result))) 362 | (reverse (cons list result)))) 363 | 364 | (defun tails (list) 365 | (let ((result nil)) 366 | (dotimes (n (length list)) 367 | (setf result (cons (subseq list n) result))) 368 | (reverse (cons nil result)))) 369 | 370 | 371 | ;; Predicates 372 | 373 | 374 | (defun is-prefix-of (prefix list) 375 | (equal (take (length prefix) list) prefix)) 376 | 377 | (defun is-suffix-of (suffix list) 378 | (is-prefix-of (reverse suffix) (reverse list))) 379 | 380 | (defun is-infix-of (infix list) 381 | (cond 382 | ((> (length infix) (length list)) nil) 383 | ((is-prefix-of infix list) t) 384 | (t (is-infix-of infix (cdr list))))) 385 | 386 | (defun is-subsequence-of (subseq list) 387 | (cond 388 | ((null subseq) t) 389 | ((null list) nil) 390 | (t (if (equal (car subseq) (car list)) 391 | (is-subsequence-of (cdr subseq) (cdr list)) 392 | (is-subsequence-of subseq (cdr list)))))) 393 | 394 | 395 | ;; Searching lists 396 | 397 | 398 | ;; Searching by equality 399 | 400 | 401 | (defun elem (element list &key (test 'equalp)) 402 | (not (null (member element list :test test)))) 403 | 404 | (defun not-elem (element list &key (test 'equalp)) 405 | (not (elem element list :test test))) 406 | 407 | 408 | ;; Searching with a predicate 409 | 410 | 411 | (defun find (pred list &optional (default nil)) 412 | (let ((tail (member-if pred list))) 413 | (if (null tail) 414 | default 415 | (car tail)))) 416 | 417 | (defun filter (pred list) 418 | (remove-if-not pred list)) 419 | 420 | (defun partition (pred list) 421 | (list (remove-if-not pred list) 422 | (remove-if pred list))) 423 | 424 | 425 | ;; Indexing lists 426 | 427 | 428 | (defun find-index (pred list &optional (default nil)) 429 | (let ((inx (position-if pred list))) 430 | (if (null inx) default inx))) 431 | 432 | (defun elem-index (item list &optional (default nil)) 433 | (find-index (lambda (x) (equalp x item)) list default)) 434 | 435 | (defun find-indices (pred list) 436 | (loop for i from 0 and e in list 437 | when (funcall pred e) collect i)) 438 | 439 | (defun elem-indices (item list) 440 | (loop for i from 0 and e in list 441 | when (equal e item) collect i)) 442 | 443 | 444 | ;; Zipping and unzipping lists 445 | 446 | 447 | (defun zip-with (fn &rest lists) 448 | (labels ((go-fn (result fn lists) 449 | (if (some #'null lists) 450 | (nreverse result) 451 | (go-fn 452 | (push (apply fn (mapcar #'car lists)) result) 453 | fn 454 | (mapcar #'cdr lists))))) 455 | (go-fn '() fn lists))) 456 | 457 | (defun zip (&rest lists) 458 | (apply #'zip-with (cons #'list lists))) 459 | 460 | (defun unzip (lists) 461 | (apply #'zip lists)) 462 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Listopia 2 | 3 | List manipulation library inspired by Haskell package [Data.List](https://hackage.haskell.org/package/base-4.10.1.0/docs/Data-List.html) 4 | 5 | ## Usage 6 | 7 | ```common-lisp 8 | (:import-from :listopia 9 | :name) 10 | ``` 11 | 12 | or 13 | 14 | ```common-lisp 15 | (:use :cl :listopia) 16 | (:shadowing-import-from :listopia 17 | :and 18 | :break 19 | :find 20 | :last 21 | :map 22 | :or) 23 | ``` 24 | 25 | ## Installation 26 | 27 | ```common-lisp 28 | (ql:quickload :listopia) 29 | ``` 30 | 31 | 32 | ### Basic functions 33 | 34 | #### head `(list &optional (default nil))` 35 | 36 | Extract the first element of a list. If list is empty, returns DEFAULT value. 37 | 38 | ```common-lisp 39 | (head '(1 2 3)) ;; => 1 40 | (head '(1)) ;; => 1 41 | (head '() 666) ;; => 666 42 | ``` 43 | 44 | #### last `(list &optional (default nil))` 45 | 46 | Extract the last element of a list. If list is empty, returns DEFAULT value. 47 | 48 | ```common-lisp 49 | (last '(1 2 3)) ;; => 3 50 | (last '(1)) ;; => 1 51 | (last '() 666) ;; => 666 52 | ``` 53 | 54 | #### tail `(list &optional (default nil))` 55 | 56 | Extract the elements after the head of a list. If list is empty, returns DEFAULT value. 57 | 58 | ```common-lisp 59 | (tail '(1 2 3)) ;; => '(2 3) 60 | (tail '(1)) ;; => '() 61 | (tail '() 666) ;; => 666 62 | ``` 63 | 64 | #### init `(list &optional (default nil))` 65 | 66 | Return all the elements of a list except the last one. If list is empty, returns DEFAULT value. 67 | 68 | ```common-lisp 69 | (init '(1 2 3)) ;; => '(1 2) 70 | (init '(1)) ;; => '() 71 | (init '() 666) ;; => 666 72 | ``` 73 | 74 | #### uncons `(list &optional (default nil))` 75 | 76 | Decompose a list into its head and tail. If the list is empty, returns DEFAULT. If the list is non-empty, returns '(x, xs), where x is the head of the list and xs its tail. 77 | 78 | ```common-lisp 79 | (uncons '(1 2 3)) ;; => '(1 (2 3)) 80 | (uncons '(1)) ;; => '(1 ()) 81 | (uncons '() 666) ;; => 666 82 | ``` 83 | 84 | ### List transformations 85 | 86 | #### map `(fn list)` 87 | 88 | Result - is the list obtained by applying FN to each element of LIST. 89 | 90 | ```common-lisp 91 | (map #'1+ '(1 2 3)) ;; => '(2 3 4) 92 | (map #'1+ '()) ;; => nil 93 | ``` 94 | 95 | #### intersperse `(sep lisp)` 96 | 97 | The `intersperse` function takes an element and a list and 'intersperses' that element between the elements of the list. 98 | 99 | ```common-lisp 100 | (intersperse 0 '(1 2 3)) ;; => '(1 0 2 0 3) 101 | (intersperse "," '("1" "2" "3")) ;; => '("1" "," "2" "," "3") 102 | (intersperse 0 '()) ;; => nil 103 | ``` 104 | 105 | #### intercalate `(sep list)` 106 | 107 | `(intercalate sep list)` is equivalent to `(concat (intersperse sep list))`. It inserts the list SEP in between the lists in LIST and concatenates the result. 108 | 109 | ```common-lisp 110 | (intercalate '(0) '((1) (2) (3))) ;; => '(1 0 2 0 3) 111 | (intercalate '(0) '((1) (2 3) (4 5 6))) ;; => '(1 0 2 3 0 4 5 6) 112 | (intercalate '(0) '()') ;; => nil 113 | ``` 114 | 115 | ### Reducing lists (folds) 116 | 117 | #### foldl `(fn init-val list)` 118 | 119 | Left-associative fold of a list. 120 | 121 | ```common-lisp 122 | (foldl (lambda (acc x) (append acc (list (1+ x))) '(1 2) '(2 3 4))) ;; => '(1 2 3 4 5) 123 | (foldl (lambda (acc x) (cons x acc)) '() '(1 2 3)) ;; => '(3 2 1) 124 | (foldl (lambda (acc x) (cons x acc)) '() '()) ;; => '() 125 | ``` 126 | 127 | #### foldl1 `(fn list)` 128 | 129 | A variant of `foldl` that has no base case, and thus may only be applied to non-empty lists. 130 | 131 | ```common-lisp 132 | (foldl1 #'+ '(1 2 3)) ;; => 6 133 | (foldl1 #'list '(1 2 3 4)) ;; => '(((1 2) 3) 4) 134 | ``` 135 | 136 | #### foldr `(fn init-val list)` 137 | 138 | Right-associative fold of a list. 139 | 140 | ```common-lisp 141 | (foldr (lambda (x acc) (append acc (list (1+ x))) '(1 2) '(4 3 2))) ;; => '(1 2 3 4 5) 142 | (foldr (lambda (x acc) (cons x acc)) '() '(1 2 3)) ;; => '(1 2 3) 143 | (foldr (lambda (x acc) (cons x acc)) '() '()) ;; => '() 144 | ``` 145 | 146 | #### foldr1 `(fn list)` 147 | 148 | A variant of `foldr` that has no base case, and thus may only be applied to non-empty lists. 149 | 150 | ```common-lisp 151 | (foldr1 #'+ '(1 2 3)) ;; => 6 152 | (foldr1 #'list '(1 2 3 4)) ;; => '(1 (2 (3 4))) 153 | ``` 154 | 155 | ### Special folds 156 | 157 | #### concat `(list)` 158 | 159 | The concatenation of all the elements of a container of lists. 160 | 161 | ```common-lisp 162 | (concat '((1) (2 3) (4 5 6))) ;; => '(1 2 3 4 5 6) 163 | (concat '((1)) ;; => '(1) 164 | (concat '(() ()) ;; => '() 165 | ``` 166 | 167 | #### concat-map `(fn list)` 168 | 169 | Map a function over all the elements of a container and concatenate the resulting lists. 170 | 171 | ```common-lisp 172 | (concat-map #'list '(1 2 3)) ;; => '(1 2 3) 173 | (concat-map (lambda (x) 174 | (list x x)) 175 | '(1 2 3)) ;; => '(1 1 2 2 3 3) 176 | (concat-map #'null '()) ;; => NIL 177 | ``` 178 | 179 | #### and `(list)` 180 | 181 | `and` returns the conjunction of values in LIST. 182 | 183 | 184 | ```common-lisp 185 | (and '(t t)) ;; => t 186 | (and '(t nil)) ;; => nil 187 | (and '()) ;; => t 188 | ``` 189 | 190 | #### or `(list)` 191 | 192 | `or` returns the disjunction of values in LIST. 193 | 194 | ```common-lisp 195 | (or '(t nil t)) ;; => t 196 | (or '(nil nil nil)) ;; => nil 197 | (or '()) ;; => nil 198 | ``` 199 | 200 | #### any `(fn list)` 201 | 202 | Determines whether any element of the list satisfies the predicate. 203 | 204 | ```common-lisp 205 | (any #'numberp '("1" "2" 3)) ;; => t 206 | (any #'numberp '()) ;; => nil 207 | ``` 208 | 209 | #### all `(fn list)` 210 | 211 | Determines whether all elements of the LIST satisfy the predicate. 212 | 213 | ```common-lisp 214 | (all #'numberp '(1 2 3)) ;; => t 215 | (all #'numberp '(1 "2" 3)) ;; => nil 216 | (all #'numberp '()) ;; => t 217 | ``` 218 | 219 | #### sum `(list)` 220 | 221 | The `sum` function computes the sum of the numbers of a LIST. 222 | 223 | ```common-lisp 224 | (sum '(1 2 3)) ;; => 6 225 | (sum '()) ;; => 0 226 | ``` 227 | 228 | #### product `(list)` 229 | 230 | The `product` function computes the product of the numbers of a LIST. 231 | 232 | ```common-lisp 233 | (product '(1 2 3)) ;; => 6 234 | (product '()) ;; => 1 235 | ``` 236 | 237 | #### maximum `(list)` 238 | 239 | The largest element of a non-empty LIST. 240 | 241 | ```common-lisp 242 | (maximum '(1 2 3 4 5)) ;; => 5 243 | (maximum '(1 2 3.0)) ;; => 3.0 244 | ``` 245 | 246 | #### minimum `(list)` 247 | 248 | The least element of a non-empty LIST. 249 | 250 | ```common-lisp 251 | (minimum '(1 2 3 4 5)) ;; => 1 252 | (minimum '(1.0 2 3)) ;; => 1.0 253 | ``` 254 | 255 | ### Building lists 256 | 257 | ### Scans 258 | 259 | #### scanl `(fn init-value list)` 260 | 261 | `scanl` is similar to `foldl`, but returns a list of successive reduced values from the left: 262 | 263 | `(scanl fn init '(x1 x2 ...)) == (list init (fn init x1) (fn (fn init x1) x2) ...)` 264 | 265 | ```common-lisp 266 | (scanl #'+ 1 '(2 3 4)) ;; => '(1 3 6 10) 267 | (scanl #'+ 1 '()) ;; => '(1) 268 | ``` 269 | 270 | #### scanl1 `(fn list)` 271 | 272 | `scanl1` is a variant of `scanl` that has no starting value argument. 273 | 274 | ```common-lisp 275 | (scanl1 #'+ '(1 2 3 4)) ;; => '(1 3 6 10) 276 | (scanl1 #'+ '()) ;; => nil 277 | ``` 278 | 279 | #### scanr `(fn init-value list)` 280 | 281 | `scanr` is the right-to-left dual of `scanl`. 282 | 283 | ```common-lisp 284 | (scanr #'+ 1 '(2 3 4)) ;; => '(10 8 5 1) 285 | (scanr #'+ 1 '()) ;; => '(1) 286 | ``` 287 | 288 | #### scanr1 `(fn list)` 289 | 290 | `scanr1` is a variant of `scanr` that has no starting value argument. 291 | 292 | ```common-lisp 293 | (scanr1 #'+ '(2 3 4 1)) ;; => '(10 8 5 1) 294 | (scanr1 #'+ '(5 4 3 2 1)) ;; => '(15 10 6 3 1) 295 | (scanr1 #'+ '()) ;; => nil 296 | ``` 297 | 298 | ### Accumulating maps 299 | 300 | #### map-accum-l `(fn init-val list)` 301 | 302 | `map-accum-l` applies a function to each element of a LIST, passing an accumulating parameter from left to right, and returning a final value of this accumulator together with the new structure. 303 | 304 | ```common-lisp 305 | (map-accum-l (lambda (acc x) (list acc (+ x acc))) 1 '(1 2 3)) ;; => '(1 (2 3 4)) 306 | (map-accum-l (lambda (acc x) (list (1+ acc) (+ x acc))) 1 '(1 2 3)) ;; => '(4 (2 4 6)) 307 | ``` 308 | 309 | #### map-accum-r `(fn init-val list)` 310 | 311 | `map-accum-r` applies a function to each element of a LIST, passing an accumulating parameter from right to left, and returning a final value of this accumulator together with the new structure. 312 | 313 | ```common-lisp 314 | (map-accum-r (lambda (acc x) (list acc (+ x acc))) 1 '(1 2 3)) ;; => '(1 (2 3 4)) 315 | (map-accum-r (lambda (acc x) (list (1+ acc) (+ x acc))) 1 '(1 2 3)) ;; => '(4 (4 4 4)) 316 | ``` 317 | 318 | ### Infinite lists 319 | 320 | #### iterate `(fn init-val)` 321 | 322 | `(iterate fn val)` returns an list of repeated applications of fn to val: 323 | 324 | `(iterate f x) == (list x (f x) (f (f x)) ...)` 325 | 326 | ```common-lisp 327 | (take 4 (iterate #'1+ 0)) ;; => '(0 1 2 3)' 328 | (take 0 (iterate #'1+ 0)) ;; => nil 329 | ``` 330 | 331 | #### repeat `(init-val)` 332 | 333 | `(repeat x)` is an list, with x the value of every element. 334 | 335 | ```common-lisp 336 | (take 4 (repeat 1)) ;; => '(1 1 1 1) 337 | (take 2 (repeat :foo)) ;; => '(:foo :foo) 338 | (take 0 (repeat :foo)) ;; => nil 339 | ``` 340 | 341 | #### replicate `(size init-val)` 342 | 343 | `(replicate n x)` is a list of length n with x the value of every element. 344 | 345 | ```common-lisp 346 | (replicate 4 1) ;; => '(1 1 1 1) 347 | (replicate 2 :foo) ;; => '(:foo :foo) 348 | (replicate 0 :foo) ;; => nil 349 | ``` 350 | 351 | #### cycle `(list)` 352 | 353 | `cycle` ties a finite list into a circular one, or equivalently, the infinite repetition of the original list. It is the identity on infinite lists. 354 | 355 | ```common-lisp 356 | (take 5 (cycle '(1 2 3))) ;; => '(1 2 3 1 2) 357 | (take 0 (cycle '(1 2 3))) ;; => nil 358 | ``` 359 | 360 | ### Unfolding 361 | 362 | #### unfoldr `(fn init-val)` 363 | 364 | The `unfoldr` function is a dual to `foldr`: while `foldr` reduces a list to a summary value, `unfoldr`` builds a list from a seed value. The function takes the element and returns NIL if it is done producing the list or returns '(a b), in which case, a is a prepended to the list and b is used as the next element in a recursive call. 365 | 366 | ```common-lisp 367 | (unfoldr (lambda (x) (if (= x 0) nil (list x (1- x)))) 10) ;; => '(10 9 8 7 6 5 4 3 2 1) 368 | (unfoldr (lambda (x) (if (= x 6) nil (list (expt 2 x) (1+ x)))) 1) ;; => '(2 4 8 16 32) 369 | ``` 370 | 371 | ### Sublists 372 | 373 | 374 | ### Extracting sublists 375 | 376 | #### take `(count list)` 377 | 378 | `(take n xs)` returns the prefix of xs of length n, or xs itself if n > length xs. 379 | 380 | ```common-lisp 381 | (take 3 '(1 2 3 4 5)) ;; => '(1 2 3) 382 | (take 3 '(1 2)) ;; => '(1 2) 383 | (take 3 '()) ;; => nil 384 | (take -1 '(1 2)) ;; => nil 385 | (take 0 '(1 2)) ;; => nil 386 | ``` 387 | 388 | This function maybe use with infinite lists. 389 | 390 | ```common-lisp 391 | (take 4 (cycle '(1 2 3))) ;; => '(1 2 3 1) 392 | (take 4 (iterate #'1+ 0)) ;; => '(0 1 2 3) 393 | (take 4 (repeat 1)) ;; => '(1 1 1 1) 394 | ``` 395 | 396 | #### drop `(count list)` 397 | 398 | `(drop n xs)` returns the suffix of xs after the first n elements, or NIL if n > length xs. 399 | 400 | ```common-lisp 401 | (drop 3 '(1 2 3 4 5)) ;; => '(4 5) 402 | (drop 3 '(1 2)) ;; => nil 403 | (drop 3 '()) ;; => nil 404 | (drop -1 '(1 2)) ;; => '(1 2) 405 | (drop 0 '(1 2)) ;; => '(1 2) 406 | ``` 407 | 408 | #### split-at `(count list)` 409 | 410 | `(split-at n xs)` returns a list where first element is xs prefix of length n and second element is the remainder of the list. 411 | 412 | ```common-lisp 413 | (split-at 3 '(1 2 3 4 5)) ;; => '((1 2 3) (4 5)) 414 | (split-at 1 '(1 2 3) ;; => '((1) (2 3)) 415 | (split-at 3 '(1 2 3) ;; => '((1 2 3) nil) 416 | (split-at 4 '(1 2 3) ;; => '((1 2 3) ()) 417 | (split-at 0 '(1 2 3) ;; => '(nil (1 2 3)) 418 | (split-at -1 '(1 2 3) ;; => '(nil (1 2 3)) 419 | ``` 420 | 421 | #### take-while `(pred list)` 422 | 423 | `take-while`, applied to a predicate PRED and a LIST, returns the longest prefix (possibly empty) of LIST of elements that satisfy PRED. 424 | 425 | ```common-lisp 426 | (take-while #'evenp '(1 2 3 4)) ;; => '() 427 | (take-while #'evenp '(2 4 5 6)) ;; => '(2 4) 428 | ``` 429 | 430 | #### drop-while `(pred list)` 431 | 432 | `(drop-while p xs)` returns the suffix remaining after `(take-while p xs)`. 433 | 434 | ```common-lisp 435 | (drop-while #'numberp '(1 2 3 nil nil 1 2 3)) ;; => '(nil nil 1 2 3) 436 | (drop-while #'numberp '(1 2 3)) ;; => '() 437 | (drop-while #'stringp '(1 2 3)) ;; => '(1 2 3) 438 | ``` 439 | 440 | #### drop-while-end `(pred list)` 441 | 442 | The `drop-while-end` function drops the largest suffix of a list in which the given predicate holds for all elements. 443 | 444 | ```common-lisp 445 | (drop-while-end #'numberp '("foo" "bar" 1 2 3)) ;; => '("foo" "bar") 446 | (drop-while-end #'numberp '("foo" 1 2 3 "bar")) ;; => '("foo" 1 2 3 "bar") 447 | (drop-while-end #'numberp '(1 2 3)) ;; => '() 448 | ``` 449 | 450 | #### span `(pred list)` 451 | 452 | `span`, applied to a predicate PRED and a LIST, returns a list where first element is longest prefix (possibly empty) of LIST of elements that satisfy PRED and second element is the remainder of the list. 453 | 454 | ```common-lisp 455 | (span (lambda (x) (< x 3)) '(1 2 3 4 1 2 3 4)) ;; => '((1 2) (3 4 1 2 3 4)) 456 | (span (lambda (x) (< x 9)) '(1 2 3)) ;; => '((1 2 3) ()) 457 | (span (lambda (x) (< x 0)) '(1 2 3)) ;; => '(() (1 2 3)) 458 | ``` 459 | 460 | #### break `(pred list)` 461 | 462 | `break`, applied to a predicate PRED and a LIST, returns a list where first element is longest prefix (possibly empty) of LIST of elements that do not satisfy PRED and second element is the remainder of the list 463 | 464 | ```common-lisp 465 | (break (lambda (x) (> x 3)) '(1 2 3 4 1 2 3 4)) ;; => '((1 2 3) (4 1 2 3 4)) 466 | (break (lambda (x) (< x 9)) '(1 2 3)) ;; => '(() (1 2 3)) 467 | (break (lambda (x) (> x 9)) '(1 2 3)) ;; => '((1 2 3) ()) 468 | ``` 469 | 470 | #### strip-prefix `(prefix list &optional (default nil))` 471 | 472 | The `strip-prefix` function drops the given prefix from a list. It returns DEFAULT value if the list did not start with the prefix given, or the list after the prefix, if it does. 473 | 474 | ```common-lisp 475 | (strip-prefix '(1 2) '(1 2 3 4)) ;; => '(3 4) 476 | (strip-prefix '(1 2) '(1 2)) ;; => '() 477 | (strip-prefix '(1 2) '(3 4 1 2)) ;; => NIL 478 | (strip-prefix '(1 2) '(3 4 1 2 5 6)) ;; => NIL 479 | ``` 480 | 481 | #### inits `(list)` 482 | 483 | The `inits` function returns all initial segments of the argument, shortest first. 484 | 485 | ```common-lisp 486 | (inits '(1 2 3)) ;; => '(nil (1) (1 2) (1 2 3)) 487 | (inits '()) ;; => '(nil) 488 | ``` 489 | 490 | #### tails `(list)` 491 | 492 | The `tails` function returns all final segments of the argument, longest first. 493 | 494 | ```common-lisp 495 | (tails '(1 2 3)) ;; => '((1 2 3) (2 3) (3) nil) 496 | (tails '()) ;; => '(nil) 497 | ``` 498 | 499 | ### Predicates 500 | 501 | #### is-prefix-of `(prefix list)` 502 | 503 | The `is-prefix-of` function takes two lists and returns `T` if the first list is a prefix of the second. 504 | 505 | ```common-lisp 506 | (is-prefix-of '(1 2) '(1 2 3 4)) ;; => T 507 | (is-prefix-of '(1 2) '(4 3 2 1)) ;; => nil 508 | (is-prefix-of '() '(1 2 3)) ;; => T 509 | ``` 510 | 511 | #### is-suffix-of `(suffix list)` 512 | 513 | The `is-suffix-of` function takes two lists and returns `T` if the first list is a suffix of the second. 514 | 515 | ```common-lisp 516 | (is-suffix-of '(2 1) '(4 3 2 1)) ;; => T 517 | (is-suffix-of '(1 2) '(4 3 2 1)) ;; => nil 518 | (is-suffix-of '() '(1 2 3)) ;; => T 519 | ``` 520 | 521 | #### is-infix-of `(infix list)` 522 | 523 | The `is-infix-of` function takes two lists and returns `T` if the first list is contained, wholly and intact, anywhere within the second. 524 | 525 | ```common-lisp 526 | (is-infix-of '(1 2) '(3 3 1 2 3 3)) ;; => T 527 | (is-infix-of '(1 2 3) '(4 1 2 4 3)) ;; => nil 528 | (is-infix-of '() '(1 2 3)) ;; => T 529 | ``` 530 | 531 | #### is-subsequence-of `(subseq list)` 532 | 533 | The `is-subsequence-of` function takes two lists and returns `T` if all the elements of the first list occur, in order, in the second. The elements do not have to occur consecutively. 534 | 535 | 536 | ```common-lisp 537 | (is-subsequence-of '(1 2 3) '(1 0 2 0 3 0)) ;; => T 538 | (is-subsequence-of '(1 2 3) '(1 0 2 0 4 0)) ;; => nil 539 | (is-subsequence-of '() '(1 2 3)) ;; => T 540 | ``` 541 | 542 | 543 | ### Searching lists 544 | 545 | 546 | ### Searching by equality 547 | 548 | #### elem `(element list &key (test 'equalp))` 549 | 550 | Does the element occur in the structure? 551 | 552 | ```common-lisp 553 | (elem 1 '(1 2 3)) ;; => T 554 | (elem "one" '(1 "one" 3) :test 'eql) ;; => nil 555 | ``` 556 | 557 | #### not-elem `(element list &key (test 'equalp))` 558 | 559 | `not-elem` is the negation of elem. 560 | 561 | ```common-lisp 562 | (not-elem 7 '(1 2 3)) ;; => T 563 | (not-elem "one" '(1 "one" 3) :test 'eql) ;; => T 564 | ``` 565 | 566 | ### Searching with a predicate 567 | 568 | #### find `(pred list &optional (default nil))` 569 | 570 | The `find` function takes a predicate and a LIST and returns the leftmost element of the list matching the predicate, or DEFAULT argument if there is no such element. 571 | 572 | ```common-lisp 573 | (find #'numberp '(:foo :bar 1 2 3)) ;; => 1 574 | (find #'numberp '(:foo :bar)) ;; => nil 575 | (find #'numberp '(:foo :bar) 666) ;; => 666 576 | ``` 577 | 578 | #### filter `(pred list)` 579 | 580 | `filter`, applied to a predicate and a LIST, returns the list of those elements that satisfy the predicate; i.e., 581 | 582 | ```common-lisp 583 | (filter #'numberp '(:foo 1 :bar 2 3)) ;; => '(1 2 3) 584 | (filter #'numberp '(:foo :bar)) ;; => nil 585 | ``` 586 | 587 | #### partition `(pred list)` 588 | 589 | The `partition` function takes a predicate a LIST and returns the pair of lists of elements which do and do not satisfy the predicate, respectively; i.e., 590 | 591 | ```common-lisp 592 | (partition #'numberp '(:foo 1 :bar 2)) ;; => '((1 2) (:foo :bar)) 593 | (partition #'numberp '(:foo :bar)) ;; => '(() (:foo :bar)) 594 | ``` 595 | 596 | ### Indexing lists 597 | 598 | #### elem-index `(item list &optional (default nil))` 599 | 600 | The `elem-index` function returns the index of the first element in the given list which is equal to the query element, or DEFAULT if there is no such element. 601 | 602 | ```common-lisp 603 | (elem-index 2 '(1 2 3)) ;; => 1 604 | (elem-index 2 '(1 2 3 2 1)) ;; => 1 605 | (elem-index 0 '(1 2 3) 42) ;; => 42 606 | ``` 607 | 608 | #### elem-indices `(item list)` 609 | 610 | The `elem-indices` function extends `elem-index`, by returning the indices of all elements equal to the query element, in ascending order. 611 | 612 | ```common-lisp 613 | (elem-indices 42 '(1 42 3 42)) ;; => '(1 3) 614 | (elem-indices 42 '(1 2 3)) ;; => '() 615 | ``` 616 | 617 | #### find-index `(pred list &optional (default nil))` 618 | 619 | The `find-index` function takes a predicate and a LIST and returns the index of the first element in the list satisfying the predicate, or DEFAULT if there is no such element. 620 | 621 | ```common-lisp 622 | (find-index #'keywordp '(1 :foo 3)) ;; => 1 623 | (find-index #'keywordp '(1 :foo 3 :bar 1)) ;; => 1 624 | (find-index #'keywordp '(1 2 3) 42) ;; => 42 625 | ``` 626 | 627 | #### find-indices `(pred list)` 628 | 629 | The `find-indices` function extends `find-index`, by returning the indices of all elements satisfying the PRED, in ascending order. 630 | 631 | ```common-lisp 632 | (find-indices #'keywordp '(1 :foo 3 :bar)) ;; => '(1 3) 633 | (find-indices #'keywordp '(1 2 3)) ;; => '() 634 | ``` 635 | 636 | ### Zipping and unzipping lists 637 | 638 | #### zip `(&rest lists)` 639 | 640 | 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. 641 | 642 | 643 | ```common-lisp 644 | (zip '(1 2) '(3 4) '(5 6))) ;; => '((1 3 5) (2 4 6)) 645 | (zip '(1 2 3) '(4) '(5 6))) ;; => '((1 4 5)) 646 | (zip '(1 2 3) '() '(5 6))) ;; => '() 647 | ``` 648 | 649 | #### zip-with `(fn &rest lists)` 650 | 651 | `zip-with` generalises `zip` by zipping with the function given as the first argument. 652 | 653 | ```common-lisp 654 | (zip-with #'+ '(1 2) '(3 4) '(5 6))) ;; => '(9 12) 655 | (zip-with #'list '(1 2) '(3 4) '(5 6))) ;; => '((1 3 5) (2 4 6)) 656 | (zip-with #'+ '(1 2 3) '() '(5 6))) ;; => '() 657 | ``` 658 | 659 | #### unzip `(lists)` 660 | 661 | Opposite by sense to `zip`. 662 | 663 | 664 | ```common-lisp 665 | (unzip '((1 2) (3 4) (5 6))) ;; => '((1 3 5) (2 4 6)) 666 | (unzip (.zip '(1 2) '(3 4) '(5 6))) ;; => '((1 2) (3 4) (5 6)) 667 | (unzip '((1 2 3) '(4) '(5 6))) ;; => '((1 4 5)) 668 | (unzip '((1 2 3) '())) ;; => '() 669 | ``` 670 | 671 | 672 | ## Author 673 | 674 | * Ito Dimercel (xolcman@gmail.com) 675 | 676 | ## Copyright 677 | 678 | Copyright (c) 2021 Ito Dimercel (xolcman@gmail.com) 679 | 680 | ## License 681 | 682 | Licensed under the LLGPL License. 683 | --------------------------------------------------------------------------------