7 Pairs and lists
A pair is an object that refers to two other objects, called its car and cdr. The Kernel data type pair is encapsulated.
The null data type consists of a single immutable value, called nil or the empty list and having external representation ()
, with or without whitespace between the parentheses. It is immutable, and the null type is encapsulated.
If a
and d
are external representations of respectively the car and cdr of a pair p
, then (a . d)
is an external representation of p
. If the cdr of p
is nil, then (a)
is also an external representation of p
. If the cdr of p
is a pair p2
, and (r)
is an external representation of p2
, then (a r)
is an external representation of p
. When a pair is output (as by write), an external representation with the fewest parentheses is used; in the case of a finite list, only one set of parentheses is required beyond those used in representing the elements of the list. For example, an object with external representation (1 . (2 . (3 . ())))
would be output using, modulo whitespace, external representation (1 2 3)
.
The primitive type predicate for type pair.
pair?
returns true iff all the objects inobjects
are of type pair.
The primitive type predicate for type null.
null?
returns true iff all the objects inobjects
are of type null.
— Applicative: mutable-pair? (mutable-pair? objects)
The primitive type predicates for types immutable pair and mutable pair. These return true iff all the objects in
objects
are of type immutable pair or mutable pair respectively.SOURCE NOTE: these aren’t provided in the Kernel report, but added for convenience. These can be implemented in standard kernel by using guards.
A new mutable pair object is constructed and returned, whose car and cdr referents are respectively
object1
andobject2
. No two objects returned by different calls to cons areeq?
to each other.
— Applicative: set-cdr! (set-cdr! pair object)
pair
should be a mutable pair.These applicatives set the referent of, respectively, the car reference or the cdr reference of
pair
toobject
. The result of the expression is inert.
The short description of this applicative is that it returns an object
equal?
toobject
with an immutable evaluation structure. The “-es-” in the name is short for “evaluation structure”.The evaluation structure of an object
o
is defined to be the set of all pairs that can be reached by following chains of references fromo
without ever passing through a non-pair object. The evaluation structure of a non-pair object is empty.If
object
is not a pair, the applicative returnsobject
. Otherwise (ifobject
is a pair), the applicative returns an immutable pair whose car and cdr would be suitable results for(copy-es-immutable (car object))
and(copy-es-immutable (cdr object))
, respectively. Further, the evaluation structure of the returned value is isomorphic to that ofobject
at the time of copying, with corresponding non-pair referents beingeq?
.NOTE: In Kernel it’s undefined whether immutable pairs are copied or left “as is” in the result. klisp doesn’t copy immutable pairs, but that behaviour should not be depended upon.
The
list
applicative returnsobjects
.The underlying operative of
list
returns its undifferentiated operand tree, regardless of whether that tree is or is not a list.
objects
should be a finite nonempty list of arguments.The following equivalences hold:
(list* arg1) == arg1 (list* arg1 arg2 . args) == (cons arg1 (list* arg2 . args))
— Applicative: cdr (cdr pair)
These applicatives return, respectively, the car and cdr of
pair
.
— Applicative: cadr (cadr pair)
— Applicative: cdar (cdar pair)
— Applicative: cddr (cddr pair)
— Applicative: caaar (caaar pair)
— Applicative: caadr (caadr pair)
— Applicative: cadar (cadar pair)
— Applicative: caddr (caddr pair)
— Applicative: cdaar (cdaar pair)
— Applicative: cdadr (cdadr pair)
— Applicative: cddar (cddar pair)
— Applicative: cdddr (cdddr pair)
— Applicative: caaaar (caaaar pair)
— Applicative: caaadr (caaadr pair)
— Applicative: caadar (caadar pair)
— Applicative: caaddr (caaddr pair)
— Applicative: cadaar (cadaar pair)
— Applicative: cadadr (cadadr pair)
— Applicative: caddar (caddar pair)
— Applicative: cadddr (cadddr pair)
— Applicative: cdaaar (cdaaar pair)
— Applicative: cdaadr (cdaadr pair)
— Applicative: cdadar (cdadar pair)
— Applicative: cdaddr (cdaddr pair)
— Applicative: cddaar (cddaar pair)
— Applicative: cddadr (cddadr pair)
— Applicative: cdddar (cdddar pair)
— Applicative: cddddr (cddddr pair)
These applicatives are compositions of
car
andcdr
, with the “a’s” and “d’s” in the same order as they would appear if all the individual “car’s” and “cdr’s” were written out in prefix order. Arbitrary compositions up to four deep are provided. There are twenty-eight of these applicatives in all.
length
shoulde be an exact non-negative integer.Applicative
make-list
creates a new mutable acyclic list of lengthlength
, with all pairs havingfill
in their cars. If no value is provided forfill
,#inert
is used.SOURCE NOTE: this is taken from r7rs.
Applicative
list-copy
creates a new mutable copy oflist
. That is, the returned list has the same list metrics aslist
and the cars in the returned list are initiallyeq?
to the corresponding cars inlist
.SOURCE NOTE: this is taken from r7rs.
list
should be an acyclic list.Applicative
reverse
makes a mutable copy of list but with the reverse order. That is, the returned list has the same number of pairs aslist
and the cars in the returned list are initiallyeq?
to the corresponding cars inlist
but starting from the end and going backwards.SOURCE NOTE: this is taken from r7rs.
By definition, an improper list is a data structure whose objects are its start together with all objects reachable from the start by following the cdr references of pairs, and whose internal references are just the cdr references of its pairs. Every object, of whatever type, is the start of an improper list. If the start is not a pair, the improper list consists of just that object. The acyclic prefix length of an improper list
L
is the number of pairs ofL
that a naive traversal ofL
would visit only once. The cycle length ofL
is the number of pairs ofL
that a naive traversal would visit repeatedly. Two improper lists are structurally isomorphic iff they have the same acyclic prefix length and cycle length and, if they are terminated by non-pair objects rather than by cycles, the non-pair objects have the same type. Applicativeget-list-metrics
constructs and returns a list of exact integers of the form(p n a c)
, wherep
,n
,a
, andc
are, respectively, the number of pairs in, the number of nil objects in, the acyclic prefix length of, and the cycle length of, the improper list starting withobject
.n
is either0
or1
,a + c = p
, andn
andc
cannot both be non-zero. Ifc = 0
, the improper list is acyclic; ifn = 1
, the improper list is a finite list; ifn = c = 0
, the improper list is not a list; ifa = c = 0
,object
is not a pair.
object
must be the start of an improper list containing at leastk
pairs.The
list-tail
applicative followsk
cdr references starting fromobject
.The following equivalences hold:
(list-tail object 0) == object (list-tail object (+ k 1)) == (list-tail (cdr object) k)
The improper list starting at
object
must contain at leastk1 + k2
pairs.If
k2 = 0
, the applicative does nothing. Ifk2 > 0
, the applicative mutates the improper list starting atobject
to have acyclic prefix lengthk1
and cycle lengthk2
, by setting the cdr of the(k1+k2)
th pair in the list to refer to the(k1 + 1)
th pair in the list. The result returned byencycle!
is inert.
lists
must be a nonempty list of lists; if there are two or more, they must all have the same length.The map applicative applies
applicative
element-wise to the elements of the lists in lists (i.e., applies it to a list of the first elements of the lists, to a list of the second elements of the lists, etc.), using the dynamic environment from which map was called, and returns a list of the results, in order. The applications may be performed in any order, as long as their results occur in the resultant list in the order of their arguments in the original lists. Iflists
is a cyclic list, each argument list to whichapplicative
is applied is structurally isomorphic tolists
. If any of the elements oflists
is a cyclic list, they all must be, or they wouldn’t all have the same length. Leta1...an
be their acyclic prefix lengths, andc1...cn
be their cycle lengths. The acyclic prefix lengtha
of the resultant list will be the maximum of theak
, while the cycle lengthc
of the resultant list will be the least common multiple of theck
. In the construction of the result,applicative
is called exactlya + c
times.
Applicative
length
returns the (exact) improper-list length ofobject
. That is, it returns the number of consecutive cdr references that can be followed starting fromobject
. Ifobject
is not a pair, it returns zero; ifobject
is a cyclic list, it returns positive infinity.
The
list-ref
applicative returns thecar
of the object obtained by followingk
cdr references starting fromobject
.NOTE: In the current report, object is required to be a list. In klisp, for now, we prefer the behaviour presented here, as it is more in line with the applicative
list-tail
. That is, we definelist-ref
by the following equivalence:(list-ref object k) == (car (list-tail object k))
Here, all the elements of
lists
except the last element (if any) must be acyclic lists. Theappend
applicative returns a freshly allocated list of the elements of all the specifiedlists
, in order, except that if there is a last specified element oflists
, it is not copied, but is simply referenced by the cdr of the preceding pair (if any) in the resultant list. Iflists
is cyclic, the cycle of the result list consists of just the elements of the lists specified in the cycle inlists
. In this case, the acyclic prefix length of the result is the sum of the lengths of the lists specified in the acyclic prefix oflists
, and the cycle length of the result is the sum of the lengths of the lists specified in the cycle oflists
.The following equivalences hold:
(append) == () (append h) == h (append () h . t) == (append h . t) (append (cons a b) h . t) == (cons a (append b h . t))
The
list-neighbors
applicative constructs and returns a list of all the consecutive sublists oflist
of length 2, in order. Iflist
is nil, the result is nil. Iflist
is non-nil, the length of the result is one less than the length oflist
. Iflist
is cyclic, the result is structurally isomorphic to it (i.e., has the same acyclic prefix length and cycle length).For example:
(list-neighbors (list 1 2 3 4)) ⇒ ((1 2) (2 3) (3 4))
Applicative
filter
passes each of the elements oflist
as an argument toapplicative
, one at a time in no particular order, using a fresh empty environment for each call. The result of each call toapplicative
must be boolean, otherwise an error is signaled.filter
constructs and returns a list of all elements oflist
on whichapplicative
returned true, in the same order as inlist
.applicative
is called exactly as many times as there are pairs inlist
. The resultant list has a cycle containing exactly those elements accepted byapplicative
that were in the cycle oflist
; if there were no such elements, the result is acyclic.
Applicative
assoc
returns the first element ofpairs
whose car iseq-pred?
toobject
. If there is no such element inpairs
, nil is returned. Ifeq-pred?
is not supplied it defaults toequal?
. SOURCE NOTE: the optional eq-pred? argument is from r7rs.
Applicative
member?
is a predicate that returns true iff some element oflist
iseq-pred?
toobject
. Ifeq-pred?
is not supplied, it defaults toequal?
. SOURCE NOTE: the optional eq-pred? argument is from r7rs.
This is the type predicate for type finite-list.
finite-list?
returns true iff all the objects inobjects
are acyclic lists.
This is the type predicate for type list.
countable-list?
returns true iff all the objects inobjects
are lists.
binary
should be an applicative. If the short form is used,list
should be an acyclic. If the long form is used,precycle
,incycle
, andpostcycle
should be applicatives.If
list
is empty, applicativereduce
returnsidentity
. Iflist
is nonempty but acyclic, applicativereduce
uses binary operationbinary
to merge all the elements oflist
into a single object, using any associative grouping of the elements. That is, the sequence of objects initially found inlist
is repeatedly decremented in length by applyingbinary
to a list of any two consecutive objects, replacing those two objects with the result at the point in the sequence where they occurred; and when the sequence contains only one object, that object is returned. Iflist
is cyclic, the long form must be used. The elements of the cycle are passed, one at a time (but just once for each position in the cycle), as arguments to unary applicativeprecycle
; the finite, cyclic sequence of results fromprecycle
is reduced using binary applicativeincycle
; and the result from reducing the cycle is passed as an argument to unary applicativepostcycle
. Binary operationbinary
is used to reduce the sequence consisting of the elements of the acyclic prefix oflist
followed by the result returned bypostcycle
. The only constraint on the order of calls to the applicatives is that each call must be made before its result is needed (thus, parts of the reduction of the acyclic prefix may occur before the contribution from the cycle has been completed).Each call to
binary
,precycle
,incycle
, orpostcycle
uses the dynamic environment of the call toreduce
.If
list
is acyclic with lengthn >= 1
,binary
is calledn - 1
times. Iflist
is cyclic with acyclic prefix lengtha
and cycle lengthc
,binary
is calleda
times;precycle
,c
times;incycle
,c - 1
times; andpostcycle
, once.
lists
must be a nonempty list; its first element must be an acyclic nonempty list, and all of its elements except the last element (if any) must be acyclic lists.The
append!
applicative sets the cdr of the last pair in each nonempty list argument to refer to the next non-nil argument, except that if there is a last non-nil argument, it isn’t mutated. It is an error for any two of the list arguments to have the same last pair. The result returned by this applicative is inert.The following equivalences hold:
(append! v) == #inert (append! u v . w) == ($sequence (append! u v) (append! u . w))
Briefly, applicative
copy-es
returns an object initiallyequal?
toobject
with a freshly constructed evaluation structure made up of mutable pairs. Ifobject
is not a pair, the applicative returnsobject
. Ifobject
is a pair, the applicative returns a freshly constructed pair whose car and cdr would be suitable results for(copy-es (car object))
and(copy-es (cdr object))
, respectively. Further, the evaluation structure of the returned value is structurally isomorphic to that ofobject
at the time of copying, with corresponding non-pair referents beingeq?
.