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 表达式中, unquoteunquote-splicing 衍生形式被求值,而其它一切内容都是引用的,即,保留为未求值的。每个 unquote 衍生形式的值会替换掉 unquote 形式,并插入到输出中,而每个 unquote-splicing 衍生形式的值会被拼接进外围的列表或向量结构中。 unquoteunquote-splicing 只在 quasiquote 表达式中有效。

quasiquote 表达式可以嵌套,每个 quasiquote 引入一层新的引用,而每个 unquoteunquote-splicing 解除一层引用。如果一个表达式嵌套在 n 层 quasiquote 表达式中,则它必须通过 n 层 unquoteunquote-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 个子形式的 unquoteunquote-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)

unquoteunquote-splicingquasiquote 的辅助关键字。除了在被识别为辅助关键字的上下文中,在其它地方引用这些标识符是语法违规的。

6.2. 通用的等价性和类型谓词

本节介绍了基本的 Scheme 谓词(返回布尔值 #t 或 #f 的过程),用于判定对象的类型或两个对象的等价性。先讨论等价性谓词 eq?, eqv?, 和 equal?, 接下来是类型谓词。

过程: (eq? obj1 obj2)

返回: 如果 obj1obj2 完全相同,则为 #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)

返回: 如果 obj1obj2 相等,则为 #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.03.0+0.0i 在数值上被认为是相等的,但如果 -0.00.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)

返回: 如果 obj1obj2 具有相同的结构和内容,则为 #t, 否则为 #f.

库: (rnrs base), (rnrs)

如果两个对象基于 eqv? 判定是相等的,则它们是相等的,如 string=? 的字符串,bytevector=? 的字节向量,carscdrs 都相等的点对,或相同长度,且对应元素都相等的向量。

“当且仅当它的所有实参展开(可能为无穷的)为常规的树,且作为有序树均相等时”,要求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 字段可以是除()以外的任何对象。

点对可用于构造二叉树。树结构中的每个点对是二叉树中的一个内部节点;它的 carcdr 是节点的子节点。

完全列表打印为圆括号中由空白隔开的对象序列。匹配的方括号( [ ] )可用于替代圆括号。例如, (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! 破坏性地更改点对的 carcdr 字段,可以创造循环列表或环状图。这类列表不是完全列表。

接受一个列表实参的过程,只在下列情况下需要检查列表是否是不完全的:它们对列表的遍历足够深,(a) 尝试操作一个非列表的尾部,或 (b) 基于环进行无限循环。例如,如果 member 找到了要找的元素,则它不需要检查列表是否是不完全的,而 list-ref 永远不需要检测列表是否为环,因为它的递归已经通过实参 index 限定了界限。

过程: (cons obj1 obj2)

返回: 一个新的点对,它的 carcdrobj1obj2

库: (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)

这些过程以至多四个 carscdrs 的组合定义。c 和 r 之间的 a 和 d 代表了对 carcdr 的调用,顺序为从右到左。例如,应用于一个点对的过程 cadr,返回点对的 cdrcar,其等价于 (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, 比较 objlist 中的元素。如果找到了等于 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))])))

memvmember 的定义类似,分别以 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.

rempprocedure 应用于列表中的每个元素,并返回一个列表,其中只包含使 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.

filterprocedure 应用于列表中的每个元素,并返回一个列表,其中只包含使 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.

partitionprocedure 应用于列表中的每个元素,并返回两个值:一个只包含使 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 返回的值也可以通过分别调用 filterremp 得到,但这样需要对列表中的每个元素调用两次 procedure.

过程: (find procedure list)

返回: list 中使 procedure 返回真值的第一个元素,或 #f

库: (rnrs lists), (rnrs)

procedure 应该接受一个参数,并返回一个单个值。它不应修改 list.

find 依序遍历实参 list, 轮流对每个元素应用 procedure. 如果 procedure 对给定元素返回一个真值,则 find 返回此元素,并且不再对其它元素应用 procedure. 如果 procedurelist 中的每个元素都返回 #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)

返回: alistcar 字段等于 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))])))

assvassoc 的定义类似,分别以 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)

返回: alistcar 字段值使 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 以一种紧凑的形式表示数字,使用能够保持属性所必要的最少数位——在读取时,打印出的数字与原数字是一样的。

本节的剩余部分介绍了数字操作的“通用算术”过程。之后的两节介绍了专用于 fixnumsflonums 的操作,此两者分别表示精确的固定精度整数值和不精确的实数。

本节中的过程所接受的数字实参类型,由参数名暗示: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 ...)

返回: num1num2 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)

返回: int1int2 的整数商

过程: (remainder int1 int2)

返回: int1int2 的整数余数

过程: (modulo int1 int2)

返回: int1int2 的整数模

库: (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 是无穷数或 NaNtruncate 返回 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 是无穷数或 NaNfloor 返回 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 是无穷数或 NaNceiling 返回 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 是无穷数或 NaNround 返回 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)). 对于实数输入, absmagnitude (参见 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)

返回: num1num2 次幂

库: (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)

