本章介绍了 Chez Scheme 中针对非数值对象的特定操作,包括 pairs, numbers 等标准对象,以及 boxes, records 等 Chez Scheme 扩展对象。第 8 章介绍了针对数字的操作。关于对象的标准操作的更多描述,参见“The Scheme Programming Language,第 4 版”的第 6 章,或 Scheme 第 6 修订版的报告。
过程: (enum-set? obj)
返回: 如果 obj
为 enum set
则为 #t
, 否则为 #f
.
库: (chezscheme)
此一谓词应当被定义,但缺失于第 6 修订版中。
过程: (record-constructor-descriptor? obj)
返回: 如果 obj
为 record constructor
则为 #t
, 否则为 #f
.
库: (chezscheme)
此一谓词应当被定义,但缺失于第 6 修订版中。
过程: (atom? obj)
返回: 如果 obj
不是点对,则为 #t
, 否则为 #f
.
库: (chezscheme)
atom? 等价于 (lambda (x) (not (pair? x))).
(atom? '(a b c)) => #f
(atom? '(3 . 4)) => #f
(atom? '()) => #t
(atom? 3) => #t
过程: (list-head list n)
返回: 列表前 n 个元素的列表
库: (chezscheme)
n 须为一精确非负整数,且小于或等于列表的长度。
list-head
可以和另一个 Scheme 标准过程 list-tail
配合,用于将一个列表分割成两个单独的列表。不过, list-tail
并不进行空间分配,只是返回原列表的子列表,而 list-head
则总是返回原列表前面部分的一份拷贝。
list-head
可依如下定义:
(define list-head
(lambda (ls n)
(if (= n 0)
'()
(cons (car ls) (list-head (cdr ls) (- n 1))))))
(list-head '(a b c) 0) => ()
(list-head '(a b c) 2) => (a b)
(list-head '(a b c) 3) => (a b c)
(list-head '(a b c . d) 2) => (a b)
(list-head '(a b c . d) 3) => (a b c)
(list-head '#1=(a . #1#) 5) => (a a a a a)
过程: (last-pair list)
返回: 列表的最后一个点对
库: (chezscheme)
列表不可为空。 last-pair
返回列表的最后一个点对(不是最后一个元素)。列表可以是一个不严格的列表(improper list),此时,最后一个点对为一个包含最末元素和结束对象的点对。
(last-pair '(a b c d)) => (d)
(last-pair '(a b c . d)) => (c . d)
过程: (list-copy list)
返回: 列表的一份拷贝
库: (chezscheme)
list-copy
返回与列表相等( equal?
)的一个列表,使用新的点对重新构成顶层的列表结构。
(list-copy '(a b c)) => (a b c)
(let ([ls '(a b c)])
(equal? ls (list-copy ls))) => #t
(let ([ls '(a b c)])
(let ([ls-copy (list-copy ls)])
(or (eq? ls-copy ls)
(eq? (cdr ls-copy) (cdr ls))
(eq? (cddr ls-copy) (cddr ls))))) => #f
过程: (list* obj … final-obj)
返回: 由 obj ...
组成的列表,结束于 final-obj
库: (chezscheme)
list*
和第 6 修订版中的 cons*
是一样的。
过程: (make-list n)
过程: (make-list n obj)
返回: n 个对象的列表
库: (chezscheme)
n 须为非负整数。如果省略 obj,则列表的元素为未指定的。
(make-list 0 '()) => ()
(make-list 3 0) => (0 0 0)
(make-list 2 "hi") => ("hi" "hi")
过程: (iota n)
返回: 从 0(包含)到 n(不包含)的整数列表
库: (chezscheme)
n 须为精确的非负整数。
(iota 0) => ()
(iota 5) => (0 1 2 3 4)
过程: (enumerate ls)
返回: 从 0(包含)到长度 ls(不包含)的整数列表。
库: (chezscheme)
(enumerate '()) => ()
(enumerate '(a b c)) => (0 1 2)
(let ([ls '(a b c)])
(map cons ls (enumerate ls))) => ((a . 0) (b . 1) (c . 2))
过程: (remq! obj list)
过程: (remv! obj list)
过程: (remove! obj list)
返回: 列表中所有 obj
都被移除后的列表
库: (chezscheme)
这些过程与第 6 修订版中的 remq
, remv
, 及 remove
过程类似,只是 remq!
, remv!
和 remove!
使用输入列表中的点对来构成输出列表。它们进行较少的空间分配,但并不一定比它们非破坏性的相应版本更快。如果滥用,很容易导致混乱或错误的结果。
(remq! 'a '(a b a c a d)) => (b c d)
(remv! #\a '(#\a #\b #\c)) => (#\b #\c)
(remove! '(c) '((a) (b) (c))) => ((a) (b))
过程: (substq new old tree)
过程: (substv new old tree)
过程: (subst new old tree)
过程: (substq! new old tree)
过程: (substv! new old tree)
过程: (subst! new old tree)
返回: old 被替换为 new 后的树
库: (chezscheme)
这些过程遍历树,以对象 new 替换树中所有与对象 old 相等的对象。
对于 substq
和 substq!
,相等性测试是基于 eq?
, substv
和 substv!
是基于 eqv?
, 而 subst
和 subst!
是基于 equal?
.
substq!
, substv!
, 和 subst!
执行破坏性的替换。它们进行较少的空间分配,但并不一定比它们非破坏性的对应版本更快。如果滥用,很容易导致混乱或错误的结果。
(substq 'a 'b '((b c) b a)) => ((a c) a a)
(substv 2 1 '((1 . 2) (1 . 4) . 1)) => ((2 . 2) (2 . 4) . 2)
(subst 'a
'(a . b)
'((a . b) (c a . b) . c)) => (a (c . a) . c)
(let ([tr '((b c) b a)])
(substq! 'a 'b tr)
tr) => ((a c) a a)
过程: (reverse! list)
返回: 对原列表反向排序的列表
库: (chezscheme)
reverse!
通过反转其链接破坏性地反向排序列表。以 reverse!
取代 reverse
减少了空间分配,但并不一定比使用 reverse
更快。如果滥用,会很容易导致混乱或错误的结果。
(reverse! '()) => ()
(reverse! '(a b c)) => (c b a)
(let ([x '(a b c)])
(reverse! x)
x) => (a)
(let ([x '(a b c)])
(set! x (reverse! x))
x) => (c b a)
过程: (append! list …)
返回: 输入列表的串联
库: (chezscheme)
如同 append
, append!
返回一个新的列表,其中元素依次为第一个列表中的元素,第二个列表中的元素,第三个列表中的元素,等等。不同之处在于, append!
重用所有参数中的点对以构造新列表。即,每一个列表参数的最后一个 cdr
, 其后一元素变为指向下一个列表参数。除最后一个参数外,如果任一参数为空列表,它实质上会被忽略。最后一个参数(并不一定得是列表)是不变的。
相比于 append
, append!
进行更少的空间分配,但并不一定更快。如果滥用,会很容易导致混乱或错误的结果。
(append! '(a b) '(c d)) => (a b c d)
(let ([x '(a b)])
(append! x '(c d))
x) => (a b c d)
Chez Scheme 以两种方式扩展了字符的句法。其一,前缀 #\
后面紧跟 3 位八进制数字会被读取为一个字符,其数字编码即为此 3 位数的八进制值,例如, #\044
被读取为 #\$
. 其二,它可以识别若干非标准命名的字符: #\rubout
(等同于 #\delete
), #\bel
(等同于 #\alarm
), #\vt
(等同于 #\vtab
), #\nel
(Unicode NEL 字符), 以及 #\ls
(Unicode LS 字符). 非标准字符的名字可以通过过程 char-name
更改(参见 9.14 节)。
读取器若遇到 #!r6rs
,则会在其后的输入流中禁用这些扩展,除非在更近的位置遇到 #!chezscheme
.
过程: (char=? char1 char2 …)
过程: (char<? char1 char2 …)
过程: (char>? char1 char2 …)
过程: (char<=? char1 char2 …)
过程: (char>=? char1 char2 …)
过程: (char-ci=? char1 char2 …)
过程: (char-ci<? char1 char2 …)
过程: (char-ci>? char1 char2 …)
过程: (char-ci<=? char1 char2 …)
过程: (char-ci>=? char1 char2 …)
返回: 如果关系成立,则为 #t
, 否则为 #f
.
库: (chezscheme)
这些谓词与第 6 修订版中的对应版本是一样的,只是被扩展为接受一个以上参数,而非两个以上参数。当只传入一个参数时,这些谓词均返回 #t
.
(char>? #\a) => #t
(char<? #\a) => #t
(char-ci=? #\a) => #t
过程: (char- char1 char2)
返回: char1
和 char2
间的整数差值
库: (chezscheme)
char-
以 char1
的整数值减去 char2
的整数值,并返回差值。后面的例子假设以字符的 ASCII 码作为其整数表示。
(char- #\f #\e) => 1
(define digit-value
; 根据基数 r 返回数字 c 的值,
; 如果 c 不是有效的数字,则返回 #f
(lambda (c r)
(let ([v (cond
[(char<=? #\0 c #\9) (char- c #\0)]
[(char<=? #\A c #\Z) (char- c #\7)]
[(char<=? #\a c #\z) (char- c #\W)]
[else 36])])
(and (fx< v r) v))))
(digit-value #\8 10) => 8
(digit-value #\z 10) => #f
(digit-value #\z 36) => 35
char-
可依如下定义。
(define char-
(lambda (c1 c2)
(- (char->integer c1) (char->integer c2))))
基于标准的字符串句法, Chez Scheme 增加了两种转义字符: \'
生成单引号字符,以及 \nnn
, 即,反斜杠紧跟着 3 位 8 进制数,生成等同于此 3 位 8 进制数的值的字符。读取器若遇到 #!r6rs
,则会在其后的输入流中禁用这些扩展,除非在更近的位置遇到 #!chezscheme
.
所有字符串默认是可变的,包括常量。程序可以通过 string->immutable-string
创建不可变字符串。尝试修改不可变字符串会导致抛出异常。
在 Chez Scheme 中,字符串的长度和索引总是定长数。
过程: (string=? string1 string2 string3 …)
过程: (string<? string1 string2 string3 …)
过程: (string>? string1 string2 string3 …)
过程: (string<=? string1 string2 string3 …)
过程: (string>=? string1 string2 string3 …)
过程: (string-ci=? string1 string2 string3 …)
过程: (string-ci<? string1 string2 string3 …)
过程: (string-ci>? string1 string2 string3 …)
过程: (string-ci<=? string1 string2 string3 …)
过程: (string-ci>=? string1 string2 string3 …)
返回: 如果关系成立,则为 #t
, 否则为 #f
.
库: (chezscheme)
这些谓词与第 6 修订版中的对应版本是一样的,只是被扩展为接受一个以上参数,而非两个以上参数。当只传入一个参数时,这些谓词均返回 #t
.
(string>? "a") => #t
(string<? "a") => #t
(string-ci=? "a") => #t
过程: (string-copy! src src-start dst dst-start n)
返回: 未定义
库: (chezscheme)
src
和 dst
必须是字符串,且 dst
必须可变。 src-start
, dst-start
, 以及 n
必须是精确的非负整数。 src-start
和 n
的和绝对不能超过 src
的长度, 而 dst-start
和 n
的和则一定不能超过 dst
的长度。
string-copy!
以 src
中起始于 src-start
,长度为 n
字节的部分,覆盖 dst
中起始于 dst-start
,长度为 n
字节的部分。即使 dst
和 src
是同一个字符串,且源和目标位置相互重叠,这个操作也能生效。即,在操作开始时,目标位置先被源字符串中的字符填充。
(define s1 "to boldly go")
(define s2 (make-string 10 #\-))
(string-copy! s1 3 s2 1 3)
s2 => "-bol------"
(string-copy! s1 7 s2 4 2)
s2 => "-bolly----"
(string-copy! s2 2 s2 5 4)
s2 => "-bollolly-"
过程: (substring-fill! string start end char)
返回: 未定义
库: (chezscheme)
string
必须是可变的。 string
于 start
(包含) 和 end
(不包含) 之间的字符均被设置为 char
. start
和 end
必须是非负整数; start
必须严格小于 string
的长度,而 end
可以小于或等于 string
的长度。如果 end ≤ start
, 则字符串保持不变。
(let ([str (string-copy "a tpyo typo")])
(substring-fill! str 2 6 #\X)
str) => "a XXXX typo"
过程: (string-truncate! string n)
返回: 字符串或空字符串
库: (chezscheme)
string
必须是可变的。 n
必须是精确的非负定长数,且不大于 string
的长度。如果 n
是 0, string-truncate!
返回空字符串。否则, string-truncate!
破坏性地把 string
缩短为其前 n
个字符,并返回 string
.
(define s (make-string 7 #\$))
(string-truncate! s 0) => ""
s => "$$$$$$$"
(string-truncate! s 3) => "$$$"
s => "$$$"
过程: (mutable-string? obj)
返回: 如果 obj
是可变字符串,则为 #t
, 否则为 #f
.
过程: (immutable-string? obj)
返回: 如果 obj
是不可变字符串,则为 #t
, 否则为 #f
.
库: (chezscheme)
(mutable-string? (string #\a #\b #\c)) => #t
(mutable-string? (string->immutable-string "abc")) => #f
(immutable-string? (string #\a #\b #\c)) => #f
(immutable-string? (string->immutable-string "abc")) => #t
(immutable-string? (cons 3 4)) => #f
过程: (string->immutable-string string)
返回: 与 string
相等(equal)的不可变字符串
库: (chezscheme)
如果 string
是不可变字符串,则结果为其本身;否则,结果是个不可变字符串,其内容与 string
相同。
(define s (string->immutable-string (string #\x #\y #\z)))
(string-set! s 0 #\a) => exception: not mutable
Chez Scheme 扩展了向量的句法,以允许在 #
和左括号之间指定向量的长度,形如, #3(a b c)
. 如果在此语法形式下提供的向量元素比指定的长度要少,则之后的每个元素都与最后一个提供的元素相同。读取器若遇到 #!r6rs
,则会在其后的输入流中禁用这些扩展,除非在更近的位置遇到 #!chezscheme
.
在 Chez Scheme 中,向量的长度和索引总是定长数。
所有向量默认都是可变的,包括常量。程序可以通过 vector->immutable-vector
创建不可变向量。尝试修改不可变向量会导致抛出异常。
过程: (vector-copy vector)
返回: vector
的一份拷贝
库: (chezscheme)
vector-copy
生成一个长度和内容都和 vector
相同的新向量。里面的元素本身不是被复制的。
(vector-copy '#(a b c)) => #(a b c)
(let ([v '#(a b c)])
(eq? v (vector-copy v))) => #f
过程: (vector-set-fixnum! vector n fixnum)
返回: 未定义
库: (chezscheme)
vector
必须是不可变的。 vector-set-fixnum!
把向量的第 n
个元素变更为 fixnum
. n
必须是一个确切的非负整数,且严格小于 vector
的长度。
储存定长数要比储存任意值快,因为对于任意值,系统需要记录从老到新的各个对象的潜在分配,以支持分代垃圾回收。不过,必须小心确保参数确实是一个定长数;否则,收集器可能无法正确地追踪资源分配。只要不在优化级别 3, 基本过程会对参数进行定长数检验。
参见后面关于全定长数向量 (fxvectors) 的描述。
(let ([v (vector 1 2 3 4 5)])
(vector-set-fixnum! v 2 73)
v) => #(1 2 73 4 5)
过程: (vector-cas! vector n old-obj new-obj)
返回: 如果 vector
有改变,则为 #t
, 否则为 #f
.
库: (chezscheme)
vector
必须是可变的。 若 vector
的第 n
个元素和 old-obj
相同( eq?
), 则 vector-cas!
自动将此元素替换为 new-obj
, 若不相同,则 vector
保持不变。
(define v (vector 'old0 'old1 'old2))
(vector-cas! v 1 'old1 'new1) => #t
(vector-ref v 1) => 'new1
(vector-cas! v 2 'old1 'new2) => #f
(vector-ref v 2) => 'old2
过程: (mutable-vector? obj)
返回: 如果 obj
是可变向量,则为 #t
, 否则为 #f
.
过程: (immutable-vector? obj)
返回: 如果 obj
是不可变向量,则为 #t
, 否则为 #f
.
库: (chezscheme)
(mutable-vector? (vector 1 2 3)) => #t
(mutable-vector? (vector->immutable-vector (vector 1 2 3))) => #f
(immutable-vector? (vector 1 2 3)) => #f
(immutable-vector? (vector->immutable-vector (vector 1 2 3))) => #t
(immutable-vector? (cons 3 4)) => #f
过程: (vector->immutable-vector vector)
返回: 与 vector
相等(equal)的一个不可变向量
库: (chezscheme)
如果 vector
是不可变向量,则结果为其本身;否则,结果是与 vector
内容相同的一个不可变向量。
(define v (vector->immutable-vector (vector 1 2 3)))
(vector-set! v 0 0) => exception: not mutable
定长数向量, 即 "fxvectors", 类似于向量,但只包含定长数。定长数向量的输出形式以前缀 #vfx
替换向量的前缀 #
, 例如, #vfx(1 2 3)
或 #10vfx(2)
. 读取器若遇到 #!r6rs
,则会在其后的输入流中禁用定长数向量的句法,除非在更近的位置遇到 #!chezscheme
.
定长数向量的长度和索引总是定长数。
更新定长数向量通常比更新向量节省资源,因为对于向量来说,系统需要记录从老到新的各个对象的潜在分配,以支持分代垃圾回收。定长数向量不包含要指向内存某一区域的指针,受益于此,存储管理系统不需要在垃圾收集期间对这些指针进行追踪。
定长数向量默认是可变的,包括常量。程序可以通过 fxvector->immutable-fxvector
创建不可变的定长数向量。尝试修改一个不可变的定长数向量会导致异常抛出。
可参考前述的 vector-set-fixnum!
.
过程: (fxvector? obj)
返回: 如果 obj
是一个定长数向量,则为 #t
, 否则为 #f
.
库: (chezscheme)
(fxvector? #vfx()) => #t
(fxvector? #vfx(1 2 3)) => #t
(fxvector? (fxvector 1 2 3)) => #t
(fxvector? '#(a b c)) => #f
(fxvector? '(a b c)) => #f
(fxvector? "abc") => #f
过程: (fxvector fixnum …)
返回: 一个由参数中的所有定长数 fixnum ...
组成的定长数向量
库: (chezscheme)
(fxvector) => #vfx()
(fxvector 1 3 5) => #vfx(1 3 5)
过程: (make-fxvector n)
过程: (make-fxvector n fixnum)
返回: 一个长度为 n
的定长数向量
库: (chezscheme)
n
必须是定长数。如果有提供参数 fixnum
, 则定长数向量中的每个元素都被初始化为 fixnum
; 不然,其中元素则均为未定义。
(make-fxvector 0) => #vfx()
(make-fxvector 0 7) => #vfx()
(make-fxvector 5 7) => #vfx(7 7 7 7 7)
过程: (fxvector-length fxvector)
返回: fxvector
中的元素个数
库: (chezscheme)
(fxvector-length #vfx()) => 0
(fxvector-length #vfx(1 2 3)) => 3
(fxvector-length #10vfx(1 2 3)) => 10
(fxvector-length (fxvector 1 2 3 4)) => 4
(fxvector-length (make-fxvector 300)) => 300
过程: (fxvector-ref fxvector n)
返回: fxvector
中的第 n
个元素 (索引基于 0)
库: (chezscheme)
n
必须是一个非负定长数,且严格小于 fxvector
的长度。
(fxvector-ref #vfx(-1 2 4 7) 0) => -1
(fxvector-ref #vfx(-1 2 4 7) 1) => 2
(fxvector-ref #vfx(-1 2 4 7) 3) => 7
过程: (fxvector-set! fxvector n fixnum)
返回: 未定义
库: (chezscheme)
fxvector
必须是可变的。 n
必须是一个非负定长数,且严格小于 fxvector
的长度。 fxvector-set!
把 fxvector
中的第 n
个元素修改为 fixnum
.
(let ([v (fxvector 1 2 3 4 5)])
(fxvector-set! v 2 (fx- (fxvector-ref v 2)))
v) => #vfx(1 2 -3 4 5)
过程: (fxvector-fill! fxvector fixnum)
返回: 未定义
库: (chezscheme)
fxvector
必须是可变的。 fxvector-fill!
把 fxvector
中的每个元素替换为 fixnum
.
(let ([v (fxvector 1 2 3)])
(fxvector-fill! v 0)
v) => #vfx(0 0 0)
过程: (fxvector->list fxvector)
返回: fxvector
中所有元素组成的列表
库: (chezscheme)
(fxvector->list (fxvector)) => ()
(fxvector->list #vfx(7 5 2)) => (7 5 2)
(let ([v #vfx(1 2 3 4 5)])
(apply fx* (fxvector->list v))) => 120
过程: (list->fxvector list)
返回: list
中所有元素组成的定长数向量
库: (chezscheme)
list
必须完全由定长数组成。
(list->fxvector '()) => #vfx()
(list->fxvector '(3 5 7)) => #vfx(3 5 7)
(let ([v #vfx(1 2 3 4 5)])
(let ([ls (fxvector->list v)])
(list->fxvector (map fx* ls ls)))) => #vfx(1 4 9 16 25)
过程: (fxvector-copy fxvector)
返回: fxvector
的一份拷贝
库: (chezscheme)
fxvector-copy
生成一个与 fxvector
长度和内容都一样的新的定长数向量。
(fxvector-copy #vfx(3 4 5)) => #vfx(3 4 5)
(let ([v #vfx(3 4 5)])
(eq? v (fxvector-copy v))) => #f
过程: (mutable-fxvector? obj)
返回: 如果 obj
是一个可变的定长数向量,则为 #t
, 否则为 #f
.
过程: (immutable-fxvector? obj)
返回: 如果 obj
是一个不可变的定长数向量,则为 #t
, 否则为 #f
.
库: (chezscheme)
(mutable-fxvector? (fxvector 1 2 3)) => #t
(mutable-fxvector? (fxvector->immutable-fxvector (fxvector 1 2 3))) => #f
(immutable-fxvector? (fxvector 1 2 3)) => #f
(immutable-fxvector? (fxvector->immutable-fxvector (fxvector 1 2 3))) => #t
(immutable-fxvector? (cons 3 4)) => #f
过程: (fxvector->immutable-fxvector fxvector)
返回: fxvector
的一份不可变的拷贝或其自身
库: (chezscheme)
如果 fxvector
是不可变的,则结果为其本身;否则,结果是与 fxvector
内容相同的一个不可变定长数向量。
(define v (fxvector->immutable-fxvector (fxvector 1 2 3)))
(fxvector-set! v 0 0) => exception: not mutable