## Rationale

This module provides routines, to access and traverse trees, i.e. nested pseudo-lists, as well as some list routines. They all help writing pattern matching and procedural macros.

In particular, there is a replacement of car, cdr and consorts by one operator, walk, of integer arguments, or, to be more precise, by +1 and -1: (walk -1) is car, (walk 1) cdr, (walk) is identity. These operaters can (and will) be composed, so, for example, (walk -1 1 1) is (o (walk -1) (walk 1) (walk 1)), in other words caddr.

The more arguments you give to walk, the deeper you can dig into a tree. If you like, you can interprete the arguments of walk as a path for traversing the tree. For compatibility with the c*r routines, you interprete the arguments of walk from right to left. It might be more convenient, to do it left to right; walk* does this.

Other routines, which walk a tree, are provided as well. In particular, we provide a routine, tree->pathes, which maps a tree to a flat list of pairs, containing the tree's items and the corresponding pathes to access that items. This list can be used, to destructure similarly shaped trees.

This way, we can use one tree as a pattern and take its pathes to destructure another tree, i.e. another nested pseudolist. You gess it: This will make pattern matching and writing procedural macros easy.

Some routines, which might be helpfull in macro-writing or tree-analysing, are provided as well, for example map*, filter* and friends.

### API

#### values->list

(values->list vals)syntax

transforms values, vals, into a list

#### list->values

list->values lstprocedure

transforms a list, lst, into a values

#### list-values

list-values proc #!rest argsprocedure

transforms the resulting values of calling proc with args into a list. Used in tests most of the time

#### always

always xprprocedure

returns a procedure of arbitrary many arguments, which always returns the value of xpr, whatever it's arguments are given

#### filter

filter ok? lstprocedure

splits a list into two sublists, one that pass the ok? test and one that doesn't

#### filter*

filter* ok? treeprocedure

filters a tree according to the predicate ok? respecting the tree structure

#### filter-pairs

filter-pairs ok? pairsprocedure

splits a list of pairs into two sublists, one that pass the ok? test on the cars and one that doesn't

#### at

at nprocedure
at n lstprocedure

the second takes the nth item of the list lst, the first is a curried version of the second.

#### take

take nprocedure
take n lstprocedure

the second takes the first n items of the list lst, the first is a curried version of the second.

#### drop

drop nprocedure
drop n lstprocedure

the second drops the first n items of the list lst, the first is a curried version of the second.

#### split-at

split-at n plsprocedure

splitting a pseudo-list in two sublists, head and tail, the sublist head excluding the nth item and the pseudolist tail starting with nth item.

#### split-when

split-when ok? lstprocedure

splitting a list in two sublists, head and tail, the sublist head before the first item passing the ok? test, the sublist tail starting at that item.

#### take-while

take-while ok?procedure
take-while ok? lstprocedure

the second takes the first items of the list lst, which pass the ok? test; the first is a curried version of the second.

#### drop-while

drop-while ok?procedure
drop-while ok? lstprocedure

the second drops the first items of the list lst, which pass the ok? test; the first is a curried version of the second.

#### every?

every? ok?procedure
every? ok? xsprocedure

checks, if every item of the list xs passes the ok? test. The first is the curried version of the second call.

#### all?

all? op?procedure

returns a predicate, which tests, if all items of it's only argument, a list, are pairweise passed by the binary predicate op?)

#### some?

some? ok?procedure
some? ok? xsprocedure

checks, if some item of the list xs passes the ok? test. The first is the curried version of the second call.

#### none?

none? ok?procedure
none? ok? xsprocedure

checks, if no item of the list xs passes the ok? test. The first is the curried version of the second call.

#### pseudo-list?

pseudo-list? xprprocedure

checks, if xpr evaluates to a pair, which is not a list, or to an atom which is not null.

#### pseudo-length

pseudo-length plsprocedure

returns the length of a pseudo-list or list

returns the head of a pseudo-list, i.e. the list stripped-of the sentinel. In case of a list, the list itself is returned.

#### pseudo-tail

pseudo-tail plsprocedure

returns the sentinel of a pseudo-list, i.e. null in the case of lists

#### pseudo-list-of?

pseudo-list-of? ok?procedure

returns a procedure, which checks, if its only argument is a pseudo-list, whose items pass the ok? test. A pseudo-list is not a list, except ok? is null? or atom?

#### indexes

indexes xsprocedure

returns the indexes of the list xs.

#### tree-of?

tree-of? ok?procedure

returns a procedure, which checks, if its only argument is a tree, whose items pass the ok? test.

#### iterate

iterate fn kprocedure
iterate fn k argprocedure

The second applies the function fn successively k times to the argument arg. The first is a curried version of the second.

#### map*

map* fn tree #!rest treesprocedure

maps a function, fn, over a list of trees

#### when-map*

when-map* ok? fn treeprocedure

map fn recursively over tree, but only on those expressions, which pass the ok? test

#### map**

map** fn pat #!rest treesprocedure

returns two values, mapping recursively pat and trees with fn.

#### flatten*

flatten* pairsprocedure

transforms a nested list of pairs into a flat list of pairs

#### zip2

zip2 xlsprocedure

transforms a list of pairs into a pair of lists

#### unzip*

unzip* treesprocedure

transforms a list of equally shaped nested pseudo-lists into a nested list of lists or atoms

#### walk

walk #!rest ksprocedure

creates a procedure, which accesses a tree along the nonzero exact integers ks from right to left, where 1 represents cdr, -1 car.

#### walk*

walk* #!rest ksprocedure

the same as walk, but the ks are accessed from left to right

#### memp

memp ok? lstprocedure

returns the sublist of lst, whose first item passes the ok? test, or #f if no item passes it.

#### dups-remove

dups-remove =? lstprocedure

removes duplicates from the list lst, compared by the binary function =?

#### and?

and? #!rest xprsprocedure

procedure version of and, so that apply is usable

#### sublist

sublist lst fromprocedure
sublist lst from uptoprocedure

returns the sublist of a list, lst, starting from indox from included up to index upto excluded. If the argument upto is missing, the length of lst is used instead.

#### tree->pathes

tree->pathes patprocedure

transforms a nested pseudo-list, pat, breadth-first into a flat list of pairs consisting of the nested pseudo-list's items and the pathes to reach these items

#### pathes

pathes item treeprocedure

returns a list of pathes, where each path is a list of integers, which, when fed to walk, would return the item in the tree.

#### same-shape?

same-shape? tree1 tree2procedure

checks, if both trees have the same shape, i.e. have items in the same position.

#### pseudo->accessors

pseudo->accessors patprocedure
pseudo->accessors pat walkerprocedure

transforms a flat pseudo-list, pat, into a flat list of triples consisting of the pseudo-list's atoms, the procedures to reach these atoms and the expressions describing these procedures. If walker is not given, 'walk is assumed.

#### tree->accessors

tree->accessors patprocedure
tree->accessors pat walkerprocedure

transforms a flat pseudo-list, pat, into a flat list of triples consisting of the pseudo-list's atoms, the procedures to reach these atoms and the expressions describing these procedures. If walker is not given, 'walk is assumed.

#### apply-accessors

apply-accessors pat treeprocedure
apply-accessors pat tree map?procedure

applies or maps, if map? is true, all accessors to a tree.

#### all-match?

all-match? treeprocedure
tree xssprocedure

Returns a procedure, which tests, if all accessors match its only argument xss. Shows the item, the corresponding path and the argument of the first failing accessor.

#### tree-walkers

tree-walkersprocedure
tree-walkers symprocedure

with sym: documentation of exported symbol without sym: list of exported symbols

### Examples

```(import tree-walkers)

((iterate cdr 2) '(1 2 3))
;-> (quote (3))

((walk -2 1 -1 1) '(a (b (c)) d))
;-> (quote c)

((walk 3) '(a (b (c)) d))
;-> (quote ())

((walk -4) '((((x)))))
;-> (quote x)

((walk -1 1 -1 1) '(a (b x) c))
;-> (quote x)

((walk -1 1 -1 1) '(a (b x) c y))
;-> (quote x)

((walk -1 1 1 1) '(a (b x) c y))
;-> (quote y)

(tree->pathes '(a b . c))
;-> (quote ((a (-1)) (b (-1 1)) (c (1 1))))

(tree->pathes '(a b c))
;-> (quote ((a (-1)) (b (-1 1)) (c (-1 1 1)) (() (1 1 1))))

(tree->pathes '(a (b c) d))
;-> (quote ((a (-1)) (d (-1 1 1)) (() (1 1 1)) (b (-1 -1 1)) (c (-1 1 -1 1)) (() (1 1 -1 1))))

(tree->pathes '(a (b (c)) d))
;-> (quote ((a (-1)) (d (-1 1 1)) (() (1 1 1)) (b (-1 -1 1)) (() (1 1 -1 1)) (c (-1 -1 1 -1 1)) (() (1 -1 1 -1 1))))

(tree->pathes '(a (b . c) . d))
;-> (quote ((a (-1)) (d (1 1)) (b (-1 -1 1)) (c (1 -1 1))))

(pathes 'a '(((((a))))))
;-> (quote ((-1 -1 -1 -1 -1)))

(pathes 'c '(a (b . c) d))
;-> (quote ((1 -1 1)))

(pathes '() '(a (b (c)) d))
;-> (quote ((1 1 1) (1 1 -1 1) (1 -1 1 -1 1)))

(pathes 'c '(a (b (c)) d))
;-> (quote ((-1 -1 1 -1 1)))

(pathes 'a '(a a))
;-> (quote ((-1) (-1 1)))

(pathes 'a 'a)
;-> (quote (()))

(map (lambda (triple) `(,(car triple) ,(caddr triple)))
(tree->accessors '(a (b . c) d) 'walk))
;-> (quote ((a (walk -1)) (d (walk -1 1 1)) (() (walk 1 1 1)) (b (walk -1 -1 1)) (c (walk 1 -1 1))))

(let ((pat '(a (b . c) . d)))
(map (lambda (proc) (proc pat)) (map cadr (tree->accessors pat))))
;-> (quote (a d b c))

(let ((pat '(a (b c) d)))
(map (lambda (proc) (proc pat)) (map cadr (tree->accessors pat))))
;-> (quote (a d () b c ()))

(let ((pat '(a (b (c . d)) (e . f))))
(map (lambda (proc) (proc pat)) (map cadr (tree->accessors pat))))
;-> (quote (a () b () c d e f))

((pseudo-list-of? atom?) '(a b c))
;-> #t

((pseudo-list-of? symbol?) '(a b c))
;-> #f

((pseudo-list-of? symbol?) '(a b . c))
;-> #t

((pseudo-list-of? atom?) '())
;-> #t

((pseudo-list-of? symbol?) 'a)
;-> #t

((tree-of? symbol?) '(a (b . c) . d))
;-> #t

((tree-of? symbol?) '(a (b . c) d))
;-> #f

((tree-of? (disjoin symbol? null?)) '(a (b . c) d))
;-> #t

((all-match? '(a (b . c) . d)) '(a (b . c) . d))
;-> #t

((all-match? '(a (b . c) . d)) '(1 (2 3) 4))
;-> #t

((all-match? '(a (b c) d)) '(1 (2 3) 4))
;-> #t

((all-match? '(a (#f c) d)) '(1 (2 3) 4))
;-> #t

((all-match? '(a (#f c) d)) '(1 (#f 3) 4))
;-> #t

((all-match? '(a b)) '(1))
;-> #f

(same-shape? '(a (b (c) d) e) '(1 (2 (3) 4) 5))
;-> #t

(same-shape? '(a (b (c) d) e) '(1 (2 (3 0) 4) 5))
;-> #f

(and? (eqv? 'a 'a) (eqv? 1 1) (equal? "foo" "foo"))
;-> #t

(and? (eqv? 'a 'a) (equal? "foo" "bar") (eqv? 1 1))
;-> #f

(none? odd? '(0 2))
;-> #t

(none? odd? '(0 1 2 3))
;-> #f

(some? odd? '(0 2))
;-> #f

(some? odd? '(1 2))
;-> #t

(every? odd? '(1 3))
;-> #t

(every? odd? '(0 1 2 3))
;-> #f

((all? same-shape?) '((1 2) (10 20) (100 200)))
;-> #t

((all? same-shape?) '((1 2) (10 20) (100 200)))
;-> #t

((all? same-shape?) '((1 2) (10 . 20) (100 200)))
;-> #f

;-> 1

;-> (quote (1 (2 3)))

(map* add1 '(0 (1 . 2)))
;-> (quote (1 (2 . 3)))

(when-map* (list-of? atom?) list->vector '((1) (2)))
;-> (quote (#(1) #(2)))

(when-map* (list-of? atom?) list->vector '((1) ((2) (3))))
;-> (quote (#(1) (#(2) #(3))))

(when-map* (list-of? atom?) list->vector '((1) ((2) 0 (3))))
;-> (quote (#(1) (#(2) 0 #(3))))

(when-map* (conjoin integer? even?) add1 '(0 (1 2 (3))))
;-> (quote (1 (1 3 (3))))

(list-values map** list '(a b) '(1 2))
;-> (quote (((a) (b)) ((1) (2))))

(list-values map** list '(a (b c) d) '(1 (2 (3 4)) #(5 6)))
;-> (quote (((a) ((b) (c)) (d)) ((1) ((2) ((3) (4))) (#(5 6)))))

(list-values map** list '(a b) '(1 2) '(10 20))
;-> (quote (((a) (b)) ((1 10) (2 20))))

(list-values map** list '(a (b c) d) '(1 (2 (3)) 4) '(10 (20 (30)) 40))
;-> (quote (((a) ((b) (c)) (d)) ((1 10) ((2 20) ((3 30))) (4 40))))

(values->list (filter odd? '(0 1 2 3)))
;-> (quote ((1 3) (0 2)))

(list-values filter odd? '(0 1 2 3))
;-> (quote ((1 3) (0 2)))

(values->list (filter-pairs odd? '((0 a) (1 b) (2 c) (3 d))))
;-> (quote (((1 b) (3 d)) ((0 a) (2 c))))

(list-values filter-pairs odd? '((0 a) (1 b) (2 c) (3 d)))
;-> (quote (((1 b) (3 d)) ((0 a) (2 c))))

(filter* odd? '(0 1 2 3))
;-> (quote (1 3))

(filter* odd? '(0 (1 2 3)))
;-> (quote ((1 3)))

(filter* odd? '(0 1 (2 3 (4 5) 6 7) 8 9))
;-> (quote (1 (3 (5) 7) 9))

(filter* odd? '(0 (2 (4) 6) 8))
;-> (quote ((())))

(filter* odd? '(1 (3 (5) 7) 9))
;-> (quote (1 (3 (5) 7) 9))

(values->list (list->values '(1 2 3)))
;-> (quote (1 2 3))

(values->list (list->values (values->list (values 1 2 3))))
;-> (quote (1 2 3))

(values->list (split-at 2 '(0 1 2 3)))
;-> (quote ((0 1) (2 3)))

(list-values split-at 2 '(0 1 2 3))
;-> (quote ((0 1) (2 3)))

(drop 2 '(0 1 2 3))
;-> (quote (2 3))

(take 2 '(0 1 2 3))
;-> (quote (0 1))

(values->list (split-when odd? '(0 1 2 3)))
;-> (quote ((0) (1 2 3)))

(list-values split-when odd? '(0 1 2 3))
;-> (quote ((0) (1 2 3)))

(take-while odd? '(1 3 2 4))
;-> (quote (1 3))

(drop-while odd? '(1 3 2 4))
;-> (quote (2 4))

(memp integer? '(a b c))
;-> #f

(memp integer? '(a b 1 c))
;-> (quote (1 c))

(sublist '(0 1 2 3 4) 1 3)
;-> (quote (1 2))

(sublist '(0 1 2 3 4) 4)
;-> (quote (4))

(indexes '(a b c d))
;-> (quote (0 1 2 3))

(unzip* '(1 2 3))
;-> (quote (1 2 3))

(unzip* '((1 2) (1 2) (1 2)))
;-> (quote ((1 1 1) (2 2 2)))

(unzip* '((1 . 2) (1 . 2) (1 . 2)))
;-> (quote ((1 1 1) 2 2 2))

(unzip* '((1 (2 3)) (1 (2 3)) (1 (2 3))))
;-> (quote ((1 1 1) ((2 2 2) (3 3 3))))

(unzip* '((0 (1 . 2)) (0 (1 . 2)) (0 (1 . 2))))
;-> (quote ((0 0 0) ((1 1 1) 2 2 2)))

(unzip* '((0 (1 2)) (0 (1 2)) (0 (1 2))))
;-> (quote ((0 0 0) ((1 1 1) (2 2 2))))

(unzip* '((0 (1 2) . 3) (0 (1 2) . 3) (0 (1 2) . 3)))
;-> (quote ((0 0 0) ((1 1 1) (2 2 2)) 3 3 3))

(unzip* '((0 (1 . 2) . 3) (0 (1 . 2) . 3) (0 (1 . 2) . 3)))
;-> (quote ((0 0 0) ((1 1 1) 2 2 2) 3 3 3))

(flatten* (map* list '(a b (c d)) '(1 2 (#(3 30) #(4 40)))))
;-> (quote ((a 1) (b 2) (c #(3 30)) (d #(4 40))))

(flatten* '((a 1) ((b 2)) (((c 3)))))
;-> (quote ((a 1) (b 2) (c 3)))

;-> (quote (1 2))

(pseudo-tail '(1 2 . 3))
;-> 3

;-> (quote ())

(pseudo-tail '())
;-> (quote ())

;-> (quote ())

(pseudo-tail 3)
;-> 3

(pseudo-length '(0 1 . 2))
;-> 2
```

None

Mar 27, 2024

## Author

Juergen Lorenz

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

` `

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

## Version history

2.0
added a lot of routines based on walk
1.0
initial check in