这些是 inexactexact 的别名,以和第 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 是整数,则 numeratorrat .

(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)

返回: num1num2 为底的对数

库: (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)

返回: 如果 exint1exint2 位为 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 取代 exint1exint2

库: (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)

exint2exint3 必须是非负的,且 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)

exint2exint3 必须非负,且 exint2 必须不大于 exint3 . 这个过程返回 exint1exint2 (包含)至 exint3 (不包含)之间的 n 位被 exint4 中由低到高的 n 位取代后的数。 exint1exint4 被作为补码形式处理,即使它们在内部并非如此表示。

(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 . 这个过程返回把 exint1exint2 (包含)位至 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)

exint2exint3 必须非负,且 exint2 必须不大于 exint3 . 这个过程返回 exint1exint2 (包含)位至 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-fixnumgreatest-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. 如果 fl1fl2 都是 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->flonuminexact 的一个受限变体。当输入是一个精确实数时, real->flonuminexact 的一个受限变体;当输入是一个不精确的非 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 , 则字符串以 nchar 填充,否则,字符串包含的字符则是未定义的。

(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)

返回: stringstart (包含)至 end (不包含)之间部分的拷贝

库: (rnrs base), (rnrs)

startend 必须是精确的非负整数; 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=? 判定),则返回 stringstring 的拷贝。否则,结果字符串则是新分配的。 string-foldcase 不使用土耳其语的特殊映射。

string-titlecasestring 中每个单词的第一个字符转化为对应的首字母大写字符,并把其它所有字符转化为对应的小写字符。单词分界依 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=? 判定),则返回 stringstring 的拷贝。否则,结果字符串则是新分配的。

(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 应用于两个元素 xy , 且在输入列表中 x 排在 y 后面,它应该只在 x 应当于输出列表中排在 y 前面时返回真。如果满足这一限制, vector-sort 就会执行稳定的排序,即,根据 predicate ,两个元素只在必要时重新排序。 vector-sort! 执行破坏性的排序,且不一定执行稳定的排序。重复元素并不会被移除。 predicate 不应有任何副作用。

vector-sort 会调用 predicate 至多 nlogn 次,其中 nvector 的长度。而 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)

srcdst 必须是字节向量。 src-start , dst-start , 和 n 必须是精确的非负整数。 src-startn 的和一定不能超出 src 的长度,而 dst-startn 的和一定不能超出 dst 的长度。

bytevector-copy!src 中始于 src-startn 个字节,覆盖 dst 中始于 dst-startn 个字节。即使 dstsrc 是同一个字节向量,且源和目标位置相互重叠,这个操作也能生效。即,在操作开始时,目标位置先被源字节向量中出现的字节填充。

(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 必须是精确的正整数,且指定了值所占用的字节数。 nsize 之和一定不能超出 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 必须是精确的正整数,且指定了值所占用的字节数。 nsize 之和一定不能超出 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)

返回: bytevectorsize 字节无符号元素组成的一个新列表

过程: (bytevector->sint-list bytevector eness size)

返回: bytevectorsize 字节有符号元素组成的一个新列表

库: (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)

hashequiv? 必须是过程。如果指定了 size , 它必须是一个非负的精确整数,指示哈希表初始化时大概应包含多少元素。哈希表根据需要增长,但当哈希表增长时,它通常必须重新哈希化所有已存在的元素。提供一个非 0 的 size 时,由于初始时填充了表,因而可以帮助限制必须进行的重新哈希的次数。

新的哈希表使用 hash 计算哈希值,使用 equiv? 比较键,两者都不应修改哈希表。 equiv? 应当比较两个键,并仅当两个键有区别时返回假。 hash 应该接受一个键作为实参,并返回一个非负的精确整数值,对于 equiv? 判定为相同的实参,则每次调用时返回的这个值都是相同的。只要哈希表只用于它们接受的键,则 hashequiv? 过程就不需要接受任意输入,且只要键在其有关联值存储在表中时不被修改,则两个过程就都可以假定键是不可变的。哈希表操作可以在每次操作时调用 hashequiv? 一次,完全不调用,或调用多次。

(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!objhashtable 中的 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)

返回: 如果 keyhashtable 中存在关联值,则为 #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! 则把 keyprocedure 返回的值相关联,取代旧的关联——如果存在的话。

不验证传入的实参是否为适当类型的 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! 会移除 keyhashtable 中的所有关联。

(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 &#x2026;) 求值为一个包含 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-set1enum-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-set1enum-set2 相等,则为 #t, 否则为 #f.

库: (rnrs enums), (rnrs)

如果两个枚举集合 enum-set1enum-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)

返回: 如果 symbolenum-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-set1enum-set2 的并集

过程: (enum-set-intersection enum-set1 enum-set2)

返回: enum-set1enum-set2 的交集

过程: (enum-set-difference enum-set1 enum-set2)

返回: enum-set1enum-set2 的差集

库: (rnrs enums), (rnrs)

enum-set1enum-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-set1enum-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

http://www.scheme.com

results matching ""

    No results matching ""