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 inobjectsare of type pair.
The primitive type predicate for type null.
null?returns true iff all the objects inobjectsare 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
objectsare 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
object1andobject2. No two objects returned by different calls to cons areeq?to each other.
— Applicative: set-cdr! (set-cdr! pair object)
pairshould be a mutable pair.These applicatives set the referent of, respectively, the car reference or the cdr reference of
pairtoobject. The result of the expression is inert.
The short description of this applicative is that it returns an object
equal?toobjectwith an immutable evaluation structure. The “-es-” in the name is short for “evaluation structure”.The evaluation structure of an object
ois defined to be the set of all pairs that can be reached by following chains of references fromowithout ever passing through a non-pair object. The evaluation structure of a non-pair object is empty.If
objectis not a pair, the applicative returnsobject. Otherwise (ifobjectis 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 ofobjectat 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
listapplicative returnsobjects.The underlying operative of
listreturns its undifferentiated operand tree, regardless of whether that tree is or is not a list.
objectsshould 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
carandcdr, 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.
lengthshoulde be an exact non-negative integer.Applicative
make-listcreates a new mutable acyclic list of lengthlength, with all pairs havingfillin their cars. If no value is provided forfill,#inertis used.SOURCE NOTE: this is taken from r7rs.
Applicative
list-copycreates a new mutable copy oflist. That is, the returned list has the same list metrics aslistand the cars in the returned list are initiallyeq?to the corresponding cars inlist.SOURCE NOTE: this is taken from r7rs.
listshould be an acyclic list.Applicative
reversemakes a mutable copy of list but with the reverse order. That is, the returned list has the same number of pairs aslistand the cars in the returned list are initiallyeq?to the corresponding cars inlistbut 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
Lis the number of pairs ofLthat a naive traversal ofLwould visit only once. The cycle length ofLis the number of pairs ofLthat 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-metricsconstructs and returns a list of exact integers of the form(p n a c), wherep,n,a, andcare, 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.nis either0or1,a + c = p, andnandccannot 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,objectis not a pair.
objectmust be the start of an improper list containing at leastkpairs.The
list-tailapplicative followskcdr 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
objectmust contain at leastk1 + k2pairs.If
k2 = 0, the applicative does nothing. Ifk2 > 0, the applicative mutates the improper list starting atobjectto have acyclic prefix lengthk1and 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.
listsmust be a nonempty list of lists; if there are two or more, they must all have the same length.The map applicative applies
applicativeelement-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. Iflistsis a cyclic list, each argument list to whichapplicativeis applied is structurally isomorphic tolists. If any of the elements oflistsis a cyclic list, they all must be, or they wouldn’t all have the same length. Leta1...anbe their acyclic prefix lengths, andc1...cnbe their cycle lengths. The acyclic prefix lengthaof the resultant list will be the maximum of theak, while the cycle lengthcof the resultant list will be the least common multiple of theck. In the construction of the result,applicativeis called exactlya + ctimes.
Applicative
lengthreturns the (exact) improper-list length ofobject. That is, it returns the number of consecutive cdr references that can be followed starting fromobject. Ifobjectis not a pair, it returns zero; ifobjectis a cyclic list, it returns positive infinity.
The
list-refapplicative returns thecarof the object obtained by followingkcdr 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-refby the following equivalence:(list-ref object k) == (car (list-tail object k))
Here, all the elements of
listsexcept the last element (if any) must be acyclic lists. Theappendapplicative 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. Iflistsis 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-neighborsapplicative constructs and returns a list of all the consecutive sublists oflistof length 2, in order. Iflistis nil, the result is nil. Iflistis non-nil, the length of the result is one less than the length oflist. Iflistis 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
filterpasses each of the elements oflistas an argument toapplicative, one at a time in no particular order, using a fresh empty environment for each call. The result of each call toapplicativemust be boolean, otherwise an error is signaled.filterconstructs and returns a list of all elements ofliston whichapplicativereturned true, in the same order as inlist.applicativeis called exactly as many times as there are pairs inlist. The resultant list has a cycle containing exactly those elements accepted byapplicativethat were in the cycle oflist; if there were no such elements, the result is acyclic.
Applicative
assocreturns the first element ofpairswhose 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 oflistiseq-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 inobjectsare acyclic lists.
This is the type predicate for type list.
countable-list?returns true iff all the objects inobjectsare lists.
binaryshould be an applicative. If the short form is used,listshould be an acyclic. If the long form is used,precycle,incycle, andpostcycleshould be applicatives.If
listis empty, applicativereducereturnsidentity. Iflistis nonempty but acyclic, applicativereduceuses binary operationbinaryto merge all the elements oflistinto a single object, using any associative grouping of the elements. That is, the sequence of objects initially found inlistis repeatedly decremented in length by applyingbinaryto 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. Iflistis 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 fromprecycleis reduced using binary applicativeincycle; and the result from reducing the cycle is passed as an argument to unary applicativepostcycle. Binary operationbinaryis used to reduce the sequence consisting of the elements of the acyclic prefix oflistfollowed 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, orpostcycleuses the dynamic environment of the call toreduce.If
listis acyclic with lengthn >= 1,binaryis calledn - 1times. Iflistis cyclic with acyclic prefix lengthaand cycle lengthc,binaryis calledatimes;precycle,ctimes;incycle,c - 1times; andpostcycle, once.
listsmust 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-esreturns an object initiallyequal?toobjectwith a freshly constructed evaluation structure made up of mutable pairs. Ifobjectis not a pair, the applicative returnsobject. Ifobjectis 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 ofobjectat the time of copying, with corresponding non-pair referents beingeq?.