6. 对象操作
本章介绍了针对各种对象的操作,包括列表,数字,字符,字符串,向量,字节向量,符号,布尔值,哈希表,以及枚举。第一节涵盖了常量对象和引用。第二节介绍了用于比较两个对象的通用等价性谓词,及判定对象类型的谓词。随后的几节介绍了主要用于处理上述某一类型对象的过程。本章没有介绍过程操作的内容,因为唯一一个专门为过程定义的操作是 application, 而它已经在第 5 章中介绍过了。端口操作在第 7 章关于输入输出的更一般性的讨论中会介绍。定义新数据类型的机制会在第 9 章中介绍。
6.1. 常量和引用
syntax: constant
返回: constant
constant
是任何自求值常量,即,数字,布尔值,字符,字符串,或字节向量。常量是不可变的;参见下文对引用的介绍中的注解。
3.2 => 3.2
#f => #f
#\c => #\c
"hi" => "hi"
#vu8(3 4 5) => #vu8(3 4 5)
syntax: (quote obj)
syntax: 'obj
返回: obj
库: (rnrs base), (rnrs)
'obj
等价于 (quote obj)
. Scheme 读取器(参见“读取”)会把缩写形式转化为完整形式。
quote
禁止了对 obj
的常规求值规则,使得 obj
可以被作为数据使用。虽然任何 Scheme 对象都可以被引用,但没有必要引用自求值常量,即,数字,布尔值,字符,字符串和字节向量。
引用的和自求值的常量是不可变的。即,程序不应通过 set-car!, string-set! 等过程修改常量,如果尝试做出这种修改,实现可以抛出一个条件类型的 &assertion 异常。如果修改不可变对象的尝试没有被检测到,程序的行为则为未定义的。一种实现可以选择使不同的常量共享存储,以节省空间。
(+ 2 3) => 5
'(+ 2 3) => (+ 2 3)
(quote (+ 2 3)) => (+ 2 3)
'a => a
'cons => cons
'() => ()
'7 => 7
syntax: (quasiquote obj ...)
syntax: `obj
syntax: (unquote obj ...)
syntax: ,obj
syntax: (unquote-splicing obj ...)
syntax: ,@obj
返回: 参见下文
库: (rnrs base), (rnrs)
`obj
等价于 (quasiquote obj)
, ,obj
等价于 (unquote obj)
, 而 ,@obj
等价于 (unquote-splicing obj)
. Scheme 读取器(参见“读取”)会把缩写形式转化为完整形式。
quasiquote
类似于 quote
, 但它允许引用内容的一部分被解除引用。在一个 quasiquote
表达式中, unquote
和 unquote-splicing
衍生形式被求值,而其它一切内容都是引用的,即,保留为未求值的。每个 unquote
衍生形式的值会替换掉 unquote
形式,并插入到输出中,而每个 unquote-splicing
衍生形式的值会被拼接进外围的列表或向量结构中。 unquote
和 unquote-splicing
只在 quasiquote
表达式中有效。
quasiquote
表达式可以嵌套,每个 quasiquote
引入一层新的引用,而每个 unquote
或 unquote-splicing
解除一层引用。如果一个表达式嵌套在 n 层 quasiquote
表达式中,则它必须通过 n 层 unquote
或 unquote-splicing
才能被求值。
`(+ 2 3) => (+ 2 3)
`(+ 2 ,(* 3 4)) => (+ 2 12)
`(a b (,(+ 2 3) c) d) => (a b (5 c) d)
`(a b ,(reverse '(c d e)) f g) => (a b (e d c) f g)
(let ([a 1] [b 2])
`(,a . ,b)) => (1 . 2)
`(+ ,@(cdr '(* 2 3))) => (+ 2 3)
`(a b ,@(reverse '(c d e)) f g) => (a b e d c f g)
(let ([a 1] [b 2])
`(,a ,@b)) => (1 . 2)
`#(,@(list 1 2 3)) => #(1 2 3)
'`,(cons 'a 'b) => `,(cons 'a 'b)
`',(cons 'a 'b) => '(a . b)
带有 0 个或多于 1 个子形式的 unquote
和 unquote-splicing
语法形式只在拼接环境(列表或向量)中有效。 (unquote obj ...)
等价于 (unquote obj) ...
, 而 (unquote-splicing obj ...)
等价于 (unquote-splicing obj) ...
. 这些形式主要作为 quasiquote
展开器输出的中间形式。它们支持某些有用的 quasiquote
嵌套惯用法 [3], 比如 ,@,@
, 在双重嵌套和双重求值的 quasiquote
表达式中使用时,有双重间接拼接的作用。
`(a (unquote) b) => (a b)
`(a (unquote (+ 3 3)) b) => (a 6 b)
`(a (unquote (+ 3 3) (* 3 3)) b) => (a 6 9 b)
(let ([x '(m n)]) ``(a ,@,@x f)) => `(a (unquote-splicing m n) f)
(let ([x '(m n)])
(eval `(let ([m '(b c)] [n '(d e)]) `(a ,@,@x f))
(environment '(rnrs)))) => (a b c d e f)
unquote
和 unquote-splicing
是 quasiquote
的辅助关键字。除了在被识别为辅助关键字的上下文中,在其它地方引用这些标识符是语法违规的。
6.2. 通用的等价性和类型谓词
本节介绍了基本的 Scheme 谓词(返回布尔值 #t 或 #f 的过程),用于判定对象的类型或两个对象的等价性。先讨论等价性谓词 eq?, eqv?, 和 equal?, 接下来是类型谓词。
过程: (eq? obj1 obj2)
返回: 如果 obj1
和 obj2
完全相同,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
在大多数 Scheme 系统中,如果两个对象内部通过相同的指针值表示,则被认为是完全相同的,如果它们在内部以不同的指针值表示,则被认为是不同的。不过也有采用其它标准的情况,比如利用时间戳判定。
虽然不同系统之间判定对象同一性的特定规则会有些微的不同,但以下规则总是成立的。
两个不同类型(布尔类型,空列表,点对,数字,字符,字符串,向量,符号,及过程)的对象是不同的。
两个内容或值不相同的同类型对象是不同的。
不论在哪里出现,布尔对象 #t 与其自身都是相同的。不论出现在哪里,#f 也与其自身相同。但#t 与#f 不同。
不论出现在哪里,空列表 () 都与其自身相同。
当且仅当两个符号的名字相同时(通过 string=?
判定),它们相同。
一个常量点对,向量,字符串或字节向量与其自身相同,比如通过应用 cons
, vector
, string
, make-bytevector
等过程创建的点对,向量,字符串或字节向量。通过对 cons
, vector
, string
, make-bytevector
等过程的不同应用创建的两个点对,向量,字符串或字节向量是不同的。由此可得的一个推论是,比如说,cons
,可以被用于创建一个和所有其它对象都不同的唯一性对象。
两个行为可能不同的过程是不同的。通过求值一个 lambda 表达式创建的过程与其自身相同。两个通过相同 lambda 表达式在不同时间创建的过程,或通过相似 lambda 表达式创建的过程,可能相同,也可能不同。
eq? 用于比较数字和字符时并不可靠。尽管每个不精确的数字与每个精确的数字都不同,但拥有相同值的两个精确的数字,两个不精确的数字,或两个字符,都可能相同,也可能不同。
由于常量对象是不可变的,即,程序不应通过 vector-set!, set-car!, 或任何其它结构修改操作修改它们,不同的引用常量或自求值字面量的全部或部分可能在内部由同一个对象表示。于是,当应用于不同的不可变常量的相等部分时,eq? 可能返回 #t.
eq? 最常用于比较符号,或检查分配对象的指针等价性,例如,点对,向量,或 record 实例。
(eq? 'a 3) => #f
(eq? #t 't) => #f
(eq? "abc" 'abc) => #f
(eq? "hi" '(hi)) => #f
(eq? #f '()) => #f
(eq? 9/2 7/2) => #f
(eq? 3.4 53344) => #f
(eq? 3 3.0) => #f
(eq? 1/3 #i1/3) => #f
(eq? 9/2 9/2) => 未定义
(eq? 3.4 (+ 3.0 .4)) => 未定义
(let ([x (* 12345678987654321 2)])
(eq? x x)) => 未定义
(eq? #\a #\b) => #f
(eq? #\a #\a) => 未定义
(let ([x (string-ref "hi" 0)])
(eq? x x)) => 未定义
(eq? #t #t) => #t
(eq? #f #f) => #t
(eq? #t #f) => #f
(eq? (null? '()) #t) => #t
(eq? (null? '(a)) #f) => #t
(eq? (cdr '(a)) '()) => #t
(eq? 'a 'a) => #t
(eq? 'a 'b) => #f
(eq? 'a (string->symbol "a")) => #t
(eq? '(a) '(b)) => #f
(eq? '(a) '(a)) => 未定义
(let ([x '(a . b)]) (eq? x x)) => #t
(let ([x (cons 'a 'b)])
(eq? x x)) => #t
(eq? (cons 'a 'b) (cons 'a 'b)) => #f
(eq? "abc" "cba") => #f
(eq? "abc" "abc") => 未定义
(let ([x "hi"]) (eq? x x)) => #t
(let ([x (string #\h #\i)]) (eq? x x)) => #t
(eq? (string #\h #\i)
(string #\h #\i)) => #f
(eq? '#vu8(1) '#vu8(1)) => 未定义
(eq? '#vu8(1) '#vu8(2)) => #f
(let ([x (make-bytevector 10 0)])
(eq? x x)) => #t
(let ([x (make-bytevector 10 0)])
(eq? x (make-bytevector 10 0))) => #f
(eq? '#(a) '#(b)) => #f
(eq? '#(a) '#(a)) => 未定义
(let ([x '#(a)]) (eq? x x)) => #t
(let ([x (vector 'a)])
(eq? x x)) => #t
(eq? (vector 'a) (vector 'a)) => #f
(eq? car car) => #t
(eq? car cdr) => #f
(let ([f (lambda (x) x)])
(eq? f f)) => #t
(let ([f (lambda () (lambda (x) x))])
(eq? (f) (f))) => 未定义
(eq? (lambda (x) x) (lambda (y) y)) => 未定义
(let ([f (lambda (x)
(lambda ()
(set! x (+ x 1))
x))])
(eq? (f 0) (f 0))) => #f
过程: (eqv? obj1 obj2)
返回: 如果 obj1
和 obj2
相等,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
eqv?
类似于 eq?
, 只是 eqv?
对以下情况确定返回 #t
: 被 char=?
判定为相等的两个字符,被 =
判定为相等 (a),且不能被 eq? 和 eqv? 以外的任何操作区分 (b) 的两个数字。(b) 的一个推论是,在区分 -0.0
和+0.0
的系统中(比如那些基于 IEEE 浮点算术的系统),虽然 (= -0.0 +0.0)
为 #t
, 但 (eqv? -0.0 +0.0)
为 #f
. 这是因为一些操作,比如 /
, 可以揭示出它们之间区别:
(/ 1.0 -0.0) => -inf.0
(/ 1.0 +0.0) => +inf.0
类似的,虽然 3.0
和 3.0+0.0i
在数值上被认为是相等的,但如果 -0.0
和 0.0
为不同的表示形式,则 eqv?
会判定它们为不相等。
(= 3.0+0.0i 3.0) => #t
(eqv? 3.0+0.0i 3.0) => #f
实参为 NaNs
时,eqv?
返回的布尔值是未定义的。
(eqv? +nan.0 (/ 0.0 0.0)) => 未定义
eqv?
比起 eq?
, 对具体实现的依赖较少,但通常对资源的消耗更大。
(eqv? 'a 3) => #f
(eqv? #t 't) => #f
(eqv? "abc" 'abc) => #f
(eqv? "hi" '(hi)) => #f
(eqv? #f '()) => #f
(eqv? 9/2 7/2) => #f
(eqv? 3.4 53344) => #f
(eqv? 3 3.0) => #f
(eqv? 1/3 #i1/3) => #f
(eqv? 9/2 9/2) => #t
(eqv? 3.4 (+ 3.0 .4)) => #t
(let ([x (* 12345678987654321 2)])
(eqv? x x)) => #t
(eqv? #\a #\b) => #f
(eqv? #\a #\a) => #t
(let ([x (string-ref "hi" 0)])
(eqv? x x)) => #t
(eqv? #t #t) => #t
(eqv? #f #f) => #t
(eqv? #t #f) => #f
(eqv? (null? '()) #t) => #t
(eqv? (null? '(a)) #f) => #t
(eqv? (cdr '(a)) '()) => #t
(eqv? 'a 'a) => #t
(eqv? 'a 'b) => #f
(eqv? 'a (string->symbol "a")) => #t
(eqv? '(a) '(b)) => #f
(eqv? '(a) '(a)) => 未定义
(let ([x '(a . b)]) (eqv? x x)) => #t
(let ([x (cons 'a 'b)])
(eqv? x x)) => #t
(eqv? (cons 'a 'b) (cons 'a 'b)) => #f
(eqv? "abc" "cba") => #f
(eqv? "abc" "abc") => 未定义
(let ([x "hi"]) (eqv? x x)) => #t
(let ([x (string #\h #\i)]) (eqv? x x)) => #t
(eqv? (string #\h #\i)
(string #\h #\i)) => #f
(eqv? '#vu8(1) '#vu8(1)) => 未定义
(eqv? '#vu8(1) '#vu8(2)) => #f
(let ([x (make-bytevector 10 0)])
(eqv? x x)) => #t
(let ([x (make-bytevector 10 0)])
(eqv? x (make-bytevector 10 0))) => #f
(eqv? '#(a) '#(b)) => #f
(eqv? '#(a) '#(a)) => 未定义
(let ([x '#(a)]) (eqv? x x)) => #t
(let ([x (vector 'a)])
(eqv? x x)) => #t
(eqv? (vector 'a) (vector 'a)) => #f
(eqv? car car) => #t
(eqv? car cdr) => #f
(let ([f (lambda (x) x)])
(eqv? f f)) => #t
(let ([f (lambda () (lambda (x) x))])
(eqv? (f) (f))) => 未定义
(eqv? (lambda (x) x) (lambda (y) y)) => 未定义
(let ([f (lambda (x)
(lambda ()
(set! x (+ x 1))
x))])
(eqv? (f 0) (f 0))) => #f
过程: (equal? obj1 obj2)
返回: 如果 obj1
和 obj2
具有相同的结构和内容,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
如果两个对象基于 eqv?
判定是相等的,则它们是相等的,如 string=?
的字符串,bytevector=?
的字节向量,cars
和 cdrs
都相等的点对,或相同长度,且对应元素都相等的向量。
“当且仅当它的所有实参展开(可能为无穷的)为常规的树,且作为有序树均相等时”,要求equal?
即使对于环状实参也要终止,并返回 #t
. 本质上,如果两个对象的结构不能被点对和向量访问器的任意组合,以及比较叶节点数据的 eqv?
, string=?
, 和 bytevector=?
过程区分,则两个值在 equal?
的意义上就是等价的。
如何高效实现 equal? 是很棘手的,而即使在一个好的实现中,它也很可能比 eqv? 或 eq? 消耗更大。
(equal? 'a 3) => #f
(equal? #t 't) => #f
(equal? "abc" 'abc) => #f
(equal? "hi" '(hi)) => #f
(equal? #f '()) => #f
(equal? 9/2 7/2) => #f
(equal? 3.4 53344) => #f
(equal? 3 3.0) => #f
(equal? 1/3 #i1/3) => #f
(equal? 9/2 9/2) => #t
(equal? 3.4 (+ 3.0 .4)) => #t
(let ([x (* 12345678987654321 2)])
(equal? x x)) => #t
(equal? #\a #\b) => #f
(equal? #\a #\a) => #t
(let ([x (string-ref "hi" 0)])
(equal? x x)) => #t
(equal? #t #t) => #t
(equal? #f #f) => #t
(equal? #t #f) => #f
(equal? (null? '()) #t) => #t
(equal? (null? '(a)) #f) => #t
(equal? (cdr '(a)) '()) => #t
(equal? 'a 'a) => #t
(equal? 'a 'b) => #f
(equal? 'a (string->symbol "a")) => #t
(equal? '(a) '(b)) => #f
(equal? '(a) '(a)) => #t
(let ([x '(a . b)]) (equal? x x)) => #t
(let ([x (cons 'a 'b)])
(equal? x x)) => #t
(equal? (cons 'a 'b) (cons 'a 'b)) => #t
(equal? "abc" "cba") => #f
(equal? "abc" "abc") => #t
(let ([x "hi"]) (equal? x x)) => #t
(let ([x (string #\h #\i)]) (equal? x x)) => #t
(equal? (string #\h #\i)
(string #\h #\i)) => #t
(equal? '#vu8(1) '#vu8(1)) => #t
(equal? '#vu8(1) '#vu8(2)) => #f
(let ([x (make-bytevector 10 0)])
(equal? x x)) => #t
(let ([x (make-bytevector 10 0)])
(equal? x (make-bytevector 10 0))) => #t
(equal? '#(a) '#(b)) => #f
(equal? '#(a) '#(a)) => #t
(let ([x '#(a)]) (equal? x x)) => #t
(let ([x (vector 'a)])
(equal? x x)) => #t
(equal? (vector 'a) (vector 'a)) => #t
(equal? car car) => #t
(equal? car cdr) => #f
(let ([f (lambda (x) x)])
(equal? f f)) => #t
(let ([f (lambda () (lambda (x) x))])
(equal? (f) (f))) => 未定义
(equal? (lambda (x) x) (lambda (y) y)) => 未定义
(let ([f (lambda (x)
(lambda ()
(set! x (+ x 1))
x))])
(equal? (f 0) (f 0))) => #f
(equal?
(let ([x (cons 'x 'x)])
(set-car! x x)
(set-cdr! x x)
x)
(let ([x (cons 'x 'x)])
(set-car! x x)
(set-cdr! x x)
(cons x x))) => #t
过程: (boolean? obj)
返回: 如果 obj
是 #t
或 #f
,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
boolean?
等价于 (lambda (x) (or (eq? x #t) (eq? x #f)))
.
(boolean? #t) => #t
(boolean? #f) => #t
(or (boolean? 't) (boolean? '())) => #f
过程: (null? obj)
返回: 如果 obj
是空列表,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
null?
等价于 (lambda (x) (eq? x '()))
.
(null? '()) => #t
(null? '(a)) => #f
(null? (cdr '(a))) => #t
(null? 3) => #f
(null? #f) => #f
过程: (pair? obj)
返回: 如果 obj
是点对,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(pair? '(a b c)) => #t
(pair? '(3 . 4)) => #t
(pair? '()) => #f
(pair? '#(a b)) => #f
(pair? 3) => #f
过程: (number? obj)
返回: 如果 obj
是数字对象,则为 #t
, 否则为 #f
.
过程: (complex? obj)
返回: 如果 obj
是复数对象,则为 #t
, 否则为 #f
.
过程: (real? obj)
返回: 如果 obj
是实数对象,则为 #t
, 否则为 #f
.
过程: (rational? obj)
返回: 如果 obj
是有理数对象,则为 #t
, 否则为 #f
.
过程: (integer? obj)
返回: 如果 obj
是整数对象,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
这些谓词形成了一个层次结构:任何整数都是有理数,任何有理数都是实数,任何实数都是复数,而任何复数都是数字。大多数实现不提供无理数的内部表示,所以所有实数通常也是有理数。
对于以不精确的 0 为虚部的复数,谓词 real?
, rational?
, 和 integer?
不会把它识别为实数,有理数或整数。
(integer? 1901) => #t
(rational? 1901) => #t
(real? 1901) => #t
(complex? 1901) => #t
(number? 1901) => #t
(integer? -3.0) => #t
(rational? -3.0) => #t
(real? -3.0) => #t
(complex? -3.0) => #t
(number? -3.0) => #t
(integer? 7+0i) => #t
(rational? 7+0i) => #t
(real? 7+0i) => #t
(complex? 7+0i) => #t
(number? 7+0i) => #t
(integer? -2/3) => #f
(rational? -2/3) => #t
(real? -2/3) => #t
(complex? -2/3) => #t
(number? -2/3) => #t
(integer? -2.345) => #f
(rational? -2.345) => #t
(real? -2.345) => #t
(complex? -2.345) => #t
(number? -2.345) => #t
(integer? 7.0+0.0i) => #f
(rational? 7.0+0.0i) => #f
(real? 7.0+0.0i) => #f
(complex? 7.0+0.0i) => #t
(number? 7.0+0.0i) => #t
(integer? 3.2-2.01i) => #f
(rational? 3.2-2.01i) => #f
(real? 3.2-2.01i) => #f
(complex? 3.2-2.01i) => #t
(number? 3.2-2.01i) => #t
(integer? 'a) => #f
(rational? '(a b c)) => #f
(real? "3") => #f
(complex? '#(1 2)) => #f
(number? #\a) => #f
过程: (real-valued? obj)
返回: 如果 obj
是实数,则为 #t
, 否则为 #f
.
过程: (rational-valued? obj)
返回: 如果 obj
是有理数,则为 #t
, 否则为 #f
.
过程: (integer-valued? obj)
返回: 如果 obj
是整数,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
这些谓词类似于 real?
, rational?
, 和 integer?
, 但把以不精确的 0 为虚部的复数当作实数,有理数或整数。
(integer-valued? 1901) => #t
(rational-valued? 1901) => #t
(real-valued? 1901) => #t
(integer-valued? -3.0) => #t
(rational-valued? -3.0) => #t
(real-valued? -3.0) => #t
(integer-valued? 7+0i) => #t
(rational-valued? 7+0i) => #t
(real-valued? 7+0i) => #t
(integer-valued? -2/3) => #f
(rational-valued? -2/3) => #t
(real-valued? -2/3) => #t
(integer-valued? -2.345) => #f
(rational-valued? -2.345) => #t
(real-valued? -2.345) => #t
(integer-valued? 7.0+0.0i) => #t
(rational-valued? 7.0+0.0i) => #t
(real-valued? 7.0+0.0i) => #t
(integer-valued? 3.2-2.01i) => #f
(rational-valued? 3.2-2.01i) => #f
(real-valued? 3.2-2.01i) => #f
和 real?
, rational?
, 及 integer?
一样,这些谓词对所有非数字值返回 #f
.
(integer-valued? 'a) => #f
(rational-valued? '(a b c)) => #f
(real-valued? "3") => #f
过程: (char? obj)
返回: 如果 obj
是字符,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(char? 'a) => #f
(char? 97) => #f
(char? #\a) => #t
(char? "a") => #f
(char? (string-ref (make-string 1) 0)) => #t
过程: (string? obj)
返回: 如果 obj
是字符串,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(string? "hi") => #t
(string? 'hi) => #f
(string? #\h) => #f
过程: (vector? obj)
返回: 如果 obj
是向量,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(vector? '#()) => #t
(vector? '#(a b c)) => #t
(vector? (vector 'a 'b 'c)) => #t
(vector? '()) => #f
(vector? '(a b c)) => #f
(vector? "abc") => #f
过程: (symbol? obj)
返回: 如果 obj
是符号,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(symbol? 't) => #t
(symbol? "t") => #f
(symbol? '(t)) => #f
(symbol? #\t) => #f
(symbol? 3) => #f
(symbol? #t) => #f
过程: (procedure? obj)
返回: 如果 obj
是过程,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(procedure? car) => #t
(procedure? 'car) => #f
(procedure? (lambda (x) x)) => #t
(procedure? '(lambda (x) x)) => #f
(call/cc procedure?) => #t
过程: (bytevector? obj)
返回: 如果 obj
是字节向量,则为 #t
, 否则为 #f
.
库: (rnrs bytevectors), (rnrs)
(bytevector? #vu8()) => #t
(bytevector? '#()) => #f
(bytevector? "abc") => #f
过程: (hashtable? obj)
返回: 如果 obj
是哈希表,则为 #t
, 否则为 #f
.
库: (rnrs hashtables), (rnrs)
(hashtable? (make-eq-hashtable)) => #t
(hashtable? '(not a hash table)) => #f
6.3. 列表和点对
点对,或称 cons cell, 是 Scheme 结构化对象类型的基础。点对最常见的用法是构造列表,列表是点对的有序序列,通过 cdr
字段一个接一个链接起来。列表元素占据点对的 car
字段。在一个完全列表中,最后一个点对的 cdr
字段是空列表,(); 在一个不完全列表中,最后一个点对的 cdr
字段可以是除()以外的任何对象。
点对可用于构造二叉树。树结构中的每个点对是二叉树中的一个内部节点;它的 car
和 cdr
是节点的子节点。
完全列表打印为圆括号中由空白隔开的对象序列。匹配的方括号( [ ] )可用于替代圆括号。例如, (1 2 3)
和 (a [nested list])
是完全列表。空列表写作 ().
不完全列表和树需要稍复杂一些的语法。一个单独的点对写作由空白和一个点分隔的两个对象,例如,(a . b)
. 这被称为点对记法。不完全列表和树也以点对记法书写;点在需要的时候显示,例如,(1 2 3 . 4)
或 ((1 . 2) . 3)
. 完全列表也可以写作点对的形式。例如, (1 2 3)
也可以写作 (1 . (2 . (3 . ())))
.
通过使用 set-car!
或 set-cdr!
破坏性地更改点对的 car
或 cdr
字段,可以创造循环列表或环状图。这类列表不是完全列表。
接受一个列表实参的过程,只在下列情况下需要检查列表是否是不完全的:它们对列表的遍历足够深,(a) 尝试操作一个非列表的尾部,或 (b) 基于环进行无限循环。例如,如果 member 找到了要找的元素,则它不需要检查列表是否是不完全的,而 list-ref 永远不需要检测列表是否为环,因为它的递归已经通过实参 index 限定了界限。
过程: (cons obj1 obj2)
返回: 一个新的点对,它的 car
和 cdr
是 obj1
和 obj2
库: (rnrs base), (rnrs)
cons
是点对构造过程。obj1
成为新点对的 car
, 而 obj2
成为新点对的 cdr
.
(cons 'a '()) => (a)
(cons 'a '(b c)) => (a b c)
(cons 3 4) => (3 . 4)
过程: (car pair)
返回: 点对的 car
库: (rnrs base), (rnrs)
空列表不是点对,所以实参不能是空列表。
(car '(a)) => a
(car '(a b c)) => a
(car (cons 3 4)) => 3
过程: (cdr pair)
返回: 点对的 cdr
库: (rnrs base), (rnrs)
空列表不是点对,所以实参不能是空列表。
(cdr '(a)) => ()
(cdr '(a b c)) => (b c)
(cdr (cons 3 4)) => 4
过程: (set-car! pair obj)
返回: 未定义
库: (rnrs mutable-pairs)
set-car! 把点对的 car 更改为 obj.
(let ([x (list 'a 'b 'c)])
(set-car! x 1)
x) => (1 b c)
过程: (set-cdr! pair obj)
返回: 未定义
库: (rnrs mutable-pairs)
set-cdr!
把点对的 cdr
更改为 obj
.
(let ([x (list 'a 'b 'c)])
(set-cdr! x 1)
x) => (a . 1)
过程: (caar pair)
过程: (cadr pair)
过程: (cddddr pair)
返回: 点对的 caar, cadr, …, 或 cddddr
库: (rnrs base), (rnrs)
这些过程以至多四个 cars
和 cdrs
的组合定义。c 和 r 之间的 a 和 d 代表了对 car
或 cdr
的调用,顺序为从右到左。例如,应用于一个点对的过程 cadr
,返回点对的 cdr
的 car
,其等价于 (lambda (x) (car (cdr x)))
.
(caar '((a))) => a
(cadr '(a b c)) => b
(cdddr '(a b c d)) => (d)
(cadadr '(a (b c))) => c
过程: (list obj ...)
返回: obj
… 组成的列表
库: (rnrs base), (rnrs)
list
等价于 (lambda x x)
.
(list) => ()
(list 1 2 3) => (1 2 3)
(list 3 2 1) => (3 2 1)
过程: (cons* obj ... final-obj)
返回: obj
… 组成的列表,以 final-obj
结束
库: (rnrs lists), (rnrs)
如果省略 obj
…, 则结果就是 final-obj
. 不然,就和 list
一样,构造出一个 obj
… 组成的列表,只是最后的 cdr
字段由 final-obj
替代了 (). 如果 final-obj
不是列表,则结果是一个不完全列表。
(cons* '()) => ()
(cons* '(a b)) => (a b)
(cons* 'a 'b 'c) => (a b . c)
(cons* 'a 'b '(c d)) => (a b c d)
过程: (list? obj)
返回: 如果 obj
是完全列表,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
对于所有不完全列表,list?
一定返回 #f
, 包括环状列表。list?
的定义参见第 67 页。
(list? '()) => #t
(list? '(a b c)) => #t
(list? 'a) => #f
(list? '(3 . 4)) => #f
(list? 3) => #f
(let ([x (list 'a 'b 'c)])
(set-cdr! (cddr x) x)
(list? x)) => #f
过程: (length list)
返回: list
中的元素数量
库: (rnrs base), (rnrs)
length
可依如下定义,使用 67 页定义 list?
的龟兔赛跑算法的修改版本。
(define length
(lambda (x)
(define improper-list
(lambda ()
(assertion-violation 'length "not a proper list" x)))
(let f ([h x] [t x] [n 0])
(if (pair? h)
(let ([h (cdr h)])
(if (pair? h)
(if (eq? h t)
(improper-list)
(f (cdr h) (cdr t) (+ n 2)))
(if (null? h)
(+ n 1)
(improper-list))))
(if (null? h)
n
(improper-list))))))
(length '()) => 0
(length '(a b c)) => 3
(length '(a b . c)) => exception
(length
(let ([ls (list 'a 'b)])
(set-cdr! (cdr ls) ls) => exception
ls))
(length
(let ([ls (list 'a 'b)])
(set-car! (cdr ls) ls) => 2
ls))
过程: (list-ref list n)
返回: list
的第 n
个元素(基于 0)
库: (rnrs base), (rnrs)
n
必须是精确的非负整数,且小于 list
的长度。不带有错误检查的 list-ref
可以定义如下。
(define list-ref
(lambda (ls n)
(if (= n 0)
(car ls)
(list-ref (cdr ls) (- n 1)))))
(list-ref '(a b c) 0) => a
(list-ref '(a b c) 1) => b
(list-ref '(a b c) 2) => c
过程: (list-tail list n)
返回: list 的第 n 个尾部(基于 0)
库: (rnrs base), (rnrs)
n 必须是精确的非负整数,且小于等于 list 的长度。结果不是拷贝;返回的尾段 eq? 于 list 的第 n 个 cdr(或 list 本身,如果 n 为 0)。
不带有错误检查的 list-tail 可以定义如下。
(define list-tail
(lambda (ls n)
(if (= n 0)
ls
(list-tail (cdr ls) (- n 1)))))
(list-tail '(a b c) 0) => (a b c)
(list-tail '(a b c) 2) => (c)
(list-tail '(a b c) 3) => ()
(list-tail '(a b c . d) 2) => (c . d)
(list-tail '(a b c . d) 3) => d
(let ([x (list 1 2 3)])
(eq? (list-tail x 2)
(cddr x))) => #t
过程: (append)
过程: (append list ... obj)
返回: 输入列表的串联
库: (rnrs base), (rnrs)
append 返回一个新的列表,其中元素依次为:第一个列表中的元素,第二个列表中的元素,第三个列表中的元素,等等。对于所有实参,新列表由新生成的点对构成,除了最后一个实参;最后一个实参(不必是列表)被直接放置于新结构的末尾。不带有错误检查的的 append 可以定义如下。
(define append
(lambda args
(let f ([ls '()] [args args])
(if (null? args)
ls
(let g ([ls ls])
(if (null? ls)
(f (car args) (cdr args))
(cons (car ls) (g (cdr ls)))))))))
(append '(a b c) '()) => (a b c)
(append '() '(a b c)) => (a b c)
(append '(a b) '(c d)) => (a b c d)
(append '(a b) 'c) => (a b . c)
(let ([x (list 'b)])
(eq? x (cdr (append '(a) x)))) => #t
过程: (reverse list)
返回: list 中的元素逆向排序后组成的新列表
库: (rnrs base), (rnrs)
不带有错误检查的 reverse 可以定义如下。
(define reverse
(lambda (ls)
(let rev ([ls ls] [new '()])
(if (null? ls)
new
(rev (cdr ls) (cons (car ls) new))))))
(reverse '()) => ()
(reverse '(a b c)) => (c b a)
过程: (memq obj list)
过程: (memv obj list)
过程: (member obj list)
返回: list
中第一个 car
等于 obj
的尾段,或 #f
库: (rnrs lists), (rnrs)
这些过程按顺序遍历实参 list
, 比较 obj
和 list
中的元素。如果找到了等于 obj
的对象,则返回 list
中首元素为此对象的尾段。如果 list
中等于 obj
的对象多于 1 个,则返回第一个首元素等于 obj
的尾段。如果没有找到等于 obj
的对象,则返回#f
. memq
的等价性测试使用 eq?
, memv
使用 eqv?
, 而 member
使用 equal?
.
这些过程最常用作谓词,但它们的名字不以问号结尾,因为它们返回一个有用的真值,而不是#t
. 不带有错误检查的 memq
可以定义如下。
(define memq
(lambda (x ls)
(cond
[(null? ls) #f]
[(eq? (car ls) x) ls]
[else (memq x (cdr ls))])))
memv
和 member
的定义类似,分别以 eqv?
和 equal?
替换 eq?
.
(memq 'a '(b c a d e)) => (a d e)
(memq 'a '(b c d e g)) => #f
(memq 'a '(b a c a d a)) => (a c a d a)
(memv 3.4 '(1.2 2.3 3.4 4.5)) => (3.4 4.5)
(memv 3.4 '(1.3 2.5 3.7 4.9)) => #f
(let ([ls (list 'a 'b 'c)])
(set-car! (memv 'b ls) 'z)
ls) => (a z c)
(member '(b) '((a) (b) (c))) => ((b) (c))
(member '(d) '((a) (b) (c))) => #f
(member "b" '("a" "b" "c")) => ("b" "c")
(let ()
(define member?
(lambda (x ls)
(and (member x ls) #t)))
(member? '(b) '((a) (b) (c)))) => #t
(define count-occurrences
(lambda (x ls)
(cond
[(memq x ls) =>
(lambda (ls)
(+ (count-occurrences x (cdr ls)) 1))]
[else 0])))
(count-occurrences 'a '(a b c d a)) => 2
过程: (memp procedure list)
返回: list
中第一个 car
字段传入 procedure
返回 #t
的尾段,或#f
库: (rnrs lists), (rnrs)
procedure
应该接受一个实参,并返回一个单个值。它不应修改 list
.
(memp odd? '(1 2 3 4)) => (1 2 3 4)
(memp even? '(1 2 3 4)) => (2 3 4)
(let ([ls (list 1 2 3 4)])
(eq? (memp odd? ls) ls)) => #t
(let ([ls (list 1 2 3 4)])
(eq? (memp even? ls) (cdr ls))) => #t
(memp odd? '(2 4 6 8)) => #f
过程: (remq obj list)
过程: (remv obj list)
过程: (remove obj list)
返回: list
中所有不等于 obj
的元素组成的列表
库: (rnrs lists), (rnrs)
这些过程遍历实参 list
, 移除所有等于 obj
的对象。输出列表中保留的元素和它们在输入列表中出现的顺序一致。如果一个列表的尾段(包括列表自身)不包含任何 obj
, 则结果列表中对应的尾段可能与输入列表中的相应尾段相同(基于 eq?
判断)。
remq
的等价性测试基于 eq?
, remv
基于 eqv?
, 而 remove
基于 equal?
.
(remq 'a '(a b a c a d)) => (b c d)
(remq 'a '(b c d)) => (b c d)
(remv 1/2 '(1.2 1/2 0.5 3/2 4)) => (1.2 0.5 3/2 4)
(remove '(b) '((a) (b) (c))) => ((a) (c))
过程: (remp procedure list)
返回: list
中应用 procedure
返回 #f
的元素组成的列表
库: (rnrs lists), (rnrs)
procedure
应该接受一个参数,并返回一个单个值。它不应修改 list
.
remp
把 procedure
应用于列表中的每个元素,并返回一个列表,其中只包含使 procedure
返回 #f
的元素。返回列表中的元素与它们在原列表中出现的顺序是一致的。
(remp odd? '(1 2 3 4)) => (2 4)
(remp
(lambda (x) (and (> x 0) (< x 10)))
'(-5 15 3 14 -20 6 0 -9)) => (-5 15 14 -20 0 -9)
过程: (filter procedure list)
返回: list
中应用 procedure
返回真值的元素组成的列表
库: (rnrs lists), (rnrs)
procedure
应该接受一个参数,并返回一个单个值。它不应修改 list
.
filter
把 procedure
应用于列表中的每个元素,并返回一个列表,其中只包含使 procedure
返回真值的元素。返回列表中的元素与它们在原列表中出现的顺序是一致的。
(filter odd? '(1 2 3 4)) => (1 3)
(filter
(lambda (x) (and (> x 0) (< x 10)))
'(-5 15 3 14 -20 6 0 -9)) => (3 6)
过程: (partition procedure list)
返回: 参见下文
库: (rnrs lists), (rnrs)
procedure
应该接受一个参数,并返回一个单个值。它不应修改 list
.
partition
把 procedure
应用于列表中的每个元素,并返回两个值:一个只包含使 procedure
返回真值的元素组成的列表,和一个只包含使 procedure
返回 #f
的元素组成的列表。返回列表中的元素与它们在原列表中出现的顺序是一致的。
(partition odd? '(1 2 3 4)) => (1 3)
(2 4)
(partition
(lambda (x) (and (> x 0) (< x 10)))
'(-5 15 3 14 -20 6 0 -9)) => (3 6)
(-5 15 14 -20 0 -9)
partition
返回的值也可以通过分别调用 filter
和 remp
得到,但这样需要对列表中的每个元素调用两次 procedure
.
过程: (find procedure list)
返回: list
中使 procedure
返回真值的第一个元素,或 #f
库: (rnrs lists), (rnrs)
procedure
应该接受一个参数,并返回一个单个值。它不应修改 list
.
find
依序遍历实参 list
, 轮流对每个元素应用 procedure
. 如果 procedure
对给定元素返回一个真值,则 find
返回此元素,并且不再对其它元素应用 procedure
. 如果 procedure
对 list
中的每个元素都返回 #f
, 则 find
返回 #f
.
如果一个程序必须区分在 list
中找到的是 #f
还是找不到任何元素,则应该使用 memp
.
(find odd? '(1 2 3 4)) => 1
(find even? '(1 2 3 4)) => 2
(find odd? '(2 4 6 8)) => #f
(find not '(1 a #f 55)) => #f
过程: (assq obj alist)
过程: (assv obj alist)
过程: (assoc obj alist)
返回: alist
中 car
字段等于 obj
的第一个元素,或 #f
库: (rnrs lists), (rnrs)
实参 alist
必须是关联列表。关联列表是一种完全列表,其元素是形如 (key . value)
的键值对。在存储特定对象(键)及其相关的信息(值)时,关联列表很有用。
这些过程遍历关联列表,测试每个键与 obj
的等价性。如果找到一个相等的键,则返回此键值对。否则,返回 #f
.
assq
的等价性测试基于 eq?
, assv
基于 eqv?
, 而 assoc
基于 equal?
. 不带有错误检测的 assq
可以定义如下。
(define assq
(lambda (x ls)
(cond
[(null? ls) #f]
[(eq? (caar ls) x) (car ls)]
[else (assq x (cdr ls))])))
assv
和 assoc
的定义类似,分别以 eqv?
和 equal?
替代 eq?
.
(assq 'b '((a . 1) (b . 2))) => (b . 2)
(cdr (assq 'b '((a . 1) (b . 2)))) => 2
(assq 'c '((a . 1) (b . 2))) => #f
(assv 2/3 '((1/3 . 1) (2/3 . 2))) => (2/3 . 2)
(assv 2/3 '((1/3 . a) (3/4 . b))) => #f
(assoc '(a) '(((a) . a) (-1 . b))) => ((a) . a)
(assoc '(a) '(((b) . b) (a . c))) => #f
(let ([alist (list (cons 2 'a) (cons 3 'b))])
(set-cdr! (assv 3 alist) 'c)
alist) => ((2 . a) (3 . c))
12.7 节给出的解释器把环境表示为关联列表,并把 assq
用于变量查找和赋值。
过程: (assp procedure alist)
返回: alist
中 car
字段值使 procedure
返回真值的第一个元素,或 #f
库: (rnrs lists), (rnrs)
alist
必须是关联列表。关联列表是一种完全列表,其元素是形如 (key . value)
的键值对。procedure
应当接受一个实参,并返回一个单个值。它不应修改 list
.
(assp odd? '((1 . a) (2 . b))) => (1 . a)
(assp even? '((1 . a) (2 . b))) => (2 . b)
(let ([ls (list (cons 1 'a) (cons 2 'b))])
(eq? (assp odd? ls) (car ls))) => #t
(let ([ls (list (cons 1 'a) (cons 2 'b))])
(eq? (assp even? ls) (cadr ls))) => #t
(assp odd? '((2 . b))) => #f
过程: (list-sort predicate list)
返回: list
中的元素根据 predicate
排序后组成的列表
库: (rnrs sorting), (rnrs)
predicate
应当是个过程,它接受两个实参,并且,当它的第一个实参在排序后的列表中必须排在第二个实参之前时返回 #t
. 即,如果 predicate
应用于两个元素 x 和 y, 且在输入列表中 x 排在 y 后面,它应该只在 x 应当于输出列表中排在 y 前面时返回真。如果满足这一限制,list-sort
就是执行稳定的排序,即,根据 predicate
,两个元素只在必要时重新排序。它并不移除重复元素。这个过程至多会调用 predicate
nlogn 次,其中 n 是 list
的长度。
(list-sort < '(3 4 2 1 2 5)) => (1 2 2 3 4 5)
(list-sort > '(0.5 1/2)) => (0.5 1/2)
(list-sort > '(1/2 0.5)) => (1/2 0.5)
(list->string
(list-sort char>?
(string->list "hello"))) => "ollhe"
6.4. 数字
Scheme 数字可以被分类为整数,有理数,实数,或复数。这种分类是层次结构的,其中所有整数是有理数,所有有理数是实数,而所有实数是复数。6.2 节中介绍的谓词 integer?
, rational?
, real?
, 以及 complex?
用于判定数字属于其中哪个类别。
Scheme 数字也可以基于派生出数字的操作的特性,以及这些操作的输入,而被分类为精确的或不精确的。谓词 exact?
和 inexact?
可以被用于判定一个数字的精确性。Scheme 中,大多数对数字的操作会保留精确性:如果给出精确的操作数,则返回精确值,如果给出不精确的操作数,或给出精确与不精确操作数的组合,则返回不精确值。
精确整数和有理数的运算通常支持任意精度;整数或比值的分子分母的大小只受限于系统的存储空间。虽然也可以用其它表示形式,但不精确数通常表示为由主机硬件或系统软件支持的浮点数。复数通常表示为有序点对(实部,虚部),其中实部和虚部是精确整数,精确有理数,或浮点数。
Scheme 数字以一种很直接的方式书写,与常规没有多少不同。一个精确整数通常写为一个数字序列,前面带有一个可选的符号。例如,3, +19, -100000, 以及 208423089237489374, 都表示精确的整数。
有理数通常写作以斜线 (/) 分隔的两个数字序列,前面带有一个可选的符号。例如,3/4, -6/5, 以及 1/1208203823, 都是精确的有理数。读取时,比值会被直接归约为最简形式,甚至可能归约为一个精确整数。
不精确的实数通常写作浮点数或科学记数法。浮点数记法由一个数字序列接着一个小数点再接着一个数字序列构成,前面带有一个可选的符号。科学计数法由一个可选的符号,一个数字序列,一个可选的小数点,接着另一个数字串,和一个指数构成;指数写作字母 e 后面跟着一个可选的符号和一个数字序列。例如,1.0 和 -200.0 是有效的不精确整数,而 1.5, 0.034, -10e-10 和 1.5e-5 是有效的不精确有理数。指数即是其前面数字所要乘以的 10 的幂次,所以 2e3 等价于 2000.0.
在使用浮点记法或科学记数法时,尾数宽度 |w 可能作为实数或复数的实部的后缀出现。尾数宽度 w 表示了数字表示形式的有效位数。尾数宽度默认为 53(标准化 IEEE 双精度浮点数的有效位数)或更多。对于非标准化的 IEEE 双精度浮点数,尾数宽度小于 53. 如果一个实现不能以指定的尾数宽度表示一个数字,在可能的情况下,它会采用至少达到要求的有效位数的表示形式,否则,它会使用它的最大尾数宽度的表示形式。
精确和不精确的实数,写作精确或不精确的整数或有理数;对于非有理数,即无理数,Scheme 没有预设的语法。
复数可以写作直角坐标或极坐标的形式。直角坐标形式中,复数写作 x+yi 或 x-yi, 其中 x 是整数,有理数或实数,y 是无符号整数,有理数,或实数。实部 x 可以省略,此时它被当作 0. 例如,3+4i, 3.2-3/4i, +i, 以及 -3e-5i, 是写作直角坐标形式的复数。极坐标形式中,复数写作 x@y, 其中 x 和 y 是整数,有理数,或实数。例如,1.1@1.764 以及 -1@-1/2, 是写作极坐标形式的复数。
表示不精确实数的语法形式 +inf.0 和 -inf.0 表示正、负无穷。语法形式 +nan.0 和 -nan.0 表示不精确的非数(NaN). 无穷值可以通过不精确的正数或负数除以不精确的 0 生成,而 NaNs 可以通过不精确的 0 除以不精确的 0 生成,此外还有其它一些方法。
通过在表示形式前添加 #e
或 #i
, 可以重载数字表示形式的精确性。#e
强制数字为精确的,而#i
强制数字为不精确的。例如,1, #e1, 1/1, #e1/1, #e1.0, 以及 #e1e0 都表示精确整数 1, 而 #i3/10, 0.3, #i0.3, 以及 3e-1 都表示不精确的有理数 0.3.
数字默认以基数 10 书写,但是可以使用特定的前缀 #b (二进制), #o (八进制), #d (十进制), 和 #x (十六进制)指定基数 2,8,10,或 16. 对于基数 16,字母 a 至 f 或 A 至 F 作为额外需要的数字以表达数位值 10 至 15。例如, #b10101 是 2110 的等价二进制数, #o72 是 5810 的等价八进制数,#xC7 是 19910 的等价十六进制数。写作浮点数或科学记数法总是采用基数 10.
基数和精确性前缀同时使用时,可以以任何顺序出现。
一个 Scheme 实现对于不精确数值的内部表示,可能支持不只一种精度。在科学记数法中,指数标记 s (短), f (单), d (双), 以及 l (长)可以替代默认的指数标记 e,以覆盖数字的默认精度。在支持多种表示形式的实现中,默认精度至少为双精度。
第 459 页给出了 Scheme 数字的精确语法。
所有数字都可以写作多种不同的形式,但是系统打印器(通过 put-datum
, write
, 以及 display
调用)和 number->string
以一种紧凑的形式表示数字,使用能够保持属性所必要的最少数位——在读取时,打印出的数字与原数字是一样的。
本节的剩余部分介绍了数字操作的“通用算术”过程。之后的两节介绍了专用于 fixnums
和 flonums
的操作,此两者分别表示精确的固定精度整数值和不精确的实数。
本节中的过程所接受的数字实参类型,由参数名暗示:num 意指复数(即,所有数字),real 意指实数,rat 意指有理数,而 int 意指整数。如果需要的是 real, rat 或 int, 则实参必须是 real?, rational?, 或 integer? 所判定的实数,有理数,或整数,即,数字的虚部必须是精确的 0. 在需要精确整数的场合,会使用名字 exint. 在每种情况下,名字后面都可能会有后缀,如,int2.
过程: (exact? num)
返回: 如果 num
是精确的,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(exact? 1) => #t
(exact? -15/16) => #t
(exact? 2.01) => #f
(exact? #i77) => #f
(exact? #i2/3) => #f
(exact? 1.0-2i) => #f
过程: (inexact? num)
返回: 如果 num
是不精确的,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(inexact? -123) => #f
(inexact? #i123) => #t
(inexact? 1e23) => #t
(inexact? +i) => #f
过程: (= num1 num2 num3 ...)
过程: (< real1 real2 real3 ...)
过程: (> real1 real2 real3 ...)
过程: (<= real1 real2 real3 ...)
过程: (>= real1 real2 real3 ...)
返回: 如果关系成立,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
谓词 =
在其实参相等时返回 #t
。谓词 <
在其实参单调递增时返回 #t
,即,每个实参都大于它前一个实参,而 >
在其实参单调递减时返回 #t
。谓词 <=
在其实参单调非递减时返回 #t
,即,每个实参不小于它前一个实参,而 >=
在其实参单调非递增时返回 #t
。只传入一个实参时,这些谓词均返回 #t
。
如同参数名所暗示的, =
定义为接受复数实参,而其它关系谓词则定义为只接受实数实参。若两个复数的实部和虚部均相等,则它们被判定为相等。涉及 NaNs
的比较均返回 #f
.
(= 7 7) => #t
(= 7 9) => #f
(< 2e3 3e2) => #f
(<= 1 2 3 3 4 5) => #t
(<= 1 2 3 4 5) => #t
(> 1 2 2 3 3 4) => #f
(>= 1 2 2 3 3 4) => #f
(= -1/2 -0.5) => #t
(= 2/3 .667) => #f
(= 7.2+0i 7.2) => #t
(= 7.2-3i 7) => #f
(< 1/2 2/3 3/4) => #t
(> 8 4.102 2/3 -5) => #t
(let ([x 0.218723452])
(< 0.210 x 0.220)) => #t
(let ([i 1] [v (vector 'a 'b 'c)])
(< -1 i (vector-length v))) => #t
(apply < '(1 2 3 4)) => #t
(apply > '(4 3 3 2)) => #f
(= +nan.0 +nan.0) => #f
(< +nan.0 +nan.0) => #f
(> +nan.0 +nan.0) => #f
(>= +inf.0 +nan.0) => #f
(>= +nan.0 -inf.0) => #f
(> +nan.0 0.0) => #f
过程: (+ num ...)
返回: 实参 num
… 的和
库: (rnrs base), (rnrs)
不带实参调用时, + 返回 0.
(+) => 0
(+ 1 2) => 3
(+ 1/2 2/3) => 7/6
(+ 3 4 5) => 12
(+ 3.0 4) => 7.0
(+ 3+4i 4+3i) => 7+7i
(apply + '(1 2 3 4 5)) => 15
过程: (- num)
返回: num
的加法逆元
过程: (- num1 num2 num3 ...)
返回: num1
与 num2
num3
… 之和的差
库: (rnrs base), (rnrs)
(- 3) => -3
(- -2/3) => 2/3
(- 4 3.0) => 1.0
(- 3.25+4.25i 1/4+1/4i) => 3.0+4.0i
(- 4 3 2 1) => -2
过程: (* num ...)
返回: num
… 的积
库: (rnrs base), (rnrs)
不带实参调用时, * 返回 1.
(*) => 1
(* 3.4) => 3.4
(* 1 1/2) => 1/2
(* 3 4 5.5) => 66.0
(* 1+2i 3+4i) => -5+10i
(apply * '(1 2 3 4 5)) => 120
过程: (/ num)
返回: num
的乘法逆元
过程: (/ num1 num2 num3 ...)
返回: num1
除以 num2
num3
… 之积的结果
库: (rnrs base), (rnrs)
(/ -17) => -1/17
(/ 1/2) => 2
(/ .5) => 2.0
(/ 3 4) => 3/4
(/ 3.0 4) => .75
(/ -5+10i 3+4i) => 1+2i
(/ 60 5 4 3 2) => 1/2
过程: (zero? num)
返回: 如果 num
为 0,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
zero?
等价于 (lambda (x) (= x 0))
.
(zero? 0) => #t
(zero? 1) => #f
(zero? (- 3.0 3.0)) => #t
(zero? (+ 1/2 1/2)) => #f
(zero? 0+0i) => #t
(zero? 0.0-0.0i) => #t
过程: (positive? real)
返回: 如果 real
大于 0,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
positive?
等价于 (lambda (x) (> x 0))
.
(positive? 128) => #t
(positive? 0.0) => #f
(positive? 1.8e-15) => #t
(positive? -2/3) => #f
(positive? .001-0.0i) => exception: not a real number
过程: (negative? real)
返回: 如果 real
小于 0,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
negative?
等价于 (lambda (x) (< x 0))
.
(negative? -65) => #t
(negative? 0) => #f
(negative? -0.0121) => #t
(negative? 15/16) => #f
(negative? -7.0+0.0i) => exception: not a real number
过程: (even? int)
返回: 如果 int
是偶数,则为 #t
, 否则为 #f
.
过程: (odd? int)
返回: 如果 int
是奇数,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(even? 0) => #t
(even? 1) => #f
(even? 2.0) => #t
(even? -120762398465) => #f
(even? 2.0+0.0i) => exception: not an integer
(odd? 0) => #f
(odd? 1) => #t
(odd? 2.0) => #f
(odd? -120762398465) => #t
(odd? 2.0+0.0i) => exception: not an integer
过程: (finite? real)
返回: 如果 real
是有限值,则为 #t
, 否则为 #f
.
过程: (infinite? real)
返回: 如果 real
是无限值,则为 #t
, 否则为 #f
.
过程: (nan? real)
返回: 如果 real
是 NaN,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
(finite? 2/3) => #t
(infinite? 2/3) => #f
(nan? 2/3) => #f
(finite? 3.1415) => #t
(infinite? 3.1415) => #f
(nan? 3.1415) => #f
(finite? +inf.0) => #f
(infinite? -inf.0) => #t
(nan? -inf.0) => #f
(finite? +nan.0) => #f
(infinite? +nan.0) => #f
(nan? +nan.0) => #t
过程: (quotient int1 int2)
返回: int1
和 int2
的整数商
过程: (remainder int1 int2)
返回: int1
和 int2
的整数余数
过程: (modulo int1 int2)
返回: int1
和 int2
的整数模
库: (rnrs r5rs)
remainder
的结果与 int1
的符号相同,而 modulo
的结果与 int2
的符号相同。
(quotient 45 6) => 7
(quotient 6.0 2.0) => 3.0
(quotient 3.0 -2) => -1.0
(remainder 16 4) => 0
(remainder 5 2) => 1
(remainder -45.0 7) => -3.0
(remainder 10.0 -3.0) => 1.0
(remainder -17 -9) => -8
(modulo 16 4) => 0
(modulo 5 2) => 1
(modulo -45.0 7) => 4.0
(modulo 10.0 -3.0) => -2.0
(modulo -17 -9) => -8
过程: (div x_1 x_2)
过程: (mod x_1 x_2)
过程: (div-and-mod x_1 x_2)
返回: 参见下文
库: (rnrs base), (rnrs)
如果 x1 和 x2 是精确的,则 x2 一定不能为 0. 这些过程实现了数论整数除法,其中 div 操作求商,而 mod 操作求余数或模,但每种情况均被扩展为可以处理实数。
(div x1 x2) 的值 nd 是一个整数,而 (mod x1 x2) 的值 xm 是一个实数,满足 x1 = nd · x2 + xm 且 0 ≤ xm < |x2|. 在某个实现无法以一个数字对象表示这些算式得出的数学结果时,div 和 mod 返回一个未定义数或抛出一个条件类型的 &implementation-restriction 异常。
div-and-mod
过程的行为如同如下定义。
(define (div-and-mod x_1 x_2) (values (div x_1 x_2) (mod x_1 x_2)))
即,除非在上述那种情况中抛出异常,它返回两个值:在两个实参上调用 div
的结果,和在两个实参上调用 mod
的结果。
(div 17 3) => 5
(mod 17 3) => 2
(div -17 3) => -6
(mod -17 3) => 1
(div 17 -3) => -5
(mod 17 -3) => 2
(div -17 -3) => 6
(mod -17 -3) => 1
(div-and-mod 17.5 3) => 5.0
2.5
过程: (div0 x_1 x_2)
过程: (mod0 x_1 x_2)
过程: (div0-and-mod0 x_1 x_2)
返回: 参见下文
库: (rnrs base), (rnrs)
如果 x1 和 x2 是精确的,则 x2 一定不能是 0. 这些过程和 div, mod, 以及 div-and-mod 类似,但对 "mod" 的值有不同的限制,这同时也影响到 "div" 的值。 (div0 x1 x2) 的值 nd 是整数,而 (mod0 x1 x2) 的值 xm 是实数, 满足 x1 = nd · x2 + xm 且 -|x2/2| ≤ xm < |x2/2|. 在某个实现无法以一个数字对象表示这些算式得出的数学结果时,div0 和 mod0 返回一个未定义数或抛出一个条件类型的 &implementation-restriction 异常。
div0-and-mod0
过程的行为如同如下定义。
(define (div0-and-mod0 x_1 x_2) (values (div0 x_1 x_2) (mod0 x_1 x_2)))
即,除非在上述那种情况中抛出异常,它返回两个值:在两个实参上调用 div0
的结果,和在两个实参上调用 mod0
的结果。
(div0 17 3) => 6
(mod0 17 3) => -1
(div0 -17 3) => -6
(mod0 -17 3) => 1
(div0 17 -3) => -6
(mod0 17 -3) => -1
(div0 -17 -3) => 6
(mod0 -17 -3) => 1
(div0-and-mod0 17.5 3) => 6.0
-0.5
过程: (truncate real)
返回: 在趋于 0 的方向上最接近 real
的整数
库: (rnrs base), (rnrs)
如果 real
是无穷数或 NaN
, truncate
返回 real
.
(truncate 19) => 19
(truncate 2/3) => 0
(truncate -2/3) => 0
(truncate 17.3) => 17.0
(truncate -17/2) => -8
过程: (floor real)
返回: 趋于 -∞ 的方向上最接近 real
的整数
库: (rnrs base), (rnrs)
如果 real
是无穷数或 NaN
, floor
返回 real
.
(floor 19) => 19
(floor 2/3) => 0
(floor -2/3) => -1
(floor 17.3) => 17.0
(floor -17/2) => -9
过程: (ceiling real)
返回: 趋于 +∞ 的方向上最接近 real
的整数
库: (rnrs base), (rnrs)
如果 real
是无穷数或 NaN
, ceiling
返回 real
.
(ceiling 19) => 19
(ceiling 2/3) => 1
(ceiling -2/3) => 0
(ceiling 17.3) => 18.0
(ceiling -17/2) => -8
过程: (round real)
返回: 最接近 real
的整数
库: (rnrs base), (rnrs)
如果 real
处于两个整数的正中间,则返回最接近的偶数。如果 real
是无穷数或 NaN
, round
返回 real
.
(round 19) => 19
(round 2/3) => 1
(round -2/3) => -1
(round 17.3) => 17.0
(round -17/2) => -8
(round 2.5) => 2.0
(round 3.5) => 4.0
过程: (abs real)
返回: real
的绝对值
库: (rnrs base), (rnrs)
abs
等价于 (lambda (x) (if (< x 0) (- x) x))
. 对于实数输入, abs
和 magnitude
(参见 183 页) 是一样的。
(abs 1) => 1
(abs -3/4) => 3/4
(abs 1.83) => 1.83
(abs -0.093) => 0.093
过程: (max real1 real2 ...)
返回: real1
real2
… 的最大值
库: (rnrs base), (rnrs)
(max 4 -7 2 0 -6) => 4
(max 1/2 3/4 4/5 5/6 6/7) => 6/7
(max 1.5 1.3 -0.3 0.4 2.0 1.8) => 2.0
(max 5 2.0) => 5.0
(max -5 -2.0) => -2.0
(let ([ls '(7 3 5 2 9 8)])
(apply max ls)) => 9
过程: (min real1 real2 ...)
返回: real1
real2
… 的最小值
库: (rnrs base), (rnrs)
(min 4 -7 2 0 -6) => -7
(min 1/2 3/4 4/5 5/6 6/7) => 1/2
(min 1.5 1.3 -0.3 0.4 2.0 1.8) => -0.3
(min 5 2.0) => 2.0
(min -5 -2.0) => -5.0
(let ([ls '(7 3 5 2 9 8)])
(apply min ls)) => 2
过程: (gcd int ...)
返回: 实参 int
… 的最大公约数
库: (rnrs base), (rnrs)
结果总是非负数,即,因子 -1 会被忽略。不带实参调用时, gcd
返回 0.
(gcd) => 0
(gcd 34) => 34
(gcd 33.0 15.0) => 3.0
(gcd 70 -42 28) => 14
过程: (lcm int ...)
返回: 实参 int
… 的最小公倍数
库: (rnrs base), (rnrs)
结果总是非负数,即,-1 的公倍数会被忽略。虽然不带实参调用 lcm
时,可能应该返回 ∞, 但它定义为返回 1. 如果一个或更多实参为 0, lcm
返回 0.
(lcm) => 1
(lcm 34) => 34
(lcm 33.0 15.0) => 165.0
(lcm 70 -42 28) => 420
(lcm 17.0 0) => 0.0
过程: (expt num1 num2)
返回: num1
的 num2
次幂
库: (rnrs base), (rnrs)
如果实参均为 0, expt
返回 1.
(expt 2 10) => 1024
(expt 2 -10) => 1/1024
(expt 2 -10.0) => 9.765625e-4
(expt -1/2 5) => -1/32
(expt 3.0 3) => 27.0
(expt +i 2) => -1
过程: (inexact num)
返回: num
的不精确表示形式
库: (rnrs base), (rnrs)
如果 num
已经是不精确的,它返回 num
本身。如果当前实现不支持 num
的不精确表示形式,则可能会抛出一个条件类型的 &implementation-violation
异常。对于量级超出了当前实现的不精确数字表示形式的范围的输入, inexact
也可能返回 +inf.0 或 -inf.0.
(inexact 3) => 3.0
(inexact 3.0) => 3.0
(inexact -1/4) => -.25
(inexact 3+4i) => 3.0+4.0i
(inexact (expt 10 20)) => 1e20
过程: (exact num)
返回: num
的精确表示形式
库: (rnrs base), (rnrs)
如果 num
已经是精确的,它返回 num
本身。如果当前实现不支持 num
的精确表示形式,则可能会抛出一个条件类型的 &implementation-violation
异常。
(exact 3.0) => 3
(exact 3) => 3
(exact -.25) => -1/4
(exact 3.0+4.0i) => 3+4i
(exact 1e20) => 100000000000000000000
过程: (exact->inexact num)
返回: num
的不精确表示形式
过程: (inexact->exact num)
返回: num
的精确表示形式
库: (rnrs r5rs)
这些是 inexact
和 exact
的别名,以和第 5 修订版报告兼容。
过程: (rationalize real1 real2)
返回: 参见下文
库: (rnrs base), (rnrs)
rationalize
返回与 real1
相差不超过 real2
的最简单的有理数。当 |n1| ≤ |n2| 且 |m1| ≤ |m2|, 且 |n1| < |n2| 或 |m1| < |m2| 时,有理数 q1 = n1/m1 比有理数 q2 = n2/m2 简单。
(rationalize 3/10 1/10) => 1/3
(rationalize .3 1/10) => 0.3333333333333333
(eqv? (rationalize .3 1/10) #i1/3) => #t
过程: (numerator rat)
返回: rat
的分子
库: (rnrs base), (rnrs)
如果 rat
是整数,则 numerator
为 rat
.
(numerator 9) => 9
(numerator 9.0) => 9.0
(numerator 0.0) => 0.0
(numerator 2/3) => 2
(numerator -9/4) => -9
(numerator -2.25) => -9.0
过程: (denominator rat)
返回: rat
的分母
库: (rnrs base), (rnrs)
如果 rat
是整数,包括 0,则 denominator
为 1.
(denominator 9) => 1
(denominator 9.0) => 1.0
(denominator 0) => 1
(denominator 0.0) => 1.0
(denominator 2/3) => 3
(denominator -9/4) => 4
(denominator -2.25) => 4.0
过程: (real-part num)
返回: num
的实部
库: (rnrs base), (rnrs)
如果 num
是实数,则 real-part
返回 num
.
(real-part 3+4i) => 3
(real-part -2.3+0.7i) => -2.3
(real-part -i) => 0
(real-part 17.2) => 17.2
(real-part -17/100) => -17/100
过程: (imag-part num)
返回: num
的虚部
库: (rnrs base), (rnrs)
如果 num
是实数, imag-part
返回精确的 0.
(imag-part 3+4i) => 4
(imag-part -2.3+0.7i) => 0.7
(imag-part -i) => -1
(imag-part -2.5) => 0
(imag-part -17/100) => 0
过程: (make-rectangular real1 real2)
返回: 一个复数,实部为 real1
,虚部为 real2
库: (rnrs base), (rnrs)
(make-rectangular -2 7) => -2+7i
(make-rectangular 2/3 -1/2) => 2/3-1/2i
(make-rectangular 3.2 5.3) => 3.2+5.3i
过程: (make-polar real1 real2)
返回: 一个复数,模为 real1
,辐角为 real2
库: (rnrs base), (rnrs)
(make-polar 2 0) => 2
(make-polar 2.0 0.0) => 2.0+0.0i
(make-polar 1.0 (asin -1.0)) => 0.0-1.0i
(eqv? (make-polar 7.2 -0.588) 7.2@-0.588) => #t
过程: (angle num)
返回: num
极坐标形式的辐角
库: (rnrs base), (rnrs)
结果的范围是 -π(不包含)至 +π(包含)。
(angle 7.3@1.5708) => 1.5708
(angle 5.2) => 0.0
过程: (magnitude num)
返回: num
的模
库: (rnrs base), (rnrs)
对实数实参,模和绝对值(参见 178 页)是一样的。复数 x + yi 的模是 +√ x2+y2.
(magnitude 1) => 1
(magnitude -3/4) => 3/4
(magnitude 1.83) => 1.83
(magnitude -0.093) => 0.093
(magnitude 3+4i) => 5
(magnitude 7.25@1.5708) => 7.25
过程: (sqrt num)
返回: num
的主平方根
库: (rnrs base), (rnrs)
sqrt
的输入为精确值时,只要可能,实现最好返回精确结果,但这并非硬性要求。
(sqrt 16) => 4
(sqrt 1/4) => 1/2
(sqrt 4.84) => 2.2
(sqrt -4.84) => 0.0+2.2i
(sqrt 3+4i) => 2+1i
(sqrt -3.0-4.0i) => 1.0-2.0i
过程: (exact-integer-sqrt n)
返回: 参见下文
库: (rnrs base), (rnrs)
这个过程返回两个非负精确整数 s 和 r,其中 n = s2 + r, 且 n < (s + 1)2.
(exact-integer-sqrt 0) => 0
0
(exact-integer-sqrt 9) => 3
=> 0
(exact-integer-sqrt 19) => 4
=> 3
过程: (exp num)
返回: e 的 num
次幂
库: (rnrs base), (rnrs)
(exp 0.0) => 1.0
(exp 1.0) => 2.7182818284590455
(exp -.5) => 0.6065306597126334
过程: (log num)
返回: num
的自然对数
过程: (log num1 num2)
返回: num1
以 num2
为底的对数
库: (rnrs base), (rnrs)
(log 1.0) => 0.0
(log (exp 1.0)) => 1.0
(/ (log 100) (log 10)) => 2.0
(log (make-polar (exp 2.0) 1.0)) => 2.0+1.0i
(log 100.0 10.0) => 2.0
(log .125 2.0) => -3.0
过程: (sin num)
过程: (cos num)
过程: (tan num)
返回: num 的正弦, 余弦, 或正切值
库: (rnrs base), (rnrs)
实参以弧度指定。
(sin 0.0) => 0.0
(cos 0.0) => 1.0
(tan 0.0) => 0.0
过程: (asin num)
过程: (acos num)
返回: num
的反正弦或反余弦值
库: (rnrs base), (rnrs)
结果以弧度表示。复数 z 的反正弦和反余弦依如下定义。
sin-1(z) = -ilog(iz + √1-z2)
cos-1(z) = π/2 - sin-1(z)
(define pi (* (asin 1) 2))
(= (* (acos 0) 2) pi) => #t
过程: (atan num)
过程: (atan real1 real2)
返回: 参见下文
库: (rnrs base), (rnrs)
当传入单个的复数实参 num
(第一种形式)时, atan
返回 num
的反正切值。复数 z 的反正切定义如下:
tan-1(z) = (log(1+iz) - log(1-iz))/(2i)
当传入两个实数实参(第二种形式)时, atan
等价于 (lambda (y x) (angle (make-rectangular x y)))
.
(define pi (* (atan 1) 4))
(= (* (atan 1.0 0.0) 2) pi) => #t
过程: (bitwise-not exint)
返回: exint
的按位非
过程: (bitwise-and exint ...)
返回: exint
… 的按位与
过程: (bitwise-ior exint ...)
返回: exint
… 的按位或
过程: (bitwise-xor exint ...)
返回: exint
… 的按位异或
库: (rnrs arithmetic bitwise), (rnrs)
输入被作为补码形式处理,即使它们在内部并不如此表示。
(bitwise-not 0) => -1
(bitwise-not 3) => -4
(bitwise-and #b01101 #b00111) => #b00101
(bitwise-ior #b01101 #b00111) => #b01111
(bitwise-xor #b01101 #b00111) => #b01010
过程: (bitwise-if exint1 exint2 exint3)
返回: 实参的按位 if
库: (rnrs arithmetic bitwise), (rnrs)
输入被作为补码形式处理,即使它们在内部并不如此表示。
对 exint1
的每个为 1 的位,结果的对应位取自 exint2
, 而对 exint1
的每个为 0 的位,结果的对应位取自 exint3
.
(bitwise-if #b101010 #b111000 #b001100) => #b101100
bitwise-if
可以定义如下:
(define bitwise-if
(lambda (exint1 exint2 exint3)
(bitwise-ior
(bitwise-and exint1 exint2)
(bitwise-and (bitwise-not exint1) exint3))))
过程: (bitwise-bit-count exint)
返回: 参见下文
库: (rnrs arithmetic bitwise), (rnrs)
若输入为非负值, bitwise-bit-count
返回 exint
的补码表示中为 1 的位的个数。若输入为负值,返回一个负数,其模为 exint
的补码表示中为 0 的位的个数加 1,等价于 (bitwise-not (bitwise-bit-count (bitwise-not exint)))
.
(bitwise-bit-count #b00000) => 0
(bitwise-bit-count #b00001) => 1
(bitwise-bit-count #b00100) => 1
(bitwise-bit-count #b10101) => 3
(bitwise-bit-count -1) => -1
(bitwise-bit-count -2) => -2
(bitwise-bit-count -4) => -3
过程: (bitwise-length exint)
返回: 参见下文
库: (rnrs arithmetic bitwise), (rnrs)
这个过程返回 exint
的最小补码表示形式的位数,对于负数,不包括符号位。对于 0, bitwise-length
返回 0.
(bitwise-length #b00000) => 0
(bitwise-length #b00001) => 1
(bitwise-length #b00100) => 3
(bitwise-length #b00110) => 3
(bitwise-length -1) => 0
(bitwise-length -6) => 3
(bitwise-length -9) => 4
过程: (bitwise-first-bit-set exint)
返回: exint
为 1 的位的最低有效位的索引
库: (rnrs arithmetic bitwise), (rnrs)
输入被作为补码形式处理,即使它们在内部并不如此表示。
如果 exint
为 0, bitwise-first-bit-set
返回 -1.
(bitwise-first-bit-set #b00000) => -1
(bitwise-first-bit-set #b00001) => 0
(bitwise-first-bit-set #b01100) => 2
(bitwise-first-bit-set -1) => 0
(bitwise-first-bit-set -2) => 1
(bitwise-first-bit-set -3) => 0
过程: (bitwise-bit-set? exint1 exint2)
返回: 如果 exint1
的 exint2
位为 1,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic bitwise), (rnrs)
exint2
作为 exint1
补码形式的基于 0 的位索引。非负数的补码形式理论上向左(趋向更高有效位)延伸无穷个 0 位,而负数的补码形式理论上向左延伸无穷个 1 位。因此,精确整数可被用于表示任意大的集合,其中 0 是空集,-1 是全集,而 bitwise-bit-set?
被用于测试成员从属。
(bitwise-bit-set? #b01011 0) => #t
(bitwise-bit-set? #b01011 2) => #f
(bitwise-bit-set? -1 0) => #t
(bitwise-bit-set? -1 20) => #t
(bitwise-bit-set? -3 1) => #f
(bitwise-bit-set? 0 5000) => #f
(bitwise-bit-set? -1 5000) => #t
过程: (bitwise-copy-bit exint1 exint2 exint3)
返回: 由 exint3
取代 exint1
的 exint2
位
库: (rnrs arithmetic bitwise), (rnrs)
exint2
作为 exint1
补码形式的基于 0 的位索引。 exint3
必须是 0 或 1. 这个过程实际上是根据 exint3
的值,对指定位清零或赋值。 exint1
作为补码形式处理,即使它在内部并非如此表示。
(bitwise-copy-bit #b01110 0 1) => #b01111
(bitwise-copy-bit #b01110 2 0) => #b01010
过程: (bitwise-bit-field exint1 exint2 exint3)
返回: 参见下文
库: (rnrs arithmetic bitwise), (rnrs)
exint2
和 exint3
必须是非负的,且 exint2
必须不大于 exint3
. 这个过程返回从 exint1
中截取的索引 exint2
(包含)至 exint3
(不包含)的位序列所表示的数字。 exint1
被作为补码形式处理,即使它在内部并非如此表示。
(bitwise-bit-field #b10110 0 3) => #b00110
(bitwise-bit-field #b10110 1 3) => #b00011
(bitwise-bit-field #b10110 2 3) => #b00001
(bitwise-bit-field #b10110 3 3) => #b00000
过程: (bitwise-copy-bit-field exint1 exint2 exint3 exint4)
返回: 参见下文
库: (rnrs arithmetic bitwise), (rnrs)
exint2
和 exint3
必须非负,且 exint2
必须不大于 exint3
. 这个过程返回 exint1
中 exint2
(包含)至 exint3
(不包含)之间的 n 位被 exint4
中由低到高的 n 位取代后的数。 exint1
和 exint4
被作为补码形式处理,即使它们在内部并非如此表示。
(bitwise-copy-bit-field #b10000 0 3 #b10101) => #b10101
(bitwise-copy-bit-field #b10000 1 3 #b10101) => #b10010
(bitwise-copy-bit-field #b10000 2 3 #b10101) => #b10100
(bitwise-copy-bit-field #b10000 3 3 #b10101) => #b10000
过程: (bitwise-arithmetic-shift-right exint1 exint2)
返回: exint1
算术右移 exint2
位
过程: (bitwise-arithmetic-shift-left exint1 exint2)
返回: exint1 左移 exint2 位
库: (rnrs arithmetic bitwise), (rnrs)
exint2
必须非负。 exint1
被作为补码形式处理,即使它在内部并非如此表示。
(bitwise-arithmetic-shift-right #b10000 3) => #b00010
(bitwise-arithmetic-shift-right -1 1) => -1
(bitwise-arithmetic-shift-right -64 3) => -8
(bitwise-arithmetic-shift-left #b00010 2) => #b01000
(bitwise-arithmetic-shift-left -1 2) => -4
过程: (bitwise-arithmetic-shift exint1 exint2)
返回: 参见下文
库: (rnrs arithmetic bitwise), (rnrs)
如果 exint2
是负数, bitwise-arithmetic-shift
返回 exint1
算术右移 -exint2
位后的结果。否则, bitwise-arithmetic-shift
返回 exint1
算术左移 exint2
位的结果。 exint1
被作为补码形式处理,即使它在内部并非如此表示。
(bitwise-arithmetic-shift #b10000 -3) => #b00010
(bitwise-arithmetic-shift -1 -1) => -1
(bitwise-arithmetic-shift -64 -3) => -8
(bitwise-arithmetic-shift #b00010 2) => #b01000
(bitwise-arithmetic-shift -1 2) => -4
因此, bitwise-arithmetic-shift
的行为如同如下定义。
(define bitwise-arithmetic-shift
(lambda (exint1 exint2)
(if (< exint2 0)
(bitwise-arithmetic-shift-right exint1 (- exint2))
(bitwise-arithmetic-shift-left exint1 exint2))))
过程: (bitwise-rotate-bit-field exint1 exint2 exint3 exint4)
返回: 参见下文
库: (rnrs arithmetic bitwise), (rnrs)
exint2
, exint3
, 和 exint4
必须非负,且 exint2
必须不大于 exint3
. 这个过程返回把 exint1
中 exint2
(包含)位至 exint3
(不包含)位左移 (mod exint4 (- exint3 exint2))
位的结果,移动时超出范围的位,插入回范围最末端。 exint1
被作为补码形式处理,即使它在内部并非如此表示。
(bitwise-rotate-bit-field #b00011010 0 5 3) => #b00010110
(bitwise-rotate-bit-field #b01101011 2 7 3) => #b01011011
过程: (bitwise-reverse-bit-field exint1 exint2 exint3)
返回: 参见下文
库: (rnrs arithmetic bitwise), (rnrs)
exint2
和 exint3
必须非负,且 exint2
必须不大于 exint3
. 这个过程返回 exint1
中 exint2
(包含)位至 exint3
(不包含)位被反转后的结果。 exint1
被作为补码形式处理,即使它在内部并非如此表示。
(bitwise-reverse-bit-field #b00011010 0 5) => #b00001011
(bitwise-reverse-bit-field #b01101011 2 7) => #b00101111
过程: (string->number string)
过程: (string->number string radix)
返回: string
所表示的数字,或 #f
库: (rnrs base), (rnrs)
如果 string
是一个数字的有效表示形式,则返回此数字,否则,返回 #f
. 数字被解释时基于基数 radix
,其必须是处于集合{2,8,10,16}中的精确整数。如果没有指定, radix
默认为 10. 任何 string
中的基数指示符,比如 #x
, 都会覆盖参数 radix
。
(string->number "0") => 0
(string->number "3.4e3") => 3400.0
(string->number "#x#e-2e2") => -738
(string->number "#e-2e2" 16) => -738
(string->number "#i15/16") => 0.9375
(string->number "10" 16) => 16
过程: (number->string num)
过程: (number->string num radix)
过程: (number->string num radix precision)
返回: num
的字符串外部表示形式
库: (rnrs base), (rnrs)
num
以基数 radix
表示,其必须是处于集合{2,8,10,16}中的精确整数。如果没有指定, radix
默认为 10. 在任何情况下,结果字符串中都不包含基数指示符。
此外部表示形式是这样的,当使用 string->number
转换回数字时,结果的数值等于 num
. 即,对所有输入:
(eqv? (string->number
(number->string num radix)
radix)
num)
返回 #t
. 如果这种转换不成立,会抛出一个条件类型的 &implementation-restriction
异常。
如果指定 precision
, 它必须是一个精确的正整数, num
必须是不精确的,而 radix
必须是 10. 在这种情况下,数字的实部和虚部(如果存在)分别以显式的尾数宽度 m 打印,其中 m 是大于等于 precision
且使上述表达式为真的最小可能值。
如果 radix
为 10, num
的不精确值在不违反上述限制的前提下,使用可能的最少有效位数表示[5]。
(number->string 3.4) => "3.4"
(number->string 1e2) => "100.0"
(number->string 1e-23) => "1e-23"
(number->string -7/2) => "-7/2"
(number->string 220/9 16) => "DC/9"
6.5. 定长数 (Fixnums)
Fixnums 表示处于 fixnum 区间的精确整数,此区间须为一个闭区间 [-2w-1, 2w-1 - 1], 其中 w(fixnum 的宽度)最少是 24. 通过过程 fixnum-width
可以确定特定实现的 w 值,通过过程 least-fixnum
和 greatest-fixnum
可以确定区间的端点。
专用于操作 fixnum 的算术过程,名字以前缀"fx"开始,以区别于它们对应的通用版本。
要求是 fixnum 的过程实参被命名为 fx, 可能会带有后缀,如,fx2.
除非另外指定,fixnum 专用过程的值也是 fixnums. 如果一个 fixnum 操作的值应该是 fixnum,但数学结果却超出了 fixnum 区间,则会抛出一个条件类型的 &implementation-restriction
异常。
fixnum 的位和移位操作假定 fixnum 表示为补码形式,即使它们在内部并非如此表示。
过程: (fixnum? obj)
返回: 如果 obj
是 fixnum,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic fixnums), (rnrs)
(fixnum? 0) => #t
(fixnum? -1) => #t
(fixnum? (- (expt 2 23))) => #t
(fixnum? (- (expt 2 23) 1)) => #t
过程: (least-fixnum)
返回: 当前实现支持的最小(最大负数)fixnum
过程: (greatest-fixnum)
返回: 当前实现支持的最大(最大正数)fixnum
库: (rnrs arithmetic fixnums), (rnrs)
(fixnum? (- (least-fixnum) 1)) => #f
(fixnum? (least-fixnum)) => #t
(fixnum? (greatest-fixnum)) => #t
(fixnum? (+ (greatest-fixnum) 1)) => #f
过程: (fixnum-width)
返回: 基于实现的 fixnum 宽度
库: (rnrs arithmetic fixnums), (rnrs)
如同本节引言中介绍的,fixnum 宽度决定了 fixnum 区间的大小,且必须至少为 24.
(define w (fixnum-width))
(= (least-fixnum) (- (expt 2 (- w 1)))) => #t
(= (greatest-fixnum) (- (expt 2 (- w 1)) 1)) => #t
(>= w 24) => #t
过程: (fx=? fx_1 fx_2 fx3 ...)
过程: (fx<? fx_1 fx_2 fx3 ...)
过程: (fx>? fx_1 fx_2 fx3 ...)
过程: (fx<=? fx_1 fx_2 fx3 ...)
过程: (fx>=? fx_1 fx_2 fx3 ...)
返回: 如果关系成立,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic fixnums), (rnrs)
谓词 fx=?
在其实参相等时返回 #t
。谓词 fx<?
在其实参单调递增时返回 #t
,即,每个实参都大于它前一个实参,而 fx>?
在其实参单调递减时返回 #t
。谓词 fx<=?
在其实参单调非递减时返回 #t
,即,每个实参不小于它前一个实参,而 fx>=?
在其实参单调非递增时返回 #t
。
(fx=? 0 0) => #t
(fx=? -1 1) => #f
(fx<? (least-fixnum) 0 (greatest-fixnum)) => #t
(let ([x 3]) (fx<=? 0 x 9)) => #t
(fx>? 5 4 3 2 1) => #t
(fx<=? 1 3 2) => #f
(fx>=? 0 0 (least-fixnum)) => #t
过程: (fxzero? fx)
返回: 如果 fx
为 0,则为 #t
, 否则为 #f
.
过程: (fxpositive? fx)
返回: 如果 fx
大于 0,则为 #t
, 否则为 #f
.
过程: (fxnegative? fx)
返回: 如果 fx
小于 0,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic fixnums), (rnrs)
fxzero?
等价于 (lambda (x) (fx=? x 0))
, fxpositive?
等价于 (lambda (x) (fx>? x 0))
, 而 fxnegative?
等价于 (lambda (x) (fx<? x 0))
.
(fxzero? 0) => #t
(fxzero? 1) => #f
(fxpositive? 128) => #t
(fxpositive? 0) => #f
(fxpositive? -1) => #f
(fxnegative? -65) => #t
(fxnegative? 0) => #f
(fxnegative? 1) => #f
过程: (fxeven? fx)
返回: 如果 fx
是偶数,则为 #t
, 否则为 #f
.
过程: (fxodd? fx)
返回: 如果 fx
是奇数,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic fixnums), (rnrs)
(fxeven? 0) => #t
(fxeven? 1) => #f
(fxeven? -1) => #f
(fxeven? -10) => #t
(fxodd? 0) => #f
(fxodd? 1) => #t
(fxodd? -1) => #t
(fxodd? -10) => #f
过程: (fxmin fx_1 fx_2 ...)
返回: fx1 fx2 … 的最小值
过程: (fxmax fx_1 fx_2 ...)
返回: fx1 fx2 … 的最大值
库: (rnrs arithmetic fixnums), (rnrs)
(fxmin 4 -7 2 0 -6) => -7
(let ([ls '(7 3 5 2 9 8)])
(apply fxmin ls)) => 2
(fxmax 4 -7 2 0 -6) => 4
(let ([ls '(7 3 5 2 9 8)])
(apply fxmax ls)) => 9
过程: (fx+ fx_1 fx_2)
返回: fx1 与 fx2 的和
库: (rnrs arithmetic fixnums), (rnrs)
(fx+ -3 4) => 1
过程: (fx- fx)
返回: fx 的加法逆元
过程: (fx- fx_1 fx_2)
返回: fx1 和 fx2 的差
库: (rnrs arithmetic fixnums), (rnrs)
(fx- 3) => -3
(fx- -3 4) => -7
过程: (fx* fx_1 fx_2)
返回: fx1 和 fx2 的积
库: (rnrs arithmetic fixnums), (rnrs)
(fx* -3 4) => -12
过程: (fxdiv fx_1 fx_2)
过程: (fxmod fx_1 fx_2)
过程: (fxdiv-and-mod fx_1 fx_2)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
fx2 不能为 0.这些是通用过程 div
, mod
, 和 div-and-mod
的 fixnum 专用版本。
(fxdiv 17 3) => 5
(fxmod 17 3) => 2
(fxdiv -17 3) => -6
(fxmod -17 3) => 1
(fxdiv 17 -3) => -5
(fxmod 17 -3) => 2
(fxdiv -17 -3) => 6
(fxmod -17 -3) => 1
(fxdiv-and-mod 17 3) => 5
2
过程: (fxdiv0 fx_1 fx_2)
过程: (fxmod0 fx_1 fx_2)
过程: (fxdiv0-and-mod0 fx_1 fx_2)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
fx2 不能为 0.这些是通用过程 div0
, mod0
, 和 div0-and-mod0
的 fixnum 专用版本。
(fxdiv0 17 3) => 6
(fxmod0 17 3) => -1
(fxdiv0 -17 3) => -6
(fxmod0 -17 3) => 1
(fxdiv0 17 -3) => -6
(fxmod0 17 -3) => -1
(fxdiv0 -17 -3) => 6
(fxmod0 -17 -3) => 1
(fxdiv0-and-mod0 17 3) => 6
-1
过程: (fx+/carry fx_1 fx_2 fx3)
过程: (fx-/carry fx_1 fx_2 fx3)
过程: (fx*/carry fx_1 fx_2 fx3)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
当常规的 fixnum 加,减,乘操作溢出时,会抛出一个异常。而这些替代过程则返回一个进位,同时也支持把所进位传递到下一个操作。它们可以用于为多精度的运算实现可移植的代码。
这些过程返回下列计算得出的两个 fixnum 值。对于 fx+/carry
:
(let* ([s (+ fx_1 fx_2 fx3)]
[s0 (mod0 s (expt 2 (fixnum-width)))]
[s1 (div0 s (expt 2 (fixnum-width)))])
(values s0 s1))
对于 fx-/carry
:
(let* ([d (- fx_1 fx_2 fx3)]
[d0 (mod0 d (expt 2 (fixnum-width)))]
[d1 (div0 d (expt 2 (fixnum-width)))])
(values d0 d1))
对于 fx\*/carry
:
(let* ([s (+ (* fx_1 fx_2) fx3)]
[s0 (mod0 s (expt 2 (fixnum-width)))]
[s1 (div0 s (expt 2 (fixnum-width)))])
(values s0 s1))
过程: (fxnot fx)
返回: fx
的按位非
过程: (fx and fx ...)
返回: fx
的按位与
过程: (fxior fx ...)
返回: fx
的按位或
过程: (fxxor fx ...)
返回: fx
的按位异或
库: (rnrs arithmetic fixnums), (rnrs)
(fxnot 0) => -1
(fxnot 3) => -4
(fx and #b01101 #b00111) => #b00101
(fxior #b01101 #b00111) => #b01111
(fxxor #b01101 #b00111) => #b01010
过程: (fxif fx_1 fx_2 fx3)
返回: 实参的按位 "if"
库: (rnrs arithmetic fixnums), (rnrs)
对 fx1 的每个为 1 的位,结果的对应位取自 fx2, 而对 fx1 的每个为 0 的位,结果的对应位取自 fx3.
(fxif #b101010 #b111000 #b001100) => #b101100
fxif
可以定义如下:
(define fxif
(lambda (fx_1 fx_2 fx3)
(fxior (fx and fx_1 fx_2)
(fx and (fxnot fx_1) fx3))))
过程: (fxbit-count fx)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
对于非负的输入, fxbit-count
返回 fx
补码形式中为 1 的位数。对于负的输入,它返回一个负数,其模为 fx
中为 0 的位数加 1,等价于 (fxnot (fxbit-count (fxnot fx)))
.
(fxbit-count #b00000) => 0
(fxbit-count #b00001) => 1
(fxbit-count #b00100) => 1
(fxbit-count #b10101) => 3
(fxbit-count -1) => -1
(fxbit-count -2) => -2
(fxbit-count -4) => -3
过程: (fxlength fx)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
这个过程返回 fx
的最小补码形式的位数,对于负数,不包括符号位。对于 0, fxlength
返回 0.
(fxlength #b00000) => 0
(fxlength #b00001) => 1
(fxlength #b00100) => 3
(fxlength #b00110) => 3
(fxlength -1) => 0
(fxlength -6) => 3
(fxlength -9) => 4
过程: (fxfirst-bit-set fx)
返回: fx
为 1 的位的最低有效位的索引
库: (rnrs arithmetic fixnums), (rnrs)
如果 fx
为 0, fxfirst-bit-set
返回 -1.
(fxfirst-bit-set #b00000) => -1
(fxfirst-bit-set #b00001) => 0
(fxfirst-bit-set #b01100) => 2
(fxfirst-bit-set -1) => 0
(fxfirst-bit-set -2) => 1
(fxfirst-bit-set -3) => 0
过程: (fxbit-set? fx_1 fx_2)
返回: 如果 fx1 的 fx2 位为 1,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic fixnums), (rnrs)
fx2 必须非负。它被作为 fx1 补码形式的基于 0 的位索引。符号位事实上向左重复无穷个位置。
(fxbit-set? #b01011 0) => #t
(fxbit-set? #b01011 2) => #f
(fxbit-set? -1 0) => #t
(fxbit-set? -1 20) => #t
(fxbit-set? -3 1) => #f
(fxbit-set? 0 (- (fixnum-width) 1)) => #f
(fxbit-set? -1 (- (fixnum-width) 1)) => #t
过程: (fxcopy-bit fx_1 fx_2 fx3)
返回: 由 fx3 取代 fx1 的 fx2 位
库: (rnrs arithmetic fixnums), (rnrs)
fx2 必须非负,且小于 (- (fixnum-width) 1)
的值。fx3 必须是 0 或 1. 这个过程实际上是根据 fx3 的值,对指定位清零或赋值。
(fxcopy-bit #b01110 0 1) => #b01111
(fxcopy-bit #b01110 2 0) => #b01010
过程: (fxbit-field fx_1 fx_2 fx3)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
fx2 和 fx3 必须是非负的,且小于 (fixnum-width)
的值,而 fx2 必须不大于 fx3. 这个过程返回从 fx1 中截取的索引 fx2(包含)至 fx3(不包含)的位序列所表示的数字。
(fxbit-field #b10110 0 3) => #b00110
(fxbit-field #b10110 1 3) => #b00011
(fxbit-field #b10110 2 3) => #b00001
(fxbit-field #b10110 3 3) => #b00000
过程: (fxcopy-bit-field fx_1 fx_2 fx3 fx4)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
fx2 和 fx3 必须非负,且小于 (fixnum-width)
的值,而 fx2 必须不大于 fx3. 这个过程返回 fx1 中 fx2(包含)至 fx3(不包含)之间的 n 位被 fx4 中由低到高的 n 位取代后的数。
(fxcopy-bit-field #b10000 0 3 #b10101) => #b10101
(fxcopy-bit-field #b10000 1 3 #b10101) => #b10010
(fxcopy-bit-field #b10000 2 3 #b10101) => #b10100
(fxcopy-bit-field #b10000 3 3 #b10101) => #b10000
过程: (fxarithmetic-shift-right fx_1 fx_2)
返回: fx1 算术右移 fx2 位
过程: (fxarithmetic-shift-left fx_1 fx_2)
返回: fx1 算术左移 fx2 位
库: (rnrs arithmetic fixnums), (rnrs)
fx2 必须非负,且小于 (fixnum-width)
的值。
(fxarithmetic-shift-right #b10000 3) => #b00010
(fxarithmetic-shift-right -1 1) => -1
(fxarithmetic-shift-right -64 3) => -8
(fxarithmetic-shift-left #b00010 2) => #b01000
(fxarithmetic-shift-left -1 2) => -4
过程: (fxarithmetic-shift fx_1 fx_2)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
fx2 的绝对值必须小于 (fixnum-width)
的值。如果 fx2 是负数, bitwise-arithmetic-shift
返回 fx1 算术右移 fx2 位后的结果。否则, bitwise-arithmetic-shift
返回 fx1 算术左移 fx2 位的结果。
(fxarithmetic-shift #b10000 -3) => #b00010
(fxarithmetic-shift -1 -1) => -1
(fxarithmetic-shift -64 -3) => -8
(fxarithmetic-shift #b00010 2) => #b01000
(fxarithmetic-shift -1 2) => -4
因此, fxarithmetic-shift
的行为如同如下定义。
(define fxarithmetic-shift
(lambda (fx_1 fx_2)
(if (fx<? fx_2 0)
(fxarithmetic-shift-right fx_1 (fx- fx_2))
(fxarithmetic-shift-left fx_1 fx_2))))
过程: (fxrotate-bit-field fx_1 fx_2 fx3 fx4)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
fx2, fx3, 和 fx4 必须非负,且小于 (fixnum-width)
的值,而 fx2 必须不大于 fx3, fx4 必须不大于 fx3 和 fx2 的差。
这个过程返回把 fx1 中 fx2(包含)位至 fx3(不包含)位左移 fx4 位的结果,移动时超出范围的位,插入回范围最末端。
(fxrotate-bit-field #b00011010 0 5 3) => #b00010110
(fxrotate-bit-field #b01101011 2 7 3) => #b01011011
过程: (fxreverse-bit-field fx_1 fx_2 fx3)
返回: 参见下文
库: (rnrs arithmetic fixnums), (rnrs)
fx2 和 fx3 必须非负,且小于 (fixnum-width)
的值,而 fx2 必须不大于 fx3. 这个过程返回 fx1 中 fx2(包含)位至 fx3(不包含)位被反转后的结果。
(fxreverse-bit-field #b00011010 0 5) => #b00001011
(fxreverse-bit-field #b01101011 2 7) => #b00101111
6.6. 浮点数 (Flonums)
Flonums 表示不精确的实数。要求实现可以把任何词法形式中不包含竖线和除了 e 以外的指数标记的不精确实数表示为 flonum, 但不要求把任何其它不精确实数表示为 flonum.
实现通常使用 IEEE 双精度浮点形式表示 flonums, 但这并非硬性要求,甚至不要求实现使用任何种类的浮点表示形式,尽管其名称为 "flonum".
本节介绍了 flonum 操作。专用于 flonum 的过程,名字以前缀"fl"开始,以区别于它们对应的通用版本。
过程实参需要是 flonum 的被命名为 fl, 可能会带有后缀,例如,fl2. 除非另外指定,flonum 专用过程的值也是 flonum.
过程: (flonum? obj)
返回: 如果 obj
是 flonum,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic flonums), (rnrs)
(flonum? 0) => #f
(flonum? 3/4) => #f
(flonum? 3.5) => #t
(flonum? .02) => #t
(flonum? 1e10) => #t
(flonum? 3.0+0.0i) => #f
过程: (fl=? fl1 fl2 fl3 ...)
过程: (fl<? fl1 fl2 fl3 ...)
过程: (fl>? fl1 fl2 fl3 ...)
过程: (fl<=? fl1 fl2 fl3 ...)
过程: (fl>=? fl1 fl2 fl3 ...)
返回: 如果关系成立,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic flonums), (rnrs)
谓词 fl=?
在其实参相等时返回 #t
。谓词 fl<?
在其实参单调递增时返回 #t
,即,每个实参都大于它前一个实参,而 fl>?
在其实参单调递减时返回 #t
。谓词 fl<=?
在其实参单调非递减时返回 #t
,即,每个实参不小于它前一个实参,而 fl>=?
在其实参单调非递增时返回 #t
。当只传入一个实参时,这些谓词均返回 #t
.
涉及 NaNs 的比较总是返回 #f
.
(fl=? 0.0 0.0) => #t
(fl<? -1.0 0.0 1.0) => #t
(fl>? -1.0 0.0 1.0) => #f
(fl<=? 0.0 3.0 3.0) => #t
(fl>=? 4.0 3.0 3.0) => #t
(fl<? 7.0 +inf.0) => #t
(fl=? +nan.0 0.0) => #f
(fl=? +nan.0 +nan.0) => #f
(fl<? +nan.0 +nan.0) => #f
(fl<=? +nan.0 +inf.0) => #f
(fl>=? +nan.0 +inf.0) => #f
过程: (flzero? fl)
返回: 如果 fl
为 0,则为 #t
, 否则为 #f
.
过程: (flpositive? fl)
返回: 如果 fl
大于 0,则为 #t
, 否则为 #f
.
过程: (flnegative? fl)
返回: 如果 fl
小于 0,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic flonums), (rnrs)
flzero?
等价于 (lambda (x) (fl=? x 0.0))
, flpositive?
等价于 (lambda (x) (fl>? x 0.0))
, 而 flnegative?
等价于 (lambda (x) (fl<? x 0.0))
.
虽然 flonum 表示形式区分 -0.0
和 +0.0
, 但-0.0
被判定为 0 且非负。
(flzero? 0.0) => #t
(flzero? 1.0) => #f
(flpositive? 128.0) => #t
(flpositive? 0.0) => #f
(flpositive? -1.0) => #f
(flnegative? -65.0) => #t
(flnegative? 0.0) => #f
(flnegative? 1.0) => #f
(flzero? -0.0) => #t
(flnegative? -0.0) => #f
(flnegative? +nan.0) => #f
(flzero? +nan.0) => #f
(flpositive? +nan.0) => #f
(flnegative? +inf.0) => #f
(flnegative? -inf.0) => #t
过程: (flinteger? fl)
返回: 如果 fl
是整数,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic flonums), (rnrs)
(flinteger? 0.0) => #t
(flinteger? -17.0) => #t
(flinteger? +nan.0) => #f
(flinteger? +inf.0) => #f
过程: (flfinite? fl)
返回: 如果 fl
是有限值,则为 #t
, 否则为 #f
.
过程: (flinfinite? fl)
返回: 如果 fl
是无限值,则为 #t
, 否则为 #f
.
过程: (flnan? fl)
返回: 如果 fl
是 NaN, 则为 #t
, 否则为 #f
.
库: (rnrs arithmetic flonums), (rnrs)
(flfinite? 3.1415) => #t
(flinfinite? 3.1415) => #f
(flnan? 3.1415) => #f
(flfinite? +inf.0) => #f
(flinfinite? -inf.0) => #t
(flnan? -inf.0) => #f
(flfinite? +nan.0) => #f
(flinfinite? +nan.0) => #f
(flnan? +nan.0) => #t
过程: (fleven? fl-int)
返回: 如果 fl-int
是偶数,则为 #t
, 否则为 #f
.
过程: (flodd? fl-int)
返回: 如果 fl-int
是奇数,则为 #t
, 否则为 #f
.
库: (rnrs arithmetic flonums), (rnrs)
fl-int 必须是一个值为整数的 flonum.
(fleven? 0.0) => #t
(fleven? 1.0) => #f
(fleven? -1.0) => #f
(fleven? -10.0) => #t
(flodd? 0.0) => #f
(flodd? 1.0) => #t
(flodd? -1.0) => #t
(flodd? -10.0) => #f
过程: (flmin fl1 fl2 ...)
返回: fl1 fl2 … 的最小值
过程: (flmax fl1 fl2 ...)
返回: fl1 fl2 … 的最大值
库: (rnrs arithmetic flonums), (rnrs)
(flmin 4.2 -7.5 2.0 0.0 -6.4) => -7.5
(let ([ls '(7.1 3.5 5.0 2.6 2.6 8.0)])
(apply flmin ls)) => 2.6
(flmax 4.2 -7.5 2.0 0.0 -6.4) => 4.2
(let ([ls '(7.1 3.5 5.0 2.6 2.6 8.0)])
(apply flmax ls)) => 8.0
过程: (fl+ fl ...)
返回: 实参 fl … 的和
库: (rnrs arithmetic flonums), (rnrs)
不带实参调用时,fl+
返回 0.0
.
(fl+) => 0.0
(fl+ 1.0 2.5) => 3.25
(fl+ 3.0 4.25 5.0) => 12.25
(apply fl+ '(1.0 2.0 3.0 4.0 5.0)) => 15.0
过程: (fl- fl)
返回: fl 的加法逆元
过程: (fl- fl1 fl2 fl3 ...)
返回: fl1 与 fl2 fl3 … 之和的差
库: (rnrs arithmetic flonums), (rnrs)
对于 flonums 的 IEEE 浮点表示形式,单参数的 fl-
等价于
(lambda (x) (fl* -1.0 x))
或
(lambda (x) (fl- -0.0 x))
但不是
(lambda (x) (fl- 0.0 x))
因为,对于 0.0
, 后者返回 0.0
, 而不是-0.0
.
(fl- 0.0) => -0.0
(fl- 3.0) => -3.0
(fl- 4.0 3.0) => 1.0
(fl- 4.0 3.0 2.0 1.0) => -2.0
过程: (fl* fl ...)
返回: 实参 fl … 的积
库: (rnrs arithmetic flonums), (rnrs)
不带实参调用时,fl\*
返回 1.0.
(fl*) => 1.0
(fl* 1.5 2.5) => 3.75
(fl* 3.0 -4.0 5.0) => -60.0
(apply fl* '(1.0 -2.0 3.0 -4.0 5.0)) => 120.0
过程: (fl/ fl)
返回: fl 的乘法逆元
过程: (fl/ fl1 fl2 fl3 ...)
返回: fl1 除以 fl2 fl3 … 之积的结果
库: (rnrs arithmetic flonums), (rnrs)
(fl/ -4.0) => -0.25
(fl/ 8.0 -2.0) => -4.0
(fl/ -9.0 2.0) => -4.5
(fl/ 60.0 5.0 3.0 2.0) => 2.0
过程: (fldiv fl1 fl2)
过程: (flmod fl1 fl2)
过程: (fldiv-and-mod fl1 fl2)
返回: 参见下文
库: (rnrs arithmetic flonums), (rnrs)
这些是通用过程 div
, mod
, 和 div-and-mod
的 flonum 专用版本。
(fldiv 17.0 3.0) => 5.0
(flmod 17.0 3.0) => 2.0
(fldiv -17.0 3.0) => -6.0
(flmod -17.0 3.0) => 1.0
(fldiv 17.0 -3.0) => -5.0
(flmod 17.0 -3.0) => 2.0
(fldiv -17.0 -3.0) => 6.0
(flmod -17.0 -3.0) => 1.0
(fldiv-and-mod 17.5 3.75) => 4.0
2.5
过程: (fldiv0 fl1 fl2)
过程: (flmod0 fl1 fl2)
过程: (fldiv0-and-mod0 fl1 fl2)
返回: 参见下文
库: (rnrs arithmetic flonums), (rnrs)
这些是通用过程 div0
, mod0
, 和 div0-and-mod0
的 flonum 专用版本。
(fldiv0 17.0 3.0) => 6.0
(flmod0 17.0 3.0) => -1.0
(fldiv0 -17.0 3.0) => -6.0
(flmod0 -17.0 3.0) => 1.0
(fldiv0 17.0 -3.0) => -6.0
(flmod0 17.0 -3.0) => -1.0
(fldiv0 -17.0 -3.0) => 6.0
(flmod0 -17.0 -3.0) => 1.0
(fldiv0-and-mod0 17.5 3.75) => 5.0
-1.25
过程: (flround fl)
返回: 最接近 fl 的整数
过程: (fltruncate fl)
返回: 在趋于 0 的方向上最接近 fl 的整数
过程: (flfloor fl)
返回: 趋于 -∞ 的方向上最接近 fl 的整数
过程: (flceiling fl)
返回: 趋于 +∞ 的方向上最接近 fl 的整数
库: (rnrs arithmetic flonums), (rnrs)
如果 fl
是整数,NaN, 或无穷,这些过程均返回 fl
. 如果 fl
处于两个整数的正中间, flround
返回最接近的偶数。
(flround 17.3) => 17.0
(flround -17.3) => -17.0
(flround 2.5) => 2.0
(flround 3.5) => 4.0
(fltruncate 17.3) => 17.0
(fltruncate -17.3) => -17.0
(flfloor 17.3) => 17.0
(flfloor -17.3) => -18.0
(flceiling 17.3) => 18.0
(flceiling -17.3) => -17.0
过程: (flnumerator fl)
返回: fl 的分子
过程: (fldenominator fl)
返回: fl 的分母
库: (rnrs arithmetic flonums), (rnrs)
如果 fl
是整数,包括 0.0, 或无穷,则分子为 fl
,而分母为 1.0.
(flnumerator -9.0) => -9.0
(fldenominator -9.0) => 1.0
(flnumerator 0.0) => 0.0
(fldenominator 0.0) => 1.0
(flnumerator -inf.0) => -inf.0
(fldenominator -inf.0) => 1.0
以下代码对 IEEE 浮点形式成立,但对其它 flonum 表示形式不一定成立。
(flnumerator 3.5) => 7.0
(fldenominator 3.5) => 2.0
过程: (flabs fl)
返回: fl 的绝对值
库: (rnrs arithmetic flonums), (rnrs)
(flabs 3.2) => 3.2
(flabs -2e-20) => 2e-20
过程: (flexp fl)
返回: e 的 fl 次幂
过程: (fllog fl)
返回: fl 的自然对数
过程: (fllog fl1 fl2)
返回: fl1 以 fl2 为底的对数
库: (rnrs arithmetic flonums), (rnrs)
(flexp 0.0) => 1.0
(flexp 1.0) => 2.7182818284590455
(fllog 1.0) => 0.0
(fllog (exp 1.0)) => 1.0
(fl/ (fllog 100.0) (fllog 10.0)) => 2.0
(fllog 100.0 10.0) => 2.0
(fllog .125 2.0) => -3.0
过程: (flsin fl)
返回: fl 的正弦值
过程: (flcos fl)
返回: fl 的余弦值
过程: (fltan fl)
返回: fl 的正切值
库: (rnrs arithmetic flonums), (rnrs)
过程: (flasin fl)
返回: fl 的反正弦值
过程: (flacos fl)
返回: fl 的反余弦值
过程: (flatan fl)
返回: fl 的反正切值
过程: (flatan fl1 fl2)
返回: fl1/fl2 的反正切值
库: (rnrs arithmetic flonums), (rnrs)
过程: (flsqrt fl)
返回: fl 的主平方根
库: (rnrs arithmetic flonums), (rnrs)
返回 fl
的主平方根。-0.0 的主平方根应为 -0.0. 对其它负数的结果可能是 NaN 或其它某个未定义 flonum.
(flsqrt 4.0) => 2.0
(flsqrt 0.0) => 0.0
(flsqrt -0.0) => -0.0
过程: (flexpt fl1 fl2)
返回: fl1 的 fl2 次幂
库: (rnrs arithmetic flonums), (rnrs)
如果 fl1
是负数,而 fl2
不是整数,结果则可能为 NaN 或其它某个未定义 flonum. 如果 fl1
和 fl2
都是 0, 则结果为 1.0. 如果 fl1
为 0 而 fl2
是正数,则结果为 0. 对于 fl1
为 0 的其它情况,结果可能是 NaN 或其它某个未定义 flonum.
(flexpt 3.0 2.0) => 9.0
(flexpt 0.0 +inf.0) => 0.0
过程: (fixnum->flonum fx)
返回: 最接近 fx 的 flonum 表示形式
过程: (real->flonum real)
返回: 最接近 real 的 flonum 表示形式
库: (rnrs arithmetic flonums), (rnrs)
fixnum->flonum
是 inexact
的一个受限变体。当输入是一个精确实数时, real->flonum
是 inexact
的一个受限变体;当输入是一个不精确的非 flonum 实数,它会把这个不精确的非 flonum 实数转化为最接近的 flonum.
(fixnum->flonum 0) => 0.0
(fixnum->flonum 13) => 13.0
(real->flonum -1/2) => -0.5
(real->flonum 1s3) => 1000.0
6.7. 字符
字符是原子对象,表示字母,数字,特殊符号,如 $ 或 -,以及某些非图形控制字符,如空格和换行。字符的写法带有前缀 #\. 对于多数字符,前缀后面跟着字符本身。例如,字母 A 的字符写法为 #\A. 换行字符,空格字符和制表符也可以以这种方式书写,但是,它们也可以更清晰地写作 #\newline, #\space, 和 #\tab. 其它的字符名也支持,如 457 页的字符对象语法所定义的。所有 unicode 字符都可以以语法形式 #\xn 书写, 其中 n 由一个或多个十六进制数字组成,表示一个有效的 Unicode 标量值。
本节介绍了主要用于处理字符的操作。参见下一节的字符串操作,以及第 7 章输入和输出,以了解其它与字符有关的操作。
过程: (char=? char1 char2 char3 ...)
过程: (char<? char1 char2 char3 ...)
过程: (char>? char1 char2 char3 ...)
过程: (char<=? char1 char2 char3 ...)
过程: (char>=? char1 char2 char3 ...)
返回: 如果关系成立,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
这些谓词的行为方式与数字谓词 =
, <
, >
, <=
, 及 >=
类似。例如, char=?
在其实参为相等字符时返回 #t
, 而 char<?
在其实参的字符值(Unicode 标量)单调递增时返回 #t
.
(char>? #\a #\b) => #f
(char<? #\a #\b) => #t
(char<? #\a #\b #\c) => #t
(let ([c #\r])
(char<=? #\a c #\z)) => #t
(char<=? #\Z #\W) => #f
(char=? #\+ #\+) => #t
过程: (char-ci=? char1 char2 char3 ...)
过程: (char-ci<? char1 char2 char3 ...)
过程: (char-ci>? char1 char2 char3 ...)
过程: (char-ci<=? char1 char2 char3 ...)
过程: (char-ci>=? char1 char2 char3 ...)
返回: 如果关系成立,则为 #t
, 否则为 #f
.
库: (rnrs unicode), (rnrs)
这些谓词和谓词 char=?
, char<?
, char>?
, char<=?
, 和 char>=?
是一样的,只是它们不区分大小写,即,比较它们的实参经过大小写折叠后的版本。例如, char=?
判定 #\a 和 #\A 是不同的值;而 char-ci=?
则判定为相同。
(char-ci<? #\a #\B) => #t
(char-ci=? #\W #\w) => #t
(char-ci=? #\= #\+) => #f
(let ([c #\R])
(list (char<=? #\a c #\z)
(char-ci<=? #\a c #\z))) => (#f #t)
过程: (char-alphabetic? char)
返回: 如果 char
是字母,则为 #t
, 否则为 #f
.
过程: (char-numeric? char)
返回: 如果 char
是数字,则为 #t
, 否则为 #f
.
过程: (char-whitespace? char)
返回: 如果 char
是空白字符,则为 #t
, 否则为 #f
.
库: (rnrs unicode), (rnrs)
如果字符带有 Unicode "Alphabetic" 属性,则它是字母;如果字符带有 Unicode "Numeric" 属性,则它是数字;如果字符带有 Unicode "White_Space" 属性,则为空白字符。
(char-alphabetic? #\a) => #t
(char-alphabetic? #\T) => #t
(char-alphabetic? #\8) => #f
(char-alphabetic? #\$) => #f
(char-numeric? #\7) => #t
(char-numeric? #\2) => #t
(char-numeric? #\X) => #f
(char-numeric? #\space) => #f
(char-whitespace? #\space) => #t
(char-whitespace? #\newline) => #t
(char-whitespace? #\Z) => #f
过程: (char-lower-case? char)
返回: 如果 char
是小写,则为 #t
, 否则为 #f
.
过程: (char-upper-case? char)
返回: 如果 char
是大写,则为 #t
, 否则为 #f
.
过程: (char-title-case? char)
返回: 如果 char
是首字母大写,则为 #t
, 否则为 #f
.
库: (rnrs unicode), (rnrs)
如果字符带有 Unicode "Uppercase" 属性,则为大写;带有 "Lowercase" 属性,则为小写;而如果它属于 Lt 通用类别,则为首字母大写。
(char-lower-case? #\r) => #t
(char-lower-case? #\R) => #f
(char-upper-case? #\r) => #f
(char-upper-case? #\R) => #t
(char-title-case? #\I) => #f
(char-title-case? #\x01C5) => #t
过程: (char-general-category char)
返回: 一个用来表示 char
的 Unicode 通用类别的符号
库: (rnrs unicode), (rnrs)
返回值是以下符号之一:Lu, Ll, Lt, Lm, Lo, Mn, Mc, Me, Nd, Nl, No, Ps, Pe, Pi, Pf, Pd, Pc, Po, Sc, Sm, Sk, So, Zs, Zp, Zl, Cc, Cf, Cs, Co, or Cn.
(char-general-category #\a) => Ll
(char-general-category #\space) => Zs
(char-general-category #\x_10FFFF) => Cn
过程: (char-upcase char)
返回: char
对应的大写字符
库: (rnrs unicode), (rnrs)
如果 char
是小写或首字母大写字符,且有一个对应的大写字符,则 char-upcase
返回它对应的大写字符。否则, char-upcase
返回 char
.
(char-upcase #\g) => #\G
(char-upcase #\G) => #\G
(char-upcase #\7) => #\7
(char-upcase #\sigmaf) => #\Sigma
过程: (char-downcase char)
返回: char
对应的小写字符
库: (rnrs unicode), (rnrs)
如果 char
是大写或首字母大写字符,且有一个对应的小写字符,则 char-downcase
返回它对应的小写字符。否则, char-downcase
返回 char
.
(char-downcase #\g) => #\g
(char-downcase #\G) => #\g
(char-downcase #\7) => #\7
(char-downcase #\sigmaf) => #\sigmaf
过程: (char-titlecase char)
返回: char
对应的首字母大写字母
库: (rnrs unicode), (rnrs)
如果 char
是大写或首字母小写字符,且有一个对应的首字母大写字符,则 char-titlecase
返回它对应的首字母大写字符。否则,如果 char
不是首字母大写字符,且没有一个对应的首字母大写字符, char-titlecase
返回对应的大写字符。否则, char-titlecase
返回 char
.
(char-titlecase #\g) => #\G
(char-titlecase #\G) => #\G
(char-titlecase #\7) => #\7
(char-titlecase #\sigmaf) => #\Sigma
过程: (char-foldcase char)
返回: char
对应的大小写折叠字符
库: (rnrs unicode), (rnrs)
如果 char
有一个对应的大小写折叠字符,则 char-foldcase
返回对应的大小写折叠字符。否则, char-foldcase
返回 char
. 对大多数字符,(char-foldcase char)
等价于 (char-downcase (char-upcase char))
, 但对于土耳其语的 İ 和 ı, char-foldcase
返回字符自身。
(char-foldcase #\g) => #\g
(char-foldcase #\G) => #\g
(char-foldcase #\7) => #\7
(char-foldcase #\sigmaf) => #\sigma
过程: (char->integer char)
返回: 一个精确的整数,为 char
的 Unicode 标量值
库: (rnrs base), (rnrs)
(char->integer #\newline) => 10
(char->integer #\space) => 32
(- (char->integer #\Z) (char->integer #\A)) => 25
过程: (integer->char n)
返回: Unicode 标量值 n
对应的字符
库: (rnrs base), (rnrs)
n
必须是精确的整数,且为有效的 Unicode 标量值,即, 0 \le n \le #xD7FF
或 #xE000 \le n \le 10FFFF
.
(integer->char 48) => #\0
(integer->char #x3BB) => #\lambda
6.8. 字符串
字符串是字符序列,常用作消息,字符缓冲区,或文本块的容器。Scheme 提供了用于创建字符串,从字符串中提取字符,获取子字符串,拼接字符串,以及修改字符串内容的操作。
字符串写作包围在双引号中的字符序列,例如, "hi there". 可以通过前置反斜线把双引号引入一个字符串,例如, "two \"quotes\" within". 反斜线同样可以通过前置一个反斜线被包含进来,例如, "a \\". 很多特殊字符都可以以另一种双字符序列的形式插入字符串,例如,\n 是换行,\r 是回车,而 \t 是制表符。所有 Unicode 字符都可以用语法形式 #\xn; 插入到字符串中,其中 n 由一个或多个十六进制数字组成,表示一个有效的 Unicode 标量值。字符串的精确语法定义在 458 页给出。
字符串以精确非负整数为索引,任何字符串的第一个元素的索引都是 0. 一个给定字符串的最大有效索引是它的长度减 1.
过程: (string=? string1 string2 string3 ...)
过程: (string<? string1 string2 string3 ...)
过程: (string>? string1 string2 string3 ...)
过程: (string<=? string1 string2 string3 ...)
过程: (string>=? string1 string2 string3 ...)
返回: 如果关系成立,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
如同 =
, <
, >
, <=
, 和 >=
, 这些谓词表达了所有实参之间的关系。例如,string>?
判定它的实参是否依字母顺序单调递减。
这些比较基于字符谓词 char=?
和 char<?
. 如果两个字符串长度相同,且由相同(通过 char=?
判定)的字符序列组成,则它们是依字母顺序相等的。如同它们只是长度不同,则判定长度较短的字符串依字母顺序小于较长的字符串。否则,第一个有不同(通过 char=?
判定)字符的位置决定哪个字符串依字母顺序较小(基于 char<?
判定)。
不带错误检查的有两个参数的 string=?
可以定义如下。
(define string=?
(lambda (s1 s2)
(let ([n (string-length s1)])
(and (= (string-length s2) n)
(let loop ([i 0])
(or (= i n)
(and (char=? (string-ref s1 i) (string-ref s2 i))
(loop (+ i 1)))))))))
不带错误检查的有两个参数的 string<?
可以定义如下。
(define string<?
(lambda (s1 s2)
(let ([n1 (string-length s1)] [n2 (string-length s2)])
(let loop ([i 0])
(and (not (= i n2))
(or (= i n1)
(let ([c1 (string-ref s1 i)] [c2 (string-ref s2 i)])
(or (char<? c1 c2)
(and (char=? c1 c2)
(loop (+ i 1)))))))))))
这些定义可以直接扩展为支持三个或更多参数。 string<=?
, string>?
, 和 string>=?
可以以类似的方法定义。
(string=? "mom" "mom") => #t
(string<? "mom" "mommy") => #t
(string>? "Dad" "Dad") => #f
(string=? "Mom and Dad" "mom and dad") => #f
(string<? "a" "b" "c") => #t
过程: (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
.
库: (rnrs unicode), (rnrs)
这些谓词和谓词 string=?
, string<?
, string>?
, string<=?
, 和 string>=?
是一样的,只是它们不区分大小写,即,比较它们的实参经过大小写折叠后的版本。
(string-ci=? "Mom and Dad" "mom and dad") => #t
(string-ci<=? "say what" "Say What!?") => #t
(string-ci>? "N" "m" "L" "k") => #t
(string-ci=? "Straße" "Strasse") => #t
过程: (string char ...)
返回: 一个由字符 char … 组成的字符串
库: (rnrs base), (rnrs)
(string) => ""
(string #\a #\b #\c) => "abc"
(string #\H #\E #\Y #\!) => "HEY!"
过程: (make-string n)
过程: (make-string n char)
返回: 长度为 n 的字符串
库: (rnrs base), (rnrs)
n
必须是精确的非负整数。如果指定 char
, 则字符串以 n
个 char
填充,否则,字符串包含的字符则是未定义的。
(make-string 0) => ""
(make-string 0 #\x) => ""
(make-string 5 #\x) => "xxxxx"
过程: (string-length string)
返回: string 中的字符数量
库: (rnrs base), (rnrs)
字符串的长度总是一个精确的非负整数。
(string-length "abc") => 3
(string-length "") => 0
(string-length "hi there") => 8
(string-length (make-string 1000000)) => 1000000
过程: (string-ref string n)
返回: string
中的第 n
个字符(基于 0)
库: (rnrs base), (rnrs)
n
必须是精确的非负整数,且小于 string
的长度。
(string-ref "hi there" 0) => #\h
(string-ref "hi there" 5) => #\e
过程: (string-set! string n char)
返回: 未定义
库: (rnrs mutable-strings)
n
必须是精确的非负整数,且小于 string
的长度。 string-set!
把 string
的第 n
个元素修改为 char
.
(let ([str (string-copy "hi three")])
(string-set! str 5 #\e)
(string-set! str 6 #\r)
str) => "hi there"
过程: (string-copy string)
返回: string
的一份拷贝
库: (rnrs base), (rnrs)
这个过程创建一个新字符串,长度和内容与 string
相同。
(string-copy "abc") => "abc"
(let ([str "abc"])
(eq? str (string-copy str))) => #f
过程: (string-append string ...)
返回: 由多个字符串 string …拼接形成的新字符串
库: (rnrs base), (rnrs)
(string-append) => ""
(string-append "abc" "def") => "abcdef"
(string-append "Hey " "you " "there!") => "Hey you there!"
string-append
的如下实现,对字符串列表进行递归,以计算总长度,然后分配新的字符串,最后在展开递归时进行填充。
(define string-append
(lambda args
(let f ([ls args] [n 0])
(if (null? ls)
(make-string n)
(let* ([s1 (car ls)]
[m (string-length s1)]
[s2 (f (cdr ls) (+ n m))])
(do ([i 0 (+ i 1)] [j n (+ j 1)])
((= i m) s2)
(string-set! s2 j (string-ref s1 i))))))))
过程: (substring string start end)
返回: string
从 start
(包含)至 end
(不包含)之间部分的拷贝
库: (rnrs base), (rnrs)
start
和 end
必须是精确的非负整数; start
必须小于等于 end
, 同时 end
必须小于等于 string
的长度。如果 end = start
, 则返回一个长度为 0 的字符串。不带错误检查的 substring
可以定义如下。
(define substring
(lambda (s1 m n)
(let ([s2 (make-string (- n m))])
(do ([j 0 (+ j 1)] [i m (+ i 1)])
((= i n) s2)
(string-set! s2 j (string-ref s1 i))))))
(substring "hi there" 0 1) => "h"
(substring "hi there" 3 6) => "the"
(substring "hi there" 5 5) => ""
(let ([str "hi there"])
(let ([end (string-length str)])
(substring str 0 end))) => "hi there"
过程: (string-fill! string char)
返回: 未定义
库: (rnrs mutable-strings)
string-fill!
把 string
中的每个字符都设为 char
.
(let ([str (string-copy "sleepy")])
(string-fill! str #\Z)
str) => "ZZZZZZ"
string-fill!
可以定义如下:
(define string-fill!
(lambda (s c)
(let ([n (string-length s)])
(do ([i 0 (+ i 1)])
((= i n))
(string-set! s i c)))))
另一种定义方式在 276 页给出。
过程: (string-upcase string)
返回: string
的等价大写形式
过程: (string-downcase string)
返回: string
的等价小写形式
过程: (string-foldcase string)
返回: string
的等价大小写折叠形式
过程: (string-titlecase string)
返回: string
的等价首字母大写形式
库: (rnrs unicode), (rnrs)
这些过程实现了 Unicode 不依赖于区域的从标量值序列到标量值序列的大小写映射。这些映射不总是把单个字符映射到单个字符,因此结果字符串的长度可能与 string
的长度不同。如果结果字符串与 string
相同(基于 string=?
判定),则返回 string
或 string
的拷贝。否则,结果字符串则是新分配的。 string-foldcase
不使用土耳其语的特殊映射。
string-titlecase
把 string
中每个单词的第一个字符转化为对应的首字母大写字符,并把其它所有字符转化为对应的小写字符。单词分界依 Unicode 标准附件 #29 [8] 中指定的方式识别。
(string-upcase "Hi") => "HI"
(string-downcase "Hi") => "hi"
(string-foldcase "Hi") => "hi"
(string-upcase "Straße") => "STRASSE"
(string-downcase "Straße") => "straße"
(string-foldcase "Straße") => "strasse"
(string-downcase "STRASSE") => "strasse"
(string-downcase "\Sigma") => \sigma
(string-titlecase "kNock KNoCK") => "Knock Knock"
(string-titlecase "who's there?") => "Who's There?"
(string-titlecase "r6rs") => "R6rs"
(string-titlecase "R6RS") => "R6rs"
过程: (string-normalize-nfd string)
返回: string
的 Unicode 标准化形式 D
过程: (string-normalize-nfkd string)
返回: string
的 Unicode 标准化形式 KD
过程: (string-normalize-nfc string)
返回: string
的 Unicode 标准化形式 C
过程: (string-normalize-nfkc string)
返回: string
的 Unicode 标准化形式 KC
库: (rnrs unicode), (rnrs)
如果结果字符串与 string
相同(基于 string=?
判定),则返回 string
或 string
的拷贝。否则,结果字符串则是新分配的。
(string-normalize-nfd "\xE9;") => "e\x301;"
(string-normalize-nfc "\xE9;") => "\xE9;"
(string-normalize-nfd "\x65;\x301;") => "e\x301;"
(string-normalize-nfc "\x65;\x301;") => "\xE9;"
过程: (string->list string)
返回: string
中的字符组成的列表
库: (rnrs base), (rnrs)
string->list
支持把一个字符串转化为一个列表,从而使 Scheme
的列表处理操作可以应用于字符串处理。不带错误检查的 string->list
可以定义如下。
(define string->list
(lambda (s)
(do ([i (- (string-length s) 1) (- i 1)]
[ls '() (cons (string-ref s i) ls)])
((< i 0) ls))))
(string->list "") => ()
(string->list "abc") => (#\a #\b #\c)
(apply char<? (string->list "abc")) => #t
(map char-upcase (string->list "abc")) => (#\A #\B #\C)
过程: (list->string list)
返回: list
中的字符组成的字符串
库: (rnrs base), (rnrs)
list
必须完全由字符组成。
list->string
在功能上与 string->list
相反。一段程序可以组合使用这两个过程,先把一个字符串转化为列表,然后操作此列表以生成一个新列表,最后再把这个新列表转化为一个字符串。
不带错误检查的 list->string
可以定义如下。
(define list->string
(lambda (ls)
(let ([s (make-string (length ls))])
(do ([ls ls (cdr ls)] [i 0 (+ i 1)])
((null? ls) s)
(string-set! s i (car ls))))))
(list->string '()) => ""
(list->string '(#\a #\b #\c)) => "abc"
(list->string
(map char-upcase
(string->list "abc"))) => "ABC"
6.9. 向量
在某些应用中,向量比列表更方便也更高效。访问列表中的任意元素需要线性遍历列表,直到找到所要访问的元素,而在常量时间内,即可访问任意向量元素。向量的长度是它所包含的元素数量。向量由精确非负整数索引,且任意向量的第一个元素的索引为 0. 对一个特定向量,最大有效索引为它的长度减 1.
和列表一样,向量的元素可以是任意类型,且一个向量可以包含不只一种类型的对象。
向量写作由空白字符隔开的对象序列,前缀为 #(
, 后面跟着 )
. 例如,由元素 a, b, 和 c 组成的向量,写作 #(a b c)
.
过程: (vector obj ...)
返回: 由对象 obj … 组成的向量
库: (rnrs base), (rnrs)
(vector) => #()
(vector 'a 'b 'c) => #(a b c)
过程: (make-vector n)
过程: (make-vector n obj)
返回: 长度为 n 的向量
库: (rnrs base), (rnrs)
n
必须是精确的非负整数。如果指定 obj
, 则 vector
中的每个元素由 obj
填充;否则,元素则是未定义的。
(make-vector 0) => #()
(make-vector 0 '#(a)) => #()
(make-vector 5 '#(a)) => #(#(a) #(a) #(a) #(a) #(a))
过程: (vector-length vector)
返回: vector
中元素的数量
库: (rnrs base), (rnrs)
vector
的长度总是一个精确的非负整数。
(vector-length '#()) => 0
(vector-length '#(a b c)) => 3
(vector-length (vector 1 '(2) 3 '#(4 5))) => 4
(vector-length (make-vector 300)) => 300
过程: (vector-ref vector n)
返回: vector
的第 n
个元素(基于 0)
库: (rnrs base), (rnrs)
n
必须是一个精确的非负整数,且小于 vector
的长度。
(vector-ref '#(a b c) 0) => a
(vector-ref '#(a b c) 1) => b
(vector-ref '#(x y z w) 3) => w
过程: (vector-set! vector n obj)
返回: 未定义
库: (rnrs base), (rnrs)
n
必须是精确的非负整数,且小于 vector
的长度。 vector-set!
把 vector
的第 n
个元素修改为 obj
.
(let ([v (vector 'a 'b 'c 'd 'e)])
(vector-set! v 2 'x)
v) => #(a b x d e)
过程: (vector-fill! vector obj)
返回: 未定义
库: (rnrs base), (rnrs)
vector-fill!
把 vector
中的每个元素都替换为 obj
. 不带错误检查的话,它可以定义如下。
(define vector-fill!
(lambda (v x)
(let ([n (vector-length v)])
(do ([i 0 (+ i 1)])
((= i n))
(vector-set! v i x)))))
(let ([v (vector 1 2 3)])
(vector-fill! v 0)
v) => #(0 0 0)
过程: (vector->list vector)
返回: vector
中的元素组成的列表
库: (rnrs base), (rnrs)
vector->list
提供了一种便捷的方法,以把列表处理操作应用到向量上。不带错误检查的话,它可以定义如下。
(define vector->list
(lambda (s)
(do ([i (- (vector-length s) 1) (- i 1)]
[ls '() (cons (vector-ref s i) ls)])
((< i 0) ls))))
(vector->list (vector)) => ()
(vector->list '#(a b c)) => (a b c)
(let ((v '#(1 2 3 4 5)))
(apply * (vector->list v))) => 120
过程: (list->vector list)
返回: list
的元素组成的列表
库: (rnrs base), (rnrs)
list->vector
在功能上与 vector->list
相反。这两个过程经常组合使用,以利用列表处理操作。一个向量可以通过 vector->list
转化为一个列表,然后以某种方式处理这个列表,进而生成一个新列表,然后再使用 list->vector
把这个新列表转化回向量。
不带错误检查的 list->vector
可以定义如下。
(define list->vector
(lambda (ls)
(let ([s (make-vector (length ls))])
(do ([ls ls (cdr ls)] [i 0 (+ i 1)])
((null? ls) s)
(vector-set! s i (car ls))))))
(list->vector '()) => #()
(list->vector '(a b c)) => #(a b c)
(let ([v '#(1 2 3 4 5)])
(let ([ls (vector->list v)])
(list->vector (map * ls ls)))) => #(1 4 9 16 25)
过程: (vector-sort predicate vector)
返回: vector
中的元素基于 predicate
排序后,组成的向量
过程: (vector-sort! predicate vector)
返回: 未定义
库: (rnrs sorting), (rnrs)
redicate
应当是个接受两个实参的过程,并且,当它的第一个实参在排序后的列表中必须排在第二个实参之前时返回 #t
. 即,如果 predicate
应用于两个元素 x
和 y
, 且在输入列表中 x
排在 y
后面,它应该只在 x
应当于输出列表中排在 y
前面时返回真。如果满足这一限制, vector-sort
就会执行稳定的排序,即,根据 predicate
,两个元素只在必要时重新排序。 vector-sort!
执行破坏性的排序,且不一定执行稳定的排序。重复元素并不会被移除。 predicate
不应有任何副作用。
vector-sort
会调用 predicate
至多 nlogn 次,其中 n
是 vector
的长度。而 vector-sort!
可能会调用 predicate
至多 n2 次。 vector-sort!
的宽松限制使得实现可以使用快速排序算法,在某些情况下,可能会比采用 nlogn 这样较严格限制的算法更快。
(vector-sort < '#(3 4 2 1 2 5)) => #(1 2 2 3 4 5)
(vector-sort > '#(0.5 1/2)) => #(0.5 1/2)
(vector-sort > '#(1/2 0.5)) => #(1/2 0.5)
(let ([v (vector 3 4 2 1 2 5)])
(vector-sort! < v)
v) => #(1 2 2 3 4 5)
6.10. 字节向量
字节向量是由原始的二进制数据组成的向量。虽然名义上以精确的无符号 8 位整数序列组成,但一个字节向量也可以被解释为精确的有符号 8 位整数序列,精确的有符号或无符号 16 位,32 位,64 位,或任意精度的整数序列,IEEE 单精度或双精度数字序列,或上述类型的任意组合。
字节向量的长度是它储存的 8 位字节的数量,而字节向量的索引总是通过字节偏移量指定。所有数据元素都可以以任意字节偏移量对齐,不论底层硬件的对齐要求是什么,而且可以使用与硬件规定不同的,指定的字节序来表示(参见下文)。对于 16 位,32 位,和 64 位整数,以及原生格式(即,采用底层硬件的字节序,且以整数或浮点数字节大小的倍数为索引存储)的单精度和双精度浮点数,均提供了特殊的,通常也更高效的操作。
多字节数据值的字节序决定了它在内存中的布局。在大端序格式下,值的布局是,高有效位字节存在低位;而在小端序格式下,值的布局是,高有效位字节存在高位。若一个字节向量过程接受一个字节序实参,则此实参可以是符号 big
,表示大端序格式,或符号 little
,表示小端序格式。实现可以扩展这些过程,以接受其它字节序符号。通过过程 native-endianness
可以获得当前实现的原生字节序。
字节向量的字面形式以前缀 #vu8(
替代向量的前缀 #(
, 例如,#vu8(1 2 3)
. 以这种方式指定的字节向量的元素总是 8 位无符号精确整数,即,0 至 255(两端包含)之间的整数,可以使用这种数字的任意有效语法书写。和字符串一样,字节向量是自求值的,所以它们不需要被引用。
'#vu8(1 2 3) => #vu8(1 2 3)
#vu8(1 2 3) => #vu8(1 2 3)
#vu8(#x3f #x7f #xbf #xff) => #vu8(63 127 191 255)
syntax: (endianness symbol)
返回: symbol
库: (rnrs bytevectors), (rnrs)
symbol
必须是符号 little
, 符号 big
, 或可以被实现识别为字节序符号的其它符号。如果 symbol
不是符号,或不能被实现识别为一个字节序符号,则为语法违规。
(endianness little) => little
(endianness big) => big
(endianness "spam") => exception
过程: (native-endianness)
返回: 一个符号,命名了实现的原生字节序
库: (rnrs bytevectors), (rnrs)
返回值是符号 little
, big
, 或实现可以识别的其它字节序符号。它通常反映了底层硬件的字节序。
(symbol? (native-endianness)) => #t
过程: (make-bytevector n)
过程: (make-bytevector n fill)
返回: 长度为 n
的新字节向量
库: (rnrs bytevectors), (rnrs)
如果指定 fill
, 字节向量的每个元素都被初始化为 fill
; 否则,元素则是未定义的。 fill
的值必须是有符号或无符号 8 位值,即,在范围 -128 至 255(两端包含)之间的值。负的填充值被视为与其等价的补码。
(make-bytevector 0) => #vu8()
(make-bytevector 0 7) => #vu8()
(make-bytevector 5 7) => #vu8(7 7 7 7 7)
(make-bytevector 5 -7) => #vu8(249 249 249 249 249)
过程: (bytevector-length bytevector)
返回: 以 8 位字节计的字节向量长度
库: (rnrs bytevectors), (rnrs)
(bytevector-length #vu8()) => 0
(bytevector-length #vu8(1 2 3)) => 3
(bytevector-length (make-bytevector 300)) => 300
过程: (bytevector=? bytevector1 bytevector2)
返回: 如果关系成立,则为 #t
, 否则为 #f
.
库: (rnrs bytevectors), (rnrs)
当且仅当两个字节向量有相同的长度和内容时, bytevector=?
判定它们为相等。
(bytevector=? #vu8() #vu8()) => #t
(bytevector=? (make-bytevector 3 0) #vu8(0 0 0)) => #t
(bytevector=? (make-bytevector 5 0) #vu8(0 0 0)) => #f
(bytevector=? #vu8(1 127 128 255) #vu8(255 128 127 1)) => #f
过程: (bytevector-fill! bytevector fill)
返回: 未定义
库: (rnrs bytevectors), (rnrs)
fill
的值必须是有符号或无符号 8 位值,即,在范围 -128 至 255(两端包含)之间的值。负的填充值被视为与其等价的补码。
bytevector-fill!
以 fill
取代 bytevector
中的每个元素。
(let ([v (make-bytevector 6)])
(bytevector-fill! v 255)
v) => #vu8(255 255 255 255 255 255)
(let ([v (make-bytevector 6)])
(bytevector-fill! v -128)
v) => #vu8(128 128 128 128 128 128)
过程: (bytevector-copy bytevector)
返回: 一个新的字节向量,为 bytevector
的拷贝
库: (rnrs bytevectors), (rnrs)
bytevector-copy
创建一个新的字节向量,与 bytevector
的长度和内容相同。
(bytevector-copy #vu8(1 127 128 255)) => #vu8(1 127 128 255)
(let ([v #vu8(1 127 128 255)])
(eq? v (bytevector-copy v))) => #f
过程: (bytevector-copy! src src-start dst dst-start n)
返回: 未定义
库: (rnrs bytevectors), (rnrs)
src
和 dst
必须是字节向量。 src-start
, dst-start
, 和 n
必须是精确的非负整数。 src-start
和 n
的和一定不能超出 src
的长度,而 dst-start
和 n
的和一定不能超出 dst
的长度。
bytevector-copy!
以 src
中始于 src-start
的 n
个字节,覆盖 dst
中始于 dst-start
的 n
个字节。即使 dst
和 src
是同一个字节向量,且源和目标位置相互重叠,这个操作也能生效。即,在操作开始时,目标位置先被源字节向量中出现的字节填充。
(define v1 #vu8(31 63 95 127 159 191 223 255))
(define v2 (make-bytevector 10 0))
(bytevector-copy! v1 2 v2 1 4)
v2 => #vu8(0 95 127 159 191 0 0 0 0 0)
(bytevector-copy! v1 5 v2 7 3)
v2 => #vu8(0 95 127 159 191 0 0 191 223 255)
(bytevector-copy! v2 3 v2 0 6)
v2 => #vu8(159 191 0 0 191 223 0 191 223 255)
(bytevector-copy! v2 0 v2 1 9)
v2 => #vu8(159 159 191 0 0 191 223 0 191 223)
过程: (bytevector-u8-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的 8 位无符号字节
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且小于 bytevector
的长度。
返回值为精确的 8 位无符号整数,即,在范围 0 至 255(两端包含)之间的值。
(bytevector-u8-ref #vu8(1 127 128 255) 0) => 1
(bytevector-u8-ref #vu8(1 127 128 255) 2) => 128
(bytevector-u8-ref #vu8(1 127 128 255) 3) => 255
过程: (bytevector-s8-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的 8 位有符号字节
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且小于 bytevector
的长度。
返回值为精确的 8 位有符号整数,即,在范围 -128 至 127(两端包含)之间的值,是存储值的等价补码值。
(bytevector-s8-ref #vu8(1 127 128 255) 0) => 1
(bytevector-s8-ref #vu8(1 127 128 255) 1) => 127
(bytevector-s8-ref #vu8(1 127 128 255) 2) => -128
(bytevector-s8-ref #vu8(1 127 128 255) 3) => -1
过程: (bytevector-u8-set! bytevector n u8)
返回: 未定义
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且小于 bytevector
的长度。u8
必须是 8 位无符号值,即,在范围 0 至 255(两端包含)之间的值。
bytevector-u8-set!
把 bytevector
在索引 n
(基于 0)处的 8 位值修改为 u8
.
(let ([v (make-bytevector 5 -1)])
(bytevector-u8-set! v 2 128)
v) => #vu8(255 255 128 255 255)
过程: (bytevector-s8-set! bytevector n s8)
返回: 未定义
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且小于 bytevector
的长度。s8
必须是 8 位有符号值,即,在范围 -128 至 127(两端包含)之间的值。
bytevector-s8-set!
把 bytevector
在索引 n
(基于 0)处的 8 位值修改为 s8
的等价补码。
(let ([v (make-bytevector 4 0)])
(bytevector-s8-set! v 1 100)
(bytevector-s8-set! v 2 -100)
v) => #vu8(0 100 156 0)
过程: (bytevector->u8-list bytevector)
返回: bytevecotr
的 8 位无符号元素组成的列表
库: (rnrs bytevectors), (rnrs)
(bytevector->u8-list (make-bytevector 0)) => ()
(bytevector->u8-list #vu8(1 127 128 255)) => (1 127 128 255)
(let ([v #vu8(1 2 3 255)])
(apply * (bytevector->u8-list v))) => 1530
过程: (u8-list->bytevector list)
返回: list
中的元素组成的新字节向量
库: (rnrs bytevectors), (rnrs)
list
必须完全由精确的 8 位无符号整数组成,即,在范围 0 至 255(两端包含)之间的值。
(u8-list->bytevector '()) => #vu8()
(u8-list->bytevector '(1 127 128 255)) => #vu8(1 127 128 255)
(let ([v #vu8(1 2 3 4 5)])
(let ([ls (bytevector->u8-list v)])
(u8-list->bytevector (map * ls ls)))) => #vu8(1 4 9 16 25)
过程: (bytevector-u16-native-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的 16 位无符号整数
过程: (bytevector-s16-native-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的 16 位有符号整数
过程: (bytevector-u32-native-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的 32 位无符号整数
过程: (bytevector-s32-native-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的 32 位有符号整数
过程: (bytevector-u64-native-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的 64 位无符号整数
过程: (bytevector-s64-native-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的 64 位有符号整数
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数。它索引了值的起始字节,且必须是值所占用的字节数的倍数:16 位值是 2,32 位值是 4,64 位值是 8。 n
和值所占用的字节数之和一定不能超出 bytevector
的长度。假设为原生的字节序。
返回值是一个精确整数,处于值所占用字节数的适当区间内。有符号数是把存储值视为补码的等价值。
(define v #vu8(#x_12 #x34 #xfe #x56 #xdc #xba #x78 #x98))
如果原生字节序为 big:
(bytevector-u16-native-ref v 2) => #xfe56
(bytevector-s16-native-ref v 2) => #x-1aa
(bytevector-s16-native-ref v 6) => #x7898
(bytevector-u32-native-ref v 0) => #x_1234fe56
(bytevector-s32-native-ref v 0) => #x_1234fe56
(bytevector-s32-native-ref v 4) => #x-23458768
(bytevector-u64-native-ref v 0) => #x_1234fe56dcba7898
(bytevector-s64-native-ref v 0) => #x_1234fe56dcba7898
如果原生字节序为 little:
(bytevector-u16-native-ref v 2) => #x56fe
(bytevector-s16-native-ref v 2) => #x56fe
(bytevector-s16-native-ref v 6) => #x-6788
(bytevector-u32-native-ref v 0) => #x56fe3412
(bytevector-s32-native-ref v 0) => #x56fe3412
(bytevector-s32-native-ref v 4) => #x-67874524
(bytevector-u64-native-ref v 0) => #x9878badc56fe3412
(bytevector-s64-native-ref v 0) => #x-67874523a901cbee
过程: (bytevector-u16-native-set! bytevector n u16)
过程: (bytevector-s16-native-set! bytevector n s16)
过程: (bytevector-u32-native-set! bytevector n u32)
过程: (bytevector-s32-native-set! bytevector n s32)
过程: (bytevector-u64-native-set! bytevector n u64)
过程: (bytevector-s64-native-set! bytevector n s64)
返回: 未定义
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数。它索引了值的起始字节,且必须是值所占用的字节数的倍数:16 位值是 2,32 位值是 4,64 位值是 8。 n
和值所占用的字节数之和一定不能超出 bytevector
的长度。u16 必须是一个 16 位无符号值,即,在 0 至 216 - 1(两端包含)区间中的值;s16 必须是一个 16 位有符号值,即,在 -215 至 215 - 1(两端包含)区间中的值;u32 必须是一个 32 位无符号值,即,在 0 至 232 - 1(两端包含)区间中的值;s32 必须是一个 32 位有符号值,即,在 -231 至 231 - 1(两端包含)区间中的值;u64 必须是一个 64 位无符号值,即,在 0 至 264 - 1(两端包含)区间中的值;s64 必须是一个 64 位有符号值,即,在 -263 至 263 - 1(两端包含)区间中的值。假设为原生的字节序。
这些过程把给定值存储到 bytevector
起始于索引 n
(基于 0)处的 2, 4, 或 8 字节中。
(define v (make-bytevector 8 0))
(bytevector-u16-native-set! v 0 #xfe56)
(bytevector-s16-native-set! v 2 #x-1aa)
(bytevector-s16-native-set! v 4 #x7898)
如果原生字节序为 big
:
v => #vu8(#xfe #x56 #xfe #x56 #x78 #x98 #x00 #x00)
如果原生字节序为 little
:
v => #vu8(#x56 #xfe #x56 #xfe #x98 #x78 #x00 #x00)
(define v (make-bytevector 16 0))
(bytevector-u32-native-set! v 0 #x_1234fe56)
(bytevector-s32-native-set! v 4 #x_1234fe56)
(bytevector-s32-native-set! v 8 #x-23458768)
如果原生字节序为 big
:
v => #vu8(#x_12 #x34 #xfe #x56 #x_12 #x34 #xfe #x56
#xdc #xba #x78 #x98 #x00 #x00 #x00 #x00)
如果原生字节序为 little
:
v => #vu8(#x56 #xfe #x34 #x_12 #x56 #xfe #x34 #x_12
#x98 #x78 #xba #xdc #x00 #x00 #x00 #x00)
(define v (make-bytevector 24 0))
(bytevector-u64-native-set! v 0 #x_1234fe56dcba7898)
(bytevector-s64-native-set! v 8 #x_1234fe56dcba7898)
(bytevector-s64-native-set! v 16 #x-67874523a901cbee)
如果原生字节序为 big
:
v => #vu8(#x_12 #x34 #xfe #x56 #xdc #xba #x78 #x98
#x_12 #x34 #xfe #x56 #xdc #xba #x78 #x98
#x98 #x78 #xba #xdc #x56 #xfe #x34 #x_12)
如果原生字节序为 little
:
v => #vu8(#x98 #x78 #xba #xdc #x56 #xfe #x34 #x_12
#x98 #x78 #xba #xdc #x56 #xfe #x34 #x_12
#x_12 #x34 #xfe #x56 #xdc #xba #x78 #x98)
过程: (bytevector-u16-ref bytevector n eness)
返回: bytevector
的索引 n
(基于 0)处的 16 位无符号整数
过程: (bytevector-s16-ref bytevector n eness)
返回: bytevector
的索引 n
(基于 0)处的 16 位有符号整数
过程: (bytevector-u32-ref bytevector n eness)
返回: bytevector
的索引 n
(基于 0)处的 32 位无符号整数
过程: (bytevector-s32-ref bytevector n eness)
返回: bytevector
的索引 n
(基于 0)处的 32 位有符号整数
过程: (bytevector-u64-ref bytevector n eness)
返回: bytevector
的索引 n
(基于 0)处的 64 位无符号整数
过程: (bytevector-s64-ref bytevector n eness)
返回: bytevector
的索引 n
(基于 0)处的 64 位有符号整数
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且索引了值的起始字节。 n
和值所占用的字节数(16 位值是 2,32 位值是 4,64 位值是 8。)之和一定不能超出 bytevector
的长度。 n
不需要是值所占用的字节数的倍数。 eness
必须是命名字节序的有效字节序符号。
返回值是一个精确整数,处于值所占用字节数的适当区间内。有符号数是把存储值视为补码的等价值。
(define v #vu8(#x_12 #x34 #xfe #x56 #xdc #xba #x78 #x98 #x9a #x76))
(bytevector-u16-ref v 0 (endianness big)) => #x_1234
(bytevector-s16-ref v 1 (endianness big)) => #x34fe
(bytevector-s16-ref v 5 (endianness big)) => #x-4588
(bytevector-u32-ref v 2 'big) => #xfe56dcba
(bytevector-s32-ref v 3 'big) => #x56dcba78
(bytevector-s32-ref v 4 'big) => #x-23458768
(bytevector-u64-ref v 0 'big) => #x_1234fe56dcba7898
(bytevector-s64-ref v 1 'big) => #x34fe56dcba78989a
(bytevector-u16-ref v 0 (endianness little)) => #x3412
(bytevector-s16-ref v 1 (endianness little)) => #x-1cc
(bytevector-s16-ref v 5 (endianness little)) => #x78ba
(bytevector-u32-ref v 2 'little) => #xbadc56fe
(bytevector-s32-ref v 3 'little) => #x78badc56
(bytevector-s32-ref v 4 'little) => #x-67874524
(bytevector-u64-ref v 0 'little) => #x9878badc56fe3412
(bytevector-s64-ref v 1 'little) => #x-6567874523a901cc
过程: (bytevector-u16-set! bytevector n u16 eness)
过程: (bytevector-s16-set! bytevector n s16 eness)
过程: (bytevector-u32-set! bytevector n u32 eness)
过程: (bytevector-s32-set! bytevector n s32 eness)
过程: (bytevector-u64-set! bytevector n u64 eness)
过程: (bytevector-s64-set! bytevector n s64 eness)
返回: 未定义
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且索引了值的起始字节。 n
和值所占用的字节数之和一定不能超出 bytevector
的长度。 n
不必是值所占用的字节数的倍数。u16 必须是一个 16 位无符号值,即,在 0 至 216 - 1(两端包含)区间中的值;s16 必须是一个 16 位有符号值,即,在 -215 至 215 - 1(两端包含)区间中的值;u32 必须是一个 32 位无符号值,即,在 0 至 232 - 1(两端包含)区间中的值;s32 必须是一个 32 位有符号值,即,在 -231 至 231 - 1(两端包含)区间中的值;u64 必须是一个 64 位无符号值,即,在 0 至 264 - 1(两端包含)区间中的值;s64 必须是一个 64 位有符号值,即,在 -263 至 263 - 1(两端包含)区间中的值。eness 必须是命名字节序的有效字节序符号。
这些过程把给定值存储到 bytevector
起始于索引 n
(基于 0)处的 2, 4, 或 8 字节中。负值存储为与它们等价的补码。
(define v (make-bytevector 8 0))
(bytevector-u16-set! v 0 #xfe56 (endianness big))
(bytevector-s16-set! v 3 #x-1aa (endianness little))
(bytevector-s16-set! v 5 #x7898 (endianness big))
v => #vu8(#xfe #x56 #x0 #x56 #xfe #x78 #x98 #x0)
(define v (make-bytevector 16 0))
(bytevector-u32-set! v 0 #x_1234fe56 'little)
(bytevector-s32-set! v 6 #x_1234fe56 'big)
(bytevector-s32-set! v 11 #x-23458768 'little)
v => #vu8(#x56 #xfe #x34 #x_12 #x0 #x0
#x_12 #x34 #xfe #x56 #x0
#x98 #x78 #xba #xdc #x0)
(define v (make-bytevector 28 0))
(bytevector-u64-set! v 0 #x_1234fe56dcba7898 'little)
(bytevector-s64-set! v 10 #x_1234fe56dcba7898 'big)
(bytevector-s64-set! v 19 #x-67874523a901cbee 'big)
v => #vu8(#x98 #x78 #xba #xdc #x56 #xfe #x34 #x_12 #x0 #x0
#x_12 #x34 #xfe #x56 #xdc #xba #x78 #x98 #x0
#x98 #x78 #xba #xdc #x56 #xfe #x34 #x_12 #x0)
过程: (bytevector-uint-ref bytevector n eness size)
返回: bytevector
的索引 n
(基于 0)处的 size
位无符号整数
过程: (bytevector-sint-ref bytevector n eness size)
返回: bytevector
的索引 n
(基于 0)处的 size
位有符号整数
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且索引了值的起始字节。 size
必须是精确的正整数,且指定了值所占用的字节数。 n
和 size
之和一定不能超出 bytevector
的长度。 n
不需要是值所占用的字节数的倍数。 eness
必须是命名字节序的有效字节序符号。
返回值是一个精确整数,处于值所占用字节数的适当区间内。有符号数是把存储值视为补码的等价值。
(define v #vu8(#x_12 #x34 #xfe #x56 #xdc #xba #x78 #x98 #x9a #x76))
(bytevector-uint-ref v 0 'big 1) => #x_12
(bytevector-uint-ref v 0 'little 1) => #x_12
(bytevector-uint-ref v 1 'big 3) => #x34fe56
(bytevector-uint-ref v 2 'little 7) => #x9a9878badc56fe
(bytevector-sint-ref v 2 'big 1) => #x-02
(bytevector-sint-ref v 1 'little 6) => #x78badc56fe34
(bytevector-sint-ref v 2 'little 7) => #x-6567874523a902
(bytevector-sint-ref (make-bytevector 1000 -1) 0 'big 1000) => -1
过程: (bytevector-uint-set! bytevector n uint eness size)
过程: (bytevector-sint-set! bytevector n sint eness size)
返回: 未定义
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且索引了值的起始字节。 size
必须是精确的正整数,且指定了值所占用的字节数。 n
和 size
之和一定不能超出 bytevector
的长度。 n
不需要是值所占用的字节数的倍数。 uint
必须是在 0 至 28size - 1(两端包含)区间中的精确整数。sint
必须是在 -28size - 1 至 28size - 1(两端包含)区间中的精确整数。 eness
必须是命名字节序的有效字节序符号。
这些过程把给定值存储到 bytevector
起始于索引 n
(基于 0)处的 size
字节中。负值存储为与它们等价的补码。
(define v (make-bytevector 5 0))
(bytevector-uint-set! v 1 #x_123456 (endianness big) 3)
v => #vu8(0 #x_12 #x34 #x56 0)
(define v (make-bytevector 7 -1))
(bytevector-sint-set! v 1 #x-8000000000 (endianness little) 5)
v => #vu8(#xff 0 0 0 0 #x80 #xff)
过程: (bytevector->uint-list bytevector eness size)
返回: bytevector
的 size
字节无符号元素组成的一个新列表
过程: (bytevector->sint-list bytevector eness size)
返回: bytevector
的 size
字节有符号元素组成的一个新列表
库: (rnrs bytevectors), (rnrs)
eness
必须是一个命名字节序的有效字节序符号。 size
必须是精确的正整数,且指定了值所占用的字节数。它必须是一个均匀划分 bytevector
长度的值。
(bytevector->uint-list (make-bytevector 0) 'little 3) => ()
(let ([v #vu8(1 2 3 4 5 6)])
(bytevector->uint-list v 'big 3)) => (#x010203 #x040506)
(let ([v (make-bytevector 80 -1)])
(bytevector->sint-list v 'big 20)) => (-1 -1 -1 -1)
过程: (uint-list->bytevector list eness size)
过程: (sint-list->bytevector list eness size)
返回: list中的元素组成的新字节向量
库: (rnrs bytevectors), (rnrs)
eness
必须是命名字节序的有效字节序符号。 size
必须是精确的正整数,且指定了值所占用的字节数。对于 uint-list->bytevector
, list
必须完全由 size
字节的精确无符号整数组成,即,0 至 28size - 1(两端包含)区间中的值。对于 sint-list->bytevector
, list
必须完全由 size
字节的精确有符号整数组成,即, -28size - 1 至 28size - 1(两端包含)区间中的值。在结果的字节向量中,每个值占用 size
字节,因此字节向量的长度为 size
乘以 list
的长度。
(uint-list->bytevector '() 'big 25) => #vu8()
(sint-list->bytevector '(0 -1) 'big 3) => #vu8(0 0 0 #xff #xff #xff)
(define (f size)
(let ([ls (list (- (expt 2 (- (* 8 size) 1)))
(- (expt 2 (- (* 8 size) 1)) 1))])
(sint-list->bytevector ls 'little size)))
(f 6) => #vu8(#x00 #x00 #x00 #x00 #x00 #x80
#xff #xff #xff #xff #xff #x7f)
过程: (bytevector-ieee-single-native-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的单精度浮点值
过程: (bytevector-ieee-double-native-ref bytevector n)
返回: bytevector
的索引 n
(基于 0)处的双精度浮点值
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数。它索引了值的起始字节,且必须是值所占用的字节数的倍数:单精度是 4,双精度是 8. n
和值所占用的字节数之和一定不能超出 bytevector
的长度。假设为原生的字节序。
返回值是不精确的实数。下方的修改操作后面会有实例。
过程: (bytevector-ieee-single-native-set! bytevector n x)
过程: (bytevector-ieee-double-native-set! bytevector n x)
返回: 未定义
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数。它索引了值的起始字节,且必须是值所占用的字节数的倍数:单精度是 4,双精度是 8. n
和值所占用的字节数之和一定不能超出 bytevector
的长度。假设为原生的字节序。
这些过程把给定值作为 IEEE-754 单精度或双精度浮点值,存储到 bytevector
的索引 n
(基于 0)处。
(define v (make-bytevector 8 0))
(bytevector-ieee-single-native-set! v 0 .125)
(bytevector-ieee-single-native-set! v 4 -3/2)
(list
(bytevector-ieee-single-native-ref v 0)
(bytevector-ieee-single-native-ref v 4)) => (0.125 -1.5)
(bytevector-ieee-double-native-set! v 0 1e23)
(bytevector-ieee-double-native-ref v 0) => 1e23
过程: (bytevector-ieee-single-ref bytevector n eness)
返回: bytevector
的索引 n
(基于 0)处的单精度浮点值
过程: (bytevector-ieee-double-ref bytevector n eness)
返回: bytevector
的索引 n
(基于 0)处的双精度浮点值
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且索引了值的起始字节。 n
和值所占用的字节数(单精度是 4,双精度是 8)之和一定不能超出 bytevector
的长度。 n
不必是值所占用的字节数的倍数。 eness
必须是命名字节序的有效字节序符号。
返回值是不精确的实数。下方的修改操作后面会有实例。
过程: (bytevector-ieee-single-set! bytevector n x eness)
过程: (bytevector-ieee-double-set! bytevector n x eness)
返回: 未定义
库: (rnrs bytevectors), (rnrs)
n
必须是精确的非负整数,且索引了值的起始字节。 n
和值所占用的字节数(单精度是 4,双精度是 8)之和一定不能超出 bytevector
的长度。 n
不必是值所占用的字节数的倍数。 eness
必须是命名字节序的有效字节序符号。
这些过程把给定值作为 IEEE-754 单精度或双精度浮点值,存储到 bytevector
的索引 n
(基于 0)处。
(define v (make-bytevector 10 #xc7))
(bytevector-ieee-single-set! v 1 .125 'little)
(bytevector-ieee-single-set! v 6 -3/2 'big)
(list
(bytevector-ieee-single-ref v 1 'little)
(bytevector-ieee-single-ref v 6 'big)) => (0.125 -1.5)
v => #vu8(#xc7 #x0 #x0 #x0 #x3e #xc7 #xbf #xc0 #x0 #x0)
(bytevector-ieee-double-set! v 1 1e23 'big)
(bytevector-ieee-double-ref v 1 'big) => 1e23
6.11. 符号
在 Scheme 程序中,符号作为符号的名称有多种用途。字符串可以达成其中大多数目的,但符号的一个重要特性使得符号之间的比较要高效得多。这个特性是,两个相同名称的符号在 eq? 的意义上是相同的。原因是,Scheme 读取器(通过 get-datum 和 read 调用)和过程 string->symbol 把符号录入一个驻留符号表,在遇到相同的名字时,总是返回相同的符号。因此,逐字符的比较就不需要了,而两个字符串间的比较则需要。
两个符号可以快速比较等价性的性质,使得它们非常适合在程序中用作标识符,以支持标识符的快速比较。这个性质也使得符号在很多其它用途中十分有用。例如,符号可以用作过程间传递的消息,列表结构记录中的标签,或关联列表中存储的对象名称(参见 6.3 节中的 assq)。
符号的字面形式不带有双引号或其它括号字符。符号的打印形式中不允许出现括号,双引号,空格,以及大多数其它对 Scheme 读取器有着特殊意义的字符。这些字符和任意其它 Unicode 字符可以出现在使用语法形式 #\xn; 的符号的打印形式中,其中 n 由一个或多个十六进制数字组成,且表示一个有效的 Unicode 标量值。
458 页的符号语法,给出了符号句法的精确定义。
过程: (symbol=? symbol1 symbol2)
返回: 如果两个符号相同,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
符号也可以通过 eq?
比较,其通常比 symbol=?
更高效。
(symbol=? 'a 'a) => #t
(symbol=? 'a (string->symbol "a")) => #t
(symbol=? 'a 'b) => #f
过程: (string->symbol string)
返回: 一个名称为 string
的符号
库: (rnrs base), (rnrs)
string->symbol
把它创建的所有符号录入一个它与系统读取器共享的驻留符号表。如果一个名字等于 string
(基于谓词 string=?
)的符号已经存在于表中,则返回这个符号。否则,则创建一个名为 string
的新符号;且把这个符号加入表中,并返回。
在一个字符串用作 string->symbol
的实参后修改它的效果是未定义的。
(string->symbol "x") => x
(eq? (string->symbol "x") 'x) => #t
(eq? (string->symbol "X") 'x) => #f
(eq? (string->symbol "x")
(string->symbol "x")) => #t
(string->symbol "()") => \x_28;\x_29;
过程: (symbol->string symbol)
返回: 一个字符串,内容为符号的名称
库: (rnrs base), (rnrs)
symbol->string
返回的字符串应当被视为不可变的。如果使用 string-set!
或其它任何方式修改传入 string->symbol
的字符串,则会导致不可预测的行为。
(symbol->string 'xyz) => "xyz"
(symbol->string 'Hi) => "Hi"
(symbol->string (string->symbol "()")) => "()"
6.12. 布尔值
虽然所有 Scheme 对象在用于条件上下文中时都有一个真值,且除了 #f
,所有对象都被视为真,但 Scheme 也提供了专用的真值 #t
,用于当表达式的值只表达真,不表达其它任何内容时。
过程: (boolean=? boolean1 boolean2)
返回: 如果两个布尔值相同,则为 #t
, 否则为 #f
.
库: (rnrs base), (rnrs)
布尔值 #t
和 #f
也可以用 eq?
比较,通常比使用 boolean=?
更加高效。
(boolean=? #t #t) => #t
(boolean=? #t #f) => #f
(boolean=? #t (< 3 4)) => #t
6.13. 哈希表
哈希表表示任意 Scheme 值之间的关联的集合。它们本质上和关联列表服务于同一个目的,但在涉及大量关联时,通常要快得多。
过程: (make-eq-hashtable)
过程: (make-eq-hashtable size)
返回: 一个新的可变 eq hashtable
库: (rnrs hashtables), (rnrs)
如果指定了 size
, 它必须是一个非负的精确整数,指示哈希表初始化时大概应包含多少元素。哈希表根据需要增长,但当哈希表增长时,它通常必须重新哈希化所有已存在的元素。提供一个非 0 的 size
时,由于初始时填充了表,因而可以帮助限制必须进行的重新哈希的次数。
eq hashtable 使用 eq?
(指针相等)过程比较键,并且通常基于对象地址采用一个哈希函数。它的哈希和等价性函数适用于任意 Scheme 对象。
(define ht1 (make-eq-hashtable))
(define ht2 (make-eq-hashtable 32))
过程: (make-eqv-hashtable)
过程: (make-eqv-hashtable size)
返回: 一个新的可变 eqv hashtable
库: (rnrs hashtables), (rnrs)
如果指定了 size
, 它必须是一个非负的精确整数,指示哈希表初始化时大概应包含多少元素。哈希表根据需要增长,但当哈希表增长时,它通常必须重新哈希化所有已存在的元素。提供一个非 0 的 size
时,由于初始时填充了表,因而可以帮助限制必须进行的重新哈希的次数。
eqv hashtable 使用 eqv?
过程比较键,并且对于可用 eq?
识别的对象,通常基于对象地址采用一个哈希函数。它的哈希和等价性函数适用于任意 Scheme 对象。
过程: (make-hashtable hash equiv?)
过程: (make-hashtable hash equiv? size)
返回: 一个新的可变的哈希表
库: (rnrs hashtables), (rnrs)
hash
和 equiv?
必须是过程。如果指定了 size
, 它必须是一个非负的精确整数,指示哈希表初始化时大概应包含多少元素。哈希表根据需要增长,但当哈希表增长时,它通常必须重新哈希化所有已存在的元素。提供一个非 0 的 size
时,由于初始时填充了表,因而可以帮助限制必须进行的重新哈希的次数。
新的哈希表使用 hash
计算哈希值,使用 equiv?
比较键,两者都不应修改哈希表。 equiv?
应当比较两个键,并仅当两个键有区别时返回假。 hash
应该接受一个键作为实参,并返回一个非负的精确整数值,对于 equiv?
判定为相同的实参,则每次调用时返回的这个值都是相同的。只要哈希表只用于它们接受的键,则 hash
和 equiv?
过程就不需要接受任意输入,且只要键在其有关联值存储在表中时不被修改,则两个过程就都可以假定键是不可变的。哈希表操作可以在每次操作时调用 hash
和 equiv?
一次,完全不调用,或调用多次。
(define ht (make-hashtable string-hash string=?))
过程: (hashtable-mutable? hashtable)
返回: 如果 hashtable
是可变的,则为 #t
, 否则为 #f
.
库: (rnrs hashtables), (rnrs)
上述的任一个哈希表创建过程返回的哈希表都是可变的,但通过 hashtable-copy
创建的表可以是不可变的。不可变的哈希表不能通过任何下列过程修改: hashtable-set!
, hashtable-update!
, hashtable-delete!
, 或 hashtable-clear!
.
(hashtable-mutable? (make-eq-hashtable)) => #t
(hashtable-mutable? (hashtable-copy (make-eq-hashtable))) => #f
过程: (hashtable-hash-function hashtable)
返回: 与 hashtable
关联的哈希函数
过程: (hashtable-equivalence-function hashtable)
返回: 与 hashtable
关联的等价性函数
库: (rnrs hashtables), (rnrs)
对于 eq 和 eqv hashtable, hashtable-hash-function
返回 #f
.
(define ht (make-eq-hashtable))
(hashtable-hash-function ht) => #f
(eq? (hashtable-equivalence-function ht) eq?) => #t
(define ht (make-hashtable string-hash string=?))
(eq? (hashtable-hash-function ht) string-hash) => #t
(eq? (hashtable-equivalence-function ht) string=?) => #t
过程: (equal-hash obj)
过程: (string-hash string)
过程: (string-ci-hash string)
过程: (symbol-hash symbol)
返回: 一个精确的非负整数哈希值
库: (rnrs hashtables), (rnrs)
这些过程是适用于适当的 Scheme 谓词的哈希函数: equal?
对应 equal-hash
, string=?
对应 string-hash
, string-ci=?
对应 string-ci-hash
, 而 symbol=?
(或 eq?
) 对应 symbol-hash
. 由 equal-hash
, string-hash
, 和 string-ci-hash
返回的哈希值,通常依赖于当前结构和输入值的内容,因此,如果键在哈希表中有关联值时被修改,则这些过程并不适用。
过程: (hashtable-set! hashtable key obj)
返回: 未定义
库: (rnrs hashtables), (rnrs)
hashtable
必须是可变哈希表。 key
应该是适用于 hashtable
的哈希函数和等价性函数的键。 obj
可以是任意 Scheme 对象。
hashtable-set!
把 obj
与 hashtable
中的 key
相关联,取代已有的关联,如果存在的话。
(define ht (make-eq-hashtable))
(hashtable-set! ht 'a 73)
过程: (hashtable-ref hashtable key default)
返回: 参见下文
库: (rnrs hashtables), (rnrs)
key
应该是适用于 hashtable
的哈希函数和等价性函数的键。 default
可以是任意 Scheme 对象。
hashtable-ref
返回与 hashtable
中的 key
相关联的值。如果没有值与 hashtable
中的 key
相关联,则 hashtable-ref
返回 default
.
(define p1 (cons 'a 'b))
(define p2 (cons 'a 'b))
(define eqht (make-eq-hashtable))
(hashtable-set! eqht p1 73)
(hashtable-ref eqht p1 55) => 73
(hashtable-ref eqht p2 55) => 55
(define equalht (make-hashtable equal-hash equal?))
(hashtable-set! equalht p1 73)
(hashtable-ref equalht p1 55) => 73
(hashtable-ref equalht p2 55) => 73
过程: (hashtable-contains? hashtable key)
返回: 如果 key
在 hashtable
中存在关联值,则为 #t
, 否则为 #f
.
库: (rnrs hashtables), (rnrs)
key
应该是适用于 hashtable
的哈希函数和等价性函数的键。
(define ht (make-eq-hashtable))
(define p1 (cons 'a 'b))
(define p2 (cons 'a 'b))
(hashtable-set! ht p1 73)
(hashtable-contains? ht p1) => #t
(hashtable-contains? ht p2) => #f
过程: (hashtable-update! hashtable key procedure default)
返回: 未定义
库: (rnrs hashtables), (rnrs)
hashtable
必须是可变的哈希表。 key
应该是适用于 hashtable
的哈希函数和等价性函数的键。 default
可以是任意 Scheme 对象。 procedure
应当接受一个实参,返回一个值,且不应修改 hashtable
.
hashtable-update!
把 procedure
应用于 hashtable
中与 key
相关联的值上,如果 hashtable
中没有值与 key
相关联,则应用于 default
上。如果 procedure
返回一个值, hashtable-update!
则把 key
与 procedure
返回的值相关联,取代旧的关联——如果存在的话。
不验证传入的实参是否为适当类型的 hashtable-update!
版本,可以定义如下。
(define hashtable-update!
(lambda (ht key proc value)
(hashtable-set! ht key
(proc (hashtable-ref ht key value)))))
不过,一个实现可以通过避免多次哈希计算和哈希表查找,把 hashtable-update!
实现得更高效。
(define ht (make-eq-hashtable))
(hashtable-update! ht 'a
(lambda (x) (* x 2))
55)
(hashtable-ref ht 'a 0) => 110
(hashtable-update! ht 'a
(lambda (x) (* x 2))
0)
(hashtable-ref ht 'a 0) => 220
过程: (hashtable-delete! hashtable key)
返回: 未定义
库: (rnrs hashtables), (rnrs)
hashtable
必须是可变哈希表。 key
应该是适用于 hashtable
的哈希函数和等价性函数的键。
hashtable-delete!
会移除 key
在 hashtable
中的所有关联。
(define ht (make-eq-hashtable))
(define p1 (cons 'a 'b))
(define p2 (cons 'a 'b))
(hashtable-set! ht p1 73)
(hashtable-contains? ht p1) => #t
(hashtable-delete! ht p1)
(hashtable-contains? ht p1) => #f
(hashtable-contains? ht p2) => #f
(hashtable-delete! ht p2)
过程: (hashtable-size hashtable)
返回: hashtable
中的条目数量
库: (rnrs hashtables), (rnrs)
(define ht (make-eq-hashtable))
(define p1 (cons 'a 'b))
(define p2 (cons 'a 'b))
(hashtable-size ht) => 0
(hashtable-set! ht p1 73)
(hashtable-size ht) => 1
(hashtable-delete! ht p1)
(hashtable-size ht) => 0
过程: (hashtable-copy hashtable)
过程: (hashtable-copy hashtable mutable?)
返回: 一个新的哈希表,包含与 hashtable
相同的条目
库: (rnrs hashtables), (rnrs)
如果传入 mutable?
,且其值不为假,则拷贝是可变的;否则,拷贝是不可变的。
(define ht (make-eq-hashtable))
(define p1 (cons 'a 'b))
(hashtable-set! ht p1 "c")
(define ht-copy (hashtable-copy ht))
(hashtable-mutable? ht-copy) => #f
(hashtable-delete! ht p1)
(hashtable-ref ht p1 #f) => #f
(hashtable-delete! ht-copy p1) => exception: not mutable
(hashtable-ref ht-copy p1 #f) => "c"
过程: (hashtable-clear! hashtable)
过程: (hashtable-clear! hashtable size)
返回: 未定义
库: (rnrs hashtables), (rnrs)
hashtable
必须是可变的哈希表。如果指定 size
,其必须是一个非负的精确整数。
hashtable-clear!
从 hashtable
中移除所有条目。如果指定 size
,则哈希表被重置为指定的大小,如同被一个带有实参 size
的哈希表创建操作重新创建一样。
(define ht (make-eq-hashtable))
(define p1 (cons 'a 'b))
(define p2 (cons 'a 'b))
(hashtable-set! ht p1 "first")
(hashtable-set! ht p2 "second")
(hashtable-size ht) => 2
(hashtable-clear! ht)
(hashtable-size ht) => 0
(hashtable-ref ht p1 #f) => #f
过程: (hashtable-keys hashtable)
返回: hashtable
中的键组成的向量
库: (rnrs hashtables), (rnrs)
键可以以任意顺序出现在返回的向量中。
(define ht (make-eq-hashtable))
(define p1 (cons 'a 'b))
(define p2 (cons 'a 'b))
(hashtable-set! ht p1 "one")
(hashtable-set! ht p2 "two")
(hashtable-set! ht 'q "three")
(hashtable-keys ht) => #((a . b) q (a . b))
过程: (hashtable-entries hashtable)
返回: 两个向量:一个由键组成,另一个由值组成
库: (rnrs hashtables), (rnrs)
hashtable-entries
返回两个值。第一个是由 hashtable
中的键组成的向量,第二个是由相应的值组成的向量。键和值可以是任意顺序,但对于相应的键和值,顺序是一致的。
(define ht (make-eq-hashtable))
(define p1 (cons 'a 'b))
(define p2 (cons 'a 'b))
(hashtable-set! ht p1 "one")
(hashtable-set! ht p2 "two")
(hashtable-set! ht 'q "three")
(hashtable-entries ht) => #((a . b) q (a . b))
#("two" "three" "one")
6.14. 枚举
枚举是符号的有序集合,通常用于命名和操控选项,正如创建文件时,可以指定缓冲区模式和文件选项。
syntax: (define-enumeration name (symbol ...) constructor)
库: (rnrs enums), (rnrs)
define-enumeration
语法形式是一种定义,可以出现在任何其它定义可以出现的地方。
define-enumeration
语法形式创建了一个新的枚举集合,以指定的符号,指定的顺序形成了此枚举的全集。它定义了一个新的由 name
命名的语法形式,可以用于检验一个符号是否在这个全集中。如果 x 在全集中,则(name x)
求值为 x. 如果 x 不在全集中,则为语法违规。
define-enumeration
同时定义了一个新的由 constructor
命名的语法形式,可以用于创建此枚举类型的子集。如果 x … 均在全集中,(constructor x …)
求值为一个包含 x … 的枚举集合。否则,便是语法违规。相同的符号可以在 x … 中出现多次,但在结果集合中符号只会出现一次。
(define-enumeration weather-element
(hot warm cold sunny rainy snowy windy)
weather)
(weather-element hot) => hot
(weather-element fun) => syntax violation
(weather hot sunny windy) => #<enum-set>
(enum-set->list (weather rainy cold rainy)) => (cold rainy)
过程: (make-enumeration symbol-list)
返回: 一个枚举集合
库: (rnrs enums), (rnrs)
这个过程创建一个新的枚举类型,其全集由 symbol-list
中的元素组成, symbol-list
必须是符号组成的列表,依它们第一次出现在列表中的次序排序。它以枚举集合的形式返回这个新枚举类型的全集。
(define positions (make-enumeration '(top bottom above top beside)))
(enum-set->list positions) => (top bottom above beside)
过程: (enum-set-constructor enum-set)
返回: 一个 enumeration-set
构造过程
库: (rnrs enums), (rnrs)
这个过程返回一个过程 p,可以用于创建 enum-set
全集的子集。p 必须被传入一个符号列表,且列表的每个元素必须是 enum-set
全集中的元素。p 返回的枚举集合包含且仅包含传入的列表中的全部符号。p 返回的值可能包含 enum-set
中没有的元素,如果 enum-set
的全集中包含这些元素。
(define e1 (make-enumeration '(one two three four)))
(define p1 (enum-set-constructor e1))
(define e2 (p1 '(one three)))
(enum-set->list e2) => (one three)
(define p2 (enum-set-constructor e2))
(define e3 (p2 '(one two four)))
(enum-set->list e3) => (one two four)
过程: (enum-set-universe enum-set)
返回: enum-set
的全集,作为一个枚举集合
库: (rnrs enums), (rnrs)
(define e1 (make-enumeration '(a b c a b c d)))
(enum-set->list (enum-set-universe e1)) => (a b c d)
(define e2 ((enum-set-constructor e1) '(c)))
(enum-set->list (enum-set-universe e2)) => (a b c d)
过程: (enum-set->list enum-set)
返回: enum-set
中的元素组成的列表
库: (rnrs enums), (rnrs)
结果列表中符号出现的顺序,与 enum-set
的枚举类型创建时给出的顺序一致。
(define e1 (make-enumeration '(a b c a b c d)))
(enum-set->list e1) => (a b c d)
(define e2 ((enum-set-constructor e1) '(d c a b)))
(enum-set->list e2) => (a b c d)
过程: (enum-set-subset? enum-set1 enum-set2)
返回: 如果 enum-set1
是 enum-set2
的子集,则为 #t
, 否则为 #f
.
库: (rnrs enums), (rnrs)
当且仅当 enum-set1
的全集是 enum-set2
的全集的子集,且 enum-set1
中的每个元素都是 enum-set2
中的元素时,枚举集合 enum-set1
是枚举集合 enum-set2
的子集。
(define e1 (make-enumeration '(a b c)))
(define e2 (make-enumeration '(a b c d e)))
(enum-set-subset? e1 e2) => #t
(enum-set-subset? e2 e1) => #f
(define e3 ((enum-set-constructor e2) '(a c)))
(enum-set-subset? e3 e1) => #f
(enum-set-subset? e3 e2) => #t
过程: (enum-set=? enum-set1 enum-set2)
返回: 如果 enum-set1
和 enum-set2
相等,则为 #t
, 否则为 #f
.
库: (rnrs enums), (rnrs)
如果两个枚举集合 enum-set1
和 enum-set2
互为子集,则它们相等。
(define e1 (make-enumeration '(a b c d)))
(define e2 (make-enumeration '(b d c a)))
(enum-set=? e1 e2) => #t
(define e3 ((enum-set-constructor e1) '(a c)))
(define e4 ((enum-set-constructor e2) '(a c)))
(enum-set=? e3 e4) => #t
(enum-set=? e3 e2) => #f
enum-set=?
可以基于 enum-set-subset?
定义如下。
(define enum-set=?
(lambda (e1 e2)
(and (enum-set-subset? e1 e2) (enum-set-subset? e2 e1))))
过程: (enum-set-member? symbol enum-set)
返回: 如果 symbol
是 enum-set
中的元素,则为 #t
, 否则为 #f
.
库: (rnrs enums), (rnrs)
(define e1 (make-enumeration '(a b c d e)))
(define e2 ((enum-set-constructor e1) '(d b)))
(enum-set-member? 'c e1) => #t
(enum-set-member? 'c e2) => #f
过程: (enum-set-union enum-set1 enum-set2)
返回: enum-set1
和 enum-set2
的并集
过程: (enum-set-intersection enum-set1 enum-set2)
返回: enum-set1
和 enum-set2
的交集
过程: (enum-set-difference enum-set1 enum-set2)
返回: enum-set1
和 enum-set2
的差集
库: (rnrs enums), (rnrs)
enum-set1
和 enum-set2
必须为相同的枚举类型。每个过程返回一个新的枚举集合,表示两个集合的并集,交集,或差集。
(define e1 (make-enumeration '(a b c d)))
(define e2 ((enum-set-constructor e1) '(a c)))
(define e3 ((enum-set-constructor e1) '(b c)))
(enum-set->list (enum-set-union e2 e3)) => (a b c)
(enum-set->list (enum-set-intersection e2 e3)) => (c)
(enum-set->list (enum-set-difference e2 e3)) => (a)
(enum-set->list (enum-set-difference e3 e2)) => (b)
(define e4 (make-enumeration '(b d c a)))
(enum-set-union e1 e4) => exception: different enumeration types
过程: (enum-set-complement enum-set)
返回: enum-set
相对于它的全集的补集
库: (rnrs enums), (rnrs)
(define e1 (make-enumeration '(a b c d)))
(enum-set->list (enum-set-complement e1)) => ()
(define e2 ((enum-set-constructor e1) '(a c)))
(enum-set->list (enum-set-complement e2)) => (b d)
过程: (enum-set-projection enum-set1 enum-set2)
返回: enum-set1
到 enum-set2
的全集的投影
库: (rnrs enums), (rnrs)
enum-set1
中任何不在 enum-set2
的全集中的元素均被丢弃。结果与 enum-set2
为相同的枚举类型。
(define e1 (make-enumeration '(a b c d)))
(define e2 (make-enumeration '(a b c d e f g)))
(define e3 ((enum-set-constructor e1) '(a d)))
(define e4 ((enum-set-constructor e2) '(a c e g)))
(enum-set->list (enum-set-projection e4 e3)) => (a c)
(enum-set->list
(enum-set-union e3
(enum-set-projection e4 e3))) => (a c d)
过程: (enum-set-indexer enum-set)
返回: 一个过程,返回 enum-set
全集中的符号的索引
库: (rnrs enums), (rnrs)
enum-set-indexer
返回一个过程 p,当应用于 enum-set
全集中的一个符号时,返回形成此全集的有序符号集合中,此符号的索引(基于 0)。如果应用于一个不在此全集中的符号上,p 返回 #f
.
(define e1 (make-enumeration '(a b c d)))
(define e2 ((enum-set-constructor e1) '(a d)))
(define p (enum-set-indexer e2))
(list (p 'a) (p 'c) (p 'e)) => (0 2 #f)
R. Kent Dybvig / The Scheme Programming Language, Fourth Edition Copyright © 2009 The MIT Press. Electronically reproduced by permission. Illustrations © 2009 Jean-Pierre Hébert ISBN 978-0-262-51298-5 / LOC QA76.73.S34D93 to order this book / about this book