4.10 Pairs and Lists
Pairs and Lists in The Racket Guide introduces pairs and lists.
A pair combines exactly two values. The first value is accessed with the car procedure, and the second value is accessed with the cdr procedure. Pairs are not mutable (but see Mutable Pairs and Lists).
A list is recursively defined: it is either the constant null, or it is a pair whose second value is a list.
A list can be used as a single-valued sequence (see Sequences). The elements of the list serve as elements of the sequence. See also in-list.
Cyclic data structures can be created using only immutable pairs via read or make-reader-graph. If starting with a pair and using some number of cdrs returns to the starting pair, then the pair is not a list.
See Reading Pairs and Lists for information on reading pairs and lists and Printing Pairs and Lists for information on printing pairs and lists.
4.10.1 Pair Constructors and Selectors
procedure
(build-list n proc) → list?
n : exact-nonnegative-integer? proc : (exact-nonnegative-integer? . -> . any)
> (build-list 10 values) '(0 1 2 3 4 5 6 7 8 9)
> (build-list 5 (lambda (x) (* x x))) '(0 1 4 9 16)
4.10.2 List Operations
procedure
(length lst) → exact-nonnegative-integer?
lst : list?
procedure
lst : pair? pos : exact-nonnegative-integer?
The lst argument need not actually be a list; lst must merely start with a chain of at least (add1 pos) pairs.
This function takes time proportional to pos.
> (list-ref (list 'a 'b 'c) 0) 'a
> (list-ref (list 'a 'b 'c) 1) 'b
> (list-ref (list 'a 'b 'c) 2) 'c
> (list-ref (cons 1 2) 0) 1
> (list-ref (cons 1 2) 1) list-ref: index reaches a non-pair
index: 1
in: '(1 . 2)
procedure
lst : any/c pos : exact-nonnegative-integer?
The lst argument need not actually be a list; lst must merely start with a chain of at least pos pairs.
This function takes time proportional to pos.
> (list-tail (list 1 2 3 4 5) 2) '(3 4 5)
> (list-tail (cons 1 2) 1) 2
> (list-tail (cons 1 2) 2) list-tail: index reaches a non-pair
index: 2
in: '(1 . 2)
> (list-tail 'not-a-pair 0) 'not-a-pair
The last argument need not be a list, in which case the result is an “improper list.”
This function takes time proportional to the length of all arguments (added together) except the last argument.
> (append (list 1 2) (list 3 4)) '(1 2 3 4)
> (append (list 1 2) (list 3 4) (list 5 6) (list 7 8)) '(1 2 3 4 5 6 7 8)
This function takes time proportional to the length of lst.
4.10.3 List Iteration
procedure
proc : procedure? lst : list?
> (map (lambda (number) (+ 1 number)) '(1 2 3 4)) '(2 3 4 5)
> (map (lambda (number1 number2) (+ number1 number2)) '(1 2 3 4) '(10 100 1000 10000)) '(11 102 1003 10004)
procedure
proc : procedure? lst : list?
The andmap function is actually closer to foldl than map, since andmap doesn’t produce a list. Still, (andmap f (list x y z)) is equivalent to (and (f x) (f y) (f z)) in the same way that (map f (list x y z)) is equivalent to (list (f x) (f y) (f z)).
the result is #f if any application of proc produces #f, in which case proc is not applied to later elements of the lsts; and
the result is that of proc applied to the last elements of the lsts; more specifically, the application of proc to the last elements in the lsts is in tail position with respect to the andmap call.
If the lsts are empty, then #t is returned.
> (andmap positive? '(1 2 3)) #t
> (andmap positive? '(1 2 a)) positive?: contract violation
expected: real?
given: 'a
> (andmap positive? '(1 -2 a)) #f
> (andmap + '(1 2 3) '(4 5 6)) 9
procedure
proc : procedure? lst : list?
To continue the andmap note above, (ormap f (list x y z)) is equivalent to (or (f x) (f y) (f z)).
the result is #f if every application of proc produces #f; and
the result is that of the first application of proc producing a value other than #f, in which case proc is not applied to later elements of the lsts; the application of proc to the last elements of the lsts is in tail position with respect to the ormap call.
If the lsts are empty, then #f is returned.
procedure
proc : procedure? lst : list?
procedure
proc : procedure? init : any/c lst : list?
If foldl is called with n lists, then proc must take n+1 arguments. The extra argument is the combined return values so far. The proc is initially invoked with the first item of each list, and the final argument is init. In subsequent invocations of proc, the last argument is the return value from the previous invocation of proc. The input lsts are traversed from left to right, and the result of the whole foldl application is the result of the last application of proc. If the lsts are empty, the result is init.
Unlike foldr, foldl processes the lsts in constant space (plus the space for each call to proc).
> (foldl cons '() '(1 2 3 4)) '(4 3 2 1)
> (foldl + 0 '(1 2 3 4)) 10
> (foldl (lambda (a b result) (* result (- a b))) 1 '(1 2 3) '(4 5 6)) -27
procedure
proc : procedure? init : any/c lst : list?
> (foldr cons '() '(1 2 3 4)) '(1 2 3 4)
> (foldr (lambda (v l) (cons (add1 v) l)) '() '(1 2 3 4)) '(2 3 4 5)
4.10.4 List Filtering
procedure
pred : procedure? lst : list?
> (remove 2 (list 1 2 3 2 4)) '(1 3 2 4)
> (remove 2 (list 1 2 3 2 4) =) '(1 3 2 4)
> (remove '(2) (list '(1) '(2) '(3))) '((1) (3))
> (remove "2" (list "1" "2" "3")) '("1" "3")
> (remove #\c (list #\a #\b #\c)) '(#\a #\b)
> (remq 2 (list 1 2 3 4 5)) '(1 3 4 5)
> (remq '(2) (list '(1) '(2) '(3))) '((1) (2) (3))
> (remq "2" (list "1" "2" "3")) '("1" "3")
> (remq #\c (list #\a #\b #\c)) '(#\a #\b)
> (remv 2 (list 1 2 3 4 5)) '(1 3 4 5)
> (remv '(2) (list '(1) '(2) '(3))) '((1) (2) (3))
> (remv "2" (list "1" "2" "3")) '("1" "3")
> (remv #\c (list #\a #\b #\c)) '(#\a #\b)
procedure
(sort lst less-than? [ #:key extract-key #:cache-keys? cache-keys?]) → list? lst : list? less-than? : (any/c any/c . -> . any/c) extract-key : (any/c . -> . any/c) = (lambda (x) x) cache-keys? : boolean? = #f
The sort is stable; if two elements of lst are “equal” (i.e., less-than? does not return a true value when given the pair in either order), then the elements preserve their relative order from lst in the output list. To preserve this guarantee, use sort with a strict comparison functions (e.g., < or string<?; not <= or string<=?).
Because of the peculiar fact that the IEEE-754 number system specifies that +nan.0 is neither greater nor less than nor equal to any other number, sorting lists containing this value may produce a surprising result.
The #:key argument extract-key is used to extract a key value for comparison from each list element. That is, the full comparison procedure is essentially
(lambda (x y) (less-than? (extract-key x) (extract-key y)))
By default, extract-key is applied to two list elements for every comparison, but if cache-keys? is true, then the extract-key function is used exactly once for each list item. Supply a true value for cache-keys? when extract-key is an expensive operation; for example, if file-or-directory-modify-seconds is used to extract a timestamp for every file in a list, then cache-keys? should be #t to minimize file-system calls, but if extract-key is car, then cache-keys? should be #f. As another example, providing extract-key as (lambda (x) (random)) and #t for cache-keys? effectively shuffles the list.
> (sort '(1 3 4 2) <) '(1 2 3 4)
> (sort '("aardvark" "dingo" "cow" "bear") string<?) '("aardvark" "bear" "cow" "dingo")
> (sort '(("aardvark") ("dingo") ("cow") ("bear")) #:key car string<?) '(("aardvark") ("bear") ("cow") ("dingo"))
4.10.5 List Searching
The lst argument need not actually be a list; lst must merely start with a chain of pairs until a matching element is found. If no matching element is found, then lst must be a list (and not a cyclic list). The result can be a non-list in the case that an element is found and the returned tail of lst is a non-list.
> (member 2 (list 1 2 3 4)) '(2 3 4)
> (member 9 (list 1 2 3 4)) #f
> (member #'x (list #'x #'y) free-identifier=?) '(#<syntax:eval:539:0 x> #<syntax:eval:539:0 y>)
> (member #'a (list #'x #'y) free-identifier=?) #f
> (member 'b '(a b . etc)) '(b . etc)
procedure
proc : procedure? lst : list?
The lst argument need not actually be a list of pairs; lst must merely start with a chain of pairs contains pairs until a matching element is found. If no matching element is found, then lst must be a list of pairs (and not a cyclic list).
> (assoc 3 (list (list 1 2) (list 3 4) (list 5 6))) '(3 4)
> (assoc 9 (list (list 1 2) (list 3 4) (list 5 6))) #f
> (assoc 3.5 (list (list 1 2) (list 3 4) (list 5 6)) (lambda (a b) (< (abs (- a b)) 1))) '(3 4)
4.10.6 Pair Accessor Shorthands
> (caar '((1 2) 3 4)) 1
> (cadr '((1 2) 3 4)) 3
> (cdar '((7 6 5 4 3 2 1) 8 9)) '(6 5 4 3 2 1)
> (cddr '(2 1)) '()
> (caaar '(((6 5 4 3 2 1) 7) 8 9)) 6
> (caadr '(9 (7 6 5 4 3 2 1) 8)) 7
> (cadar '((7 6 5 4 3 2 1) 8 9)) 6
> (caddr '(3 2 1)) 1
> (cdaar '(((6 5 4 3 2 1) 7) 8 9)) '(5 4 3 2 1)
> (cdadr '(9 (7 6 5 4 3 2 1) 8)) '(6 5 4 3 2 1)
> (cddar '((7 6 5 4 3 2 1) 8 9)) '(5 4 3 2 1)
> (cdddr '(3 2 1)) '()
> (caaaar '((((5 4 3 2 1) 6) 7) 8 9)) 5
> (caaadr '(9 ((6 5 4 3 2 1) 7) 8)) 6
> (caadar '((7 (5 4 3 2 1) 6) 8 9)) 5
> (caaddr '(9 8 (6 5 4 3 2 1) 7)) 6
> (cadaar '(((6 5 4 3 2 1) 7) 8 9)) 5
> (cadadr '(9 (7 6 5 4 3 2 1) 8)) 6
> (caddar '((7 6 5 4 3 2 1) 8 9)) 5
> (cadddr '(4 3 2 1)) 1
> (cdaaar '((((5 4 3 2 1) 6) 7) 8 9)) '(4 3 2 1)
> (cdaadr '(9 ((6 5 4 3 2 1) 7) 8)) '(5 4 3 2 1)
> (cdadar '((7 (5 4 3 2 1) 6) 8 9)) '(4 3 2 1)
> (cdaddr '(9 8 (6 5 4 3 2 1) 7)) '(5 4 3 2 1)
> (cddaar '(((6 5 4 3 2 1) 7) 8 9)) '(4 3 2 1)
> (cddadr '(9 (7 6 5 4 3 2 1) 8)) '(5 4 3 2 1)
> (cdddar '((7 6 5 4 3 2 1) 8 9)) '(4 3 2 1)
> (cddddr '(4 3 2 1)) '()
4.10.7 Additional List Functions and Synonyms
(require racket/list) | package: base |
> (cons? '(1 2)) #t
> (first '(1 2 3 4 5 6 7 8 9 10)) 1
> (rest '(1 2 3 4 5 6 7 8 9 10)) '(2 3 4 5 6 7 8 9 10)
> (second '(1 2 3 4 5 6 7 8 9 10)) 2
> (third '(1 2 3 4 5 6 7 8 9 10)) 3
> (fourth '(1 2 3 4 5 6 7 8 9 10)) 4
> (fifth '(1 2 3 4 5 6 7 8 9 10)) 5
> (sixth '(1 2 3 4 5 6 7 8 9 10)) 6
> (seventh '(1 2 3 4 5 6 7 8 9 10)) 7
> (eighth '(1 2 3 4 5 6 7 8 9 10)) 8
> (ninth '(1 2 3 4 5 6 7 8 9 10)) 9
> (tenth '(1 2 3 4 5 6 7 8 9 10)) 10
This function takes time proportional to the length of lst.
> (last '(1 2 3 4 5 6 7 8 9 10)) 10
This function takes time proportional to the “length” of p.
> (last-pair '(1 2 3 4)) '(4)
procedure
k : exact-nonnegative-integer? v : any/c
> (make-list 7 'foo) '(foo foo foo foo foo foo foo)
procedure
(list-update lst pos updater) → list?
lst : list? pos : (and/c (>=/c 0) (</c (length lst))) updater : (-> any/c any/c)
This function takes time proportional to pos.
> (list-update '(zero one two) 1 symbol->string) '(zero "one" two)
Added in version 6.3 of package base.
This function takes time proportional to pos.
> (list-set '(zero one two) 2 "two") '(zero one "two")
Added in version 6.3 of package base.
procedure
(index-of lst v [is-equal?]) → (or/c exact-nonnegative-integer? #f)
lst : list? v : any/c is-equal? : (any/c any/c . -> . any/c) = equal?
> (index-of '(1 2 3 4) 3) 2
Added in version 6.7.0.3 of package base.
procedure
(index-where lst proc) → (or/c exact-nonnegative-integer? #f)
lst : list? proc : (any/c . -> . any/c)
> (index-where '(1 2 3 4) even?) 1
Added in version 6.7.0.3 of package base.
procedure
(indexes-of lst v [is-equal?])
→ (listof exact-nonnegative-integer?) lst : list? v : any/c is-equal? : (any/c any/c . -> . any/c) = equal?
> (indexes-of '(1 2 1 2 1) 2) '(1 3)
Added in version 6.7.0.3 of package base.
procedure
(indexes-where lst proc) → (listof exact-nonnegative-integer?)
lst : list? proc : (any/c . -> . any/c)
> (indexes-where '(1 2 3 4) even?) '(1 3)
Added in version 6.7.0.3 of package base.
procedure
lst : any/c pos : exact-nonnegative-integer?
The lst argument need not actually be a list; lst must merely start with a chain of at least pos pairs.
This function takes time proportional to pos.
procedure
lst : any/c pos : exact-nonnegative-integer?
procedure
(split-at lst pos) →
list? any/c lst : any/c pos : exact-nonnegative-integer?
except that it can be faster, but it will still take time proportional to pos.
procedure
lst : any/c pred : procedure?
The lst argument need not actually be a list; the chain of pairs in lst will be traversed until a non-pair is encountered.
procedure
lst : any/c pred : procedure?
procedure
(splitf-at lst pred) →
list? any/c lst : any/c pred : procedure?
except that it can be faster.
procedure
(take-right lst pos) → any/c
lst : any/c pos : exact-nonnegative-integer?
The lst argument need not actually be a list; lst must merely end with a chain of at least pos pairs.
This function takes time proportional to the length of lst.
> (take-right '(1 2 3 4 5) 2) '(4 5)
> (take-right 'non-list 0) 'non-list
procedure
(drop-right lst pos) → list?
lst : any/c pos : exact-nonnegative-integer?
The lst argument need not actually be a list; lst must merely end with a chain of at least pos pairs.
This function takes time proportional to the length of lst.
> (drop-right '(1 2 3 4 5) 2) '(1 2 3)
> (drop-right 'non-list 0) '()
procedure
(split-at-right lst pos) →
list? any/c lst : any/c pos : exact-nonnegative-integer?
(values (drop-right lst pos) (take-right lst pos))
except that it can be faster, but it will still take time proportional to the length of lst.
> (split-at-right '(1 2 3 4 5 6) 3)
'(1 2 3)
'(4 5 6)
> (split-at-right '(1 2 3 4 5 6) 4)
'(1 2)
'(3 4 5 6)
procedure
(takef-right lst pred) → any/c
lst : any/c pred : procedure?
procedure
(dropf-right lst pred) → list?
lst : any/c pred : procedure?
procedure
(splitf-at-right lst pred) →
list? any/c lst : any/c pred : procedure?
procedure
(list-prefix? l r [same?]) → boolean?
l : list? r : list? same? : (any/c any/c . -> . any/c) = equal?
> (list-prefix? '(1 2) '(1 2 3 4 5)) #t
Added in version 6.3 of package base.
procedure
(take-common-prefix l r [same?]) → list?
l : list? r : list? same? : (any/c any/c . -> . any/c) = equal?
> (take-common-prefix '(a b c d) '(a b x y z)) '(a b)
Added in version 6.3 of package base.
procedure
(drop-common-prefix l r [same?]) →
list? list? l : list? r : list? same? : (any/c any/c . -> . any/c) = equal?
> (drop-common-prefix '(a b c d) '(a b x y z))
'(c d)
'(x y z)
Added in version 6.3 of package base.
procedure
(split-common-prefix l r [same?]) →
list? list? list? l : list? r : list? same? : (any/c any/c . -> . any/c) = equal?
> (split-common-prefix '(a b c d) '(a b x y z))
'(a b)
'(c d)
'(x y z)
Added in version 6.3 of package base.
procedure
(add-between lst v [ #:before-first before-first #:before-last before-last #:after-last after-last #:splice? splice?]) → list? lst : list? v : any/c before-first : list? = '() before-last : any/c = v after-last : list? = '() splice? : any/c = #f
If splice? is true, then v and before-last should be lists, and the list elements are spliced into the result. In addition, when splice? is true, before-first and after-last are inserted before the first element and after the last element respectively.
> (add-between '(x y z) 'and) '(x and y and z)
> (add-between '(x) 'and) '(x)
> (add-between '("a" "b" "c" "d") "," #:before-last "and") '("a" "," "b" "," "c" "and" "d")
> (add-between '(x y z) '(-) #:before-last '(- -) #:before-first '(begin) #:after-last '(end LF) #:splice? #t) '(begin x - y - - z end LF)
> (append* '(a) '(b) '((c) (d))) '(a b c d)
> (cdr (append* (map (lambda (x) (list ", " x)) '("Alpha" "Beta" "Gamma")))) '("Alpha" ", " "Beta" ", " "Gamma")
procedure
(check-duplicates lst [ same? #:key extract-key #:default failure-result]) → any lst : list? same? : (any/c any/c . -> . any/c) = equal? extract-key : (-> any/c any/c) = (lambda (x) x) failure-result : failure-result/c = (lambda () #f)
If no duplicate is found, then failure-result determines the result:
If failure-result is a procedure, it is called (through a tail call) with no arguments to produce the result.
Otherwise, failure-result is returned as the result.
The same? argument should be an equivalence predicate such as equal? or eqv? or a dictionary. The procedures equal?, eqv?, and eq? automatically use a dictionary for speed.
> (check-duplicates '(1 2 3 4)) #f
> (check-duplicates '(1 2 3 2 1)) 2
> (check-duplicates '((a 1) (b 2) (a 3)) #:key car) '(a 3)
> (check-duplicates '(1 2 3 4 5 6) (lambda (x y) (equal? (modulo x 3) (modulo y 3)))) 4
> (check-duplicates '(1 2 3 4) #:default "no duplicates") "no duplicates"
Added in version 6.3 of package base.
Changed in version 6.11.0.2: Added the #:default optional argument.
procedure
(remove-duplicates lst [ same? #:key extract-key]) → list? lst : list? same? : (any/c any/c . -> . any/c) = equal? extract-key : (any/c . -> . any/c) = (lambda (x) x)
The #:key argument extract-key is used to extract a key value from each list element, so two items are considered equal if (same? (extract-key x) (extract-key y)) is true.
> (remove-duplicates '(a b b a)) '(a b)
> (remove-duplicates '(1 2 1.0 0)) '(1 2 1.0 0)
> (remove-duplicates '(1 2 1.0 0) =) '(1 2 0)
procedure
(filter-map proc lst ...+) → list?
proc : procedure? lst : list?
> (filter-map (lambda (x) (and (negative? x) (abs x))) '(1 2 -3 -4 8)) '(3 4)
procedure
(count proc lst ...+) → exact-nonnegative-integer?
proc : procedure? lst : list?
procedure
(partition pred lst) →
list? list? pred : procedure? lst : list?
The result is the same as
but pred is applied to each item in lst only once.
The resulting list holds numbers starting at start and whose successive elements are computed by adding step to their predecessor until end (excluded) is reached. If no starting point is provided, 0 is used. If no step argument is provided, 1 is used.
Like in-range, a range application can provide better performance when it appears directly in a for clause.
> (range 10) '(0 1 2 3 4 5 6 7 8 9)
> (range 10 20) '(10 11 12 13 14 15 16 17 18 19)
> (range 20 40 2) '(20 22 24 26 28 30 32 34 36 38)
> (range 20 10 -1) '(20 19 18 17 16 15 14 13 12 11)
> (range 10 15 1.5) '(10 11.5 13.0 14.5)
Changed in version 6.7.0.4 of package base: Adjusted to cooperate with for in the same way that in-range does.
procedure
(inclusive-range start end [step]) → list?
start : real? end : real? step : real? = 1
The resulting list holds numbers starting at start and whose successive elements are computed by adding step to their predecessor until end (included) is reached. If no step argument is provided, 1 is used.
Like in-inclusive-range, an inclusive-range application can provide better performance when it appears directly in a for clause.
> (inclusive-range 10 20) '(10 11 12 13 14 15 16 17 18 19 20)
> (inclusive-range 20 40 2) '(20 22 24 26 28 30 32 34 36 38 40)
> (inclusive-range 20 10 -1) '(20 19 18 17 16 15 14 13 12 11 10)
> (inclusive-range 10 15 1.5) '(10 11.5 13.0 14.5)
Added in version 8.0.0.13 of package base.
procedure
(append-map proc lst ...+) → list?
proc : procedure? lst : list?
> (append-map vector->list '(#(1) #(2 3) #(4))) '(1 2 3 4)
> (filter-not even? '(1 2 3 4 5 6)) '(1 3 5)
> (shuffle '(1 2 3 4 5 6)) '(6 3 4 2 1 5)
> (shuffle '(1 2 3 4 5 6)) '(4 5 6 3 2 1)
> (shuffle '(1 2 3 4 5 6)) '(5 1 6 4 3 2)
procedure
(combinations lst) → list?
lst : list? (combinations lst size) → list? lst : list? size : exact-nonnegative-integer?
Wikipedia combinations
> (combinations '(1 2 3)) '(() (1) (2) (1 2) (3) (1 3) (2 3) (1 2 3))
> (combinations '(1 2 3) 2) '((1 2) (1 3) (2 3))
procedure
(in-combinations lst) → sequence?
lst : list? (in-combinations lst size) → sequence? lst : list? size : exact-nonnegative-integer?
> (time (begin (combinations (range 15)) (void))) cpu time: 10 real time: 4 gc time: 0
> (time (begin (in-combinations (range 15)) (void))) cpu time: 0 real time: 0 gc time: 0
procedure
(permutations lst) → list?
lst : list?
> (permutations '(1 2 3)) '((1 2 3) (2 1 3) (1 3 2) (3 1 2) (2 3 1) (3 2 1))
> (permutations '(x x)) '((x x) (x x))
procedure
(in-permutations lst) → sequence?
lst : list?
> (argmin car '((3 pears) (1 banana) (2 apples))) '(1 banana)
> (argmin car '((1 banana) (1 orange))) '(1 banana)
> (argmax car '((3 pears) (1 banana) (2 apples))) '(3 pears)
> (argmax car '((3 pears) (3 oranges))) '(3 pears)
Added in version 6.3 of package base.
procedure
(cartesian-product lst ...) → (listof list?)
lst : list?
> (cartesian-product '(1 2 3) '(a b c)) '((1 a) (1 b) (1 c) (2 a) (2 b) (2 c) (3 a) (3 b) (3 c))
> (cartesian-product '(4 5 6) '(d e f) '(#t #f))
'((4 d #t)
(4 d #f)
(4 e #t)
(4 e #f)
(4 f #t)
(4 f #f)
(5 d #t)
(5 d #f)
(5 e #t)
(5 e #f)
(5 f #t)
(5 f #f)
(6 d #t)
(6 d #f)
(6 e #t)
(6 e #f)
(6 f #t)
(6 f #f))
Added in version 6.3 of package base.
procedure
pred : procedure? lst : list?
Added in version 6.3 of package base.
procedure
pred : procedure? lst : list?
Added in version 6.3 of package base.
4.10.8 Immutable Cyclic Data
procedure
(make-reader-graph v) → any/c
v : any/c
Since the copied values can be immutable, and since the copy is also immutable, make-reader-graph can create cycles involving only immutable pairs, vectors, boxes, and hash tables.
Only the following kinds of values are copied and traversed to detect placeholders:
pairs
vectors, both mutable and immutable
boxes, both mutable and immutable
hash tables, both mutable and immutable
instances of a prefab structure type
placeholders created by make-placeholder and make-hash-placeholder
Due to these restrictions, make-reader-graph creates exactly the same sort of cyclic values as read.
> (let* ([ph (make-placeholder #f)] [x (cons 1 ph)]) (placeholder-set! ph x) (make-reader-graph x)) #0='(1 . #0#)
procedure
(placeholder? v) → boolean?
v : any/c
procedure
(make-placeholder v) → placeholder?
v : any/c
procedure
(placeholder-set! ph datum) → void?
ph : placeholder? datum : any/c
procedure
(placeholder-get ph) → any/c
ph : placeholder?
procedure
(hash-placeholder? v) → boolean?
v : any/c
procedure
(make-hash-placeholder assocs) → hash-placeholder?
assocs : (listof pair?)
procedure
(make-hasheq-placeholder assocs) → hash-placeholder?
assocs : (listof pair?)
procedure
(make-hasheqv-placeholder assocs) → hash-placeholder?
assocs : (listof pair?)