The Scheme Programming Language 读书笔记,chapter 6上半部.
Chapter 6. Operations on Objects
本章介绍对象之上的操作,包括连对,数字,单字符,字符串,矢量,字节矢量,符号,布尔值,哈希表,以及枚举类型。
Section 6.1. Constants and Quotation
常量通过自身就能得出值,下面是例子:
3.2
#f
#\c
"hi"
#vu8(3 4 5)
(quote obj)
和'obj
等效,把obj当作数据而不是代码。任何Scheme对象都可以quote,但是对于自求值的常量来说,quote并不是必须的,因为它们不会被误以为代码。
常量和quote返回的都是immutable的值,不能通过set-car!,seting-set!等来修改。
除了quote之外,还有quasiquote,其等同于``obj。两者的区别是quasiquote可以允许部分内容unquote,通过
,obj可以取消quasiquote。
,@obj等同于
(unquote-splicing obj)`。
quasiquote也可以嵌套, unquote的时候也要逐级解嵌套。
Section 6.2. Generic Equivalence and Type Predicates
如何判定两个对象相等?有下列规则
- 不同类型的对象不相等
- 同类型对象,但是内容或者值不同,则不相等
- #t和#t相等,#f和#f相等,#t和#f不等
- 空表
()
自相等 - 两符号名字相同则相等,通过
string=?
判定 - 常量的对子,串列,字串,或者字节串列自相等。通过cons,vector,string,make-bytevector创建的如上内容也自相等。但是,通过不同cons,vector,string,make-bytevector调用创建的具有相同内容的上述对象不相等。
- 两个执行诀如果表现不一致则不相等。通过lambda的调用创建的同一个执行诀自相等。同一个lambda表达式在不同时候创建的执行诀可能不相等。
eq?不能可靠得比较数字和字符。精确数和模糊数不相等,但是两个精确数、两个模糊数,两个字符,即便拥有相同的值,也可以不相等。
常量对象不可改,程序不应当通过vector-set!, set-car!或者其他涉及结构修改的操作去修改常量对象。
eq?多用于名号和对象指针的对比(即指向对子,串列,记录实例的指针)。
(eq? 9/2 9/2)
unspecified
(eq? 3.4 (+ 3.0 .4))
unspecified
(let ([x (* 12345678987654321 2)])
(eq? x x))
unspecified
(eq? #\a #\a)
unspecified
(let ([x (string-ref "hi" 0)])
(eq? x x))
unspecified
(eq? '(a) '(a))
unspecified
(eq? "abc" "abc")
unspecified
(eq? '#vu8(1) '#vu8(1))
unspecified
(eq? '#(a) '#(a))
unspecified
(eq? (lambda (x) x) (lambda (y) y))
unspecified
eqv?和eq?类似,但是它
- 如果两个字符通过char=?调用返回#t,那么eqv?返回#t
- 如果两个数字通过=调用返回#t,并且不能通过eq?或者eqv?以外的方式发现异同,那么eqv?返回#t
(eqv? -0.0 +0.0)
返回#f,即便(= -0.0 +0.0)
返回#t,因为系统是区分-0.0和+0.0的。因为(/ 1.0 -0.0)
需要返回-inf,而(/ 1.0 +0.0)
需要返回+inf。- 另一个例子
(= 3.0+0.0i 3.0)
返回#t,而(eqv? 3.0+0.0i 3.0)
返回#f。 (eqv? +nan.0 (/ 0.0 0.0))
的结果是unspecified
(eqv? '(a) '(a))
unspecified
(eqv? "abc" "abc")
unspecified
(eqv? '#vu8(1) '#vu8(1))
unspecified
(eqv? '#(a) '#(a))
unspecified
(let ([f (lambda () (lambda (x) x))])
(eqv? (f) (f)))
unspecified
(eqv? (lambda (x) x) (lambda (y) y))
unspecified
equal?只有在两对象具有完全相同的结构以及内容的时候才返回#t,其他情况下返回#f。equal?在eqv?的基础上采用了string=?,bytevector=?等额外判断。
(let ([f (lambda () (lambda (x) x))])
(equal? (f) (f)))
unspecified
(equal? (lambda (x) x) (lambda (y) y))
unspecifie
判定诀(boolean? obj)
等同于(lambda (x) (or (eq? x #t) (eq? x #f)))
。
判定诀(null? obj)
等同于(lambda (x) (eq? x '()))
。
判定诀(pair? obj)
用来判断是否是连对。
数字相关的判定诀:
(number? obj)
(complex? obj)
(real? obj)
(rational? obj)
(integer? obj)
real?, rational?, 以及integer?无法将复数判定为#t,即便此复数的复部为模糊数零。real-valued?, rational-valued?, integer-valued?等则没有这个问题。
判定诀(char? obj)
给出obj是否为字符。
判定诀(string? obj)
给出obj是否为字串。
判定诀(vector? obj)
给出obj是否为串列。
判定诀(symbol? obj)
给出obj是否为名号。
判定诀(procedure? obj)
给出obj是否为执行诀。
判定诀(bytevector? obj)
给出obj是否为字节串列。
判定诀(hashtable? obj)
给出obj是否为哈希表。
Section 6.3. Lists and Pairs
对子也要cons cell,是组成连对的基本单元。正当连对的最后一个对子的cdr操作应该返回空对,而不当连对的最后一个对子的cdr操作返回空对以外的对象。
可以通过set-car!或者set-cdr!来构建一个循环连对。这样的连对是不当的。
正当与否,一个连对只要其操作不走到最后,是不会发现这个连对是否正当。
下面的执行诀大家已经很熟悉了
-
(cons obj1 obj2)
-
(car pair)
-
(cdr pair)
-
(set-car! pair obj)
-
(set-cdr! pair obj)
-
(caar pair)
-
(cddr pair)
-
(list obj ...)
执行诀(cons* obj ... final-obj)
,cons的扩展版,支持串连多个对子,如果最后一个对象必须是一个连对,结果就是不当连对。
执行诀(list? obj)
判断obj是否是个正当连对。
执行诀(length list)
返回对子个数。
执行诀(list-ref list n)
返回从零开始的第n个元素。
执行诀(list-tail list n)
返回第n次cdr的结果。返回的是引用。
执行诀(append)
返回一个空对。
执行诀(append list ... obj)
将每个连对中的对子重新组合形成一个新的连对。最后的obj如果是一个连对,那么结果是正当的,如果不是,结果是不当的。
执行诀(reverse list)
反转连对中的对子。好像只能用在正当列表上。
几个查找相关的执行诀:
(memq obj list)
,使用eq?在list中查找obj(memv obj list)
,使用eqv?在list中查找obj(member obj list)
,使用equal?在list中查找obj
找到后会返回list中从obj中开始的部分。
执行诀(memp procedure list)
,和其他mem执行诀差不多,不过查找的依据是procedure的执行结果。
从连对中删除所有目标对子的操作:
(remq obj list)
,使用eq?(remv obj list)
,使用eqv?(remove obj list)
,使用equal?
执行诀(remp procedure list)
跟memp类似。
执行诀(filter procedure list)
选出procedure返回真的对子。
执行诀(partition procedure list)
,返回filter选中的结果,以及filter抛弃的结果。
执行诀(find procedure list)
用于在list中查找某个对子。
association list相关的操作:
(assq obj alist)
(assv obj alist)
(assoc obj alist)
(assp obj alist)
association list中的每个对子是一个键值对,即(key . value)
。
执行诀(list-sort predicate list)
用来给连对排序,排序的依据是predicate,它接受两个参数,如果第一个参数需要排在第二个参数前面,则predicate返回#t。
Section 6.4. Numbers
模糊数通常是以浮点数或者科学计数法方式书写的。浮点数可以指定一个尾数mantissa,形式如|w
用于表示浮点数中的有效位。默认情况下,尾数为53。
复数的话,有矩阵坐标写法(例如3+4i
),以及极坐标写法(例如1.1@1.764
)两种。
无穷大和无穷小表示为+inf.0
以及-inf.0
。无效数表示为+nan.0
和-nan.0
。无穷大以及无穷小可以用正负模糊数除以模糊数0来获取。无效数则可以用无效0除以无效0来获取。
可以用#e
前缀来强制产生精确数,可以用#i
前缀来强制产生模糊数。
默认情况下数字是十进制的。可以用#b
、#o
、#d
、#x
来产生二进制,八进制,十进制以及十六进制数。这些前缀可以和精确数前缀结合使用,顺序可以任意。
实现上,可以对模糊数使用其他尺寸标记,比如:s(short)
, f(single)
, d(double)
, l(long)
。默认的尺寸标记必须是double。
一个数字可以记为多种形式。但是在打印(在put-datum, write以及display)还有number->string的时候,系统默认会使用最简洁的方式。
下面描述和数字操作相关的执行诀。有一些记号形式需要记住。num表示复数。real表示实数。rat表示有理数。int表示整数。
(exact? num)
(inexact? num)
(= num1 num2 num3 ...)
,判定所有数是否相等(< real1 real2 real3 ...)
,判定real1 < real2, real2 < real3等等(> real1 real2 real3 ...)
(<= real1 real2 real3 ...)
(>= real1 real2 real3 ...)
(+ num ...)
(- num)
(- num1 num2 num3 ...)
(* num ...)
,注意(*)
等于1(/ num)
(/ num1 num2 num3 ...)
(zero? num)
(positive? real)
(negative? real)
(even? int)
(odd? int)
(finite? real)
(infinite? real)
(nan? real)
(quotient int1 int2)
(remainder int1 int2)
(modulo int1 int2)
(div x1 x2)
将quotient操作扩展到实数(mod x1 x2)
将remainder操作扩展到实数(div-and-mod x1 x2)
等同于(define (div-and-mod x1 x2) (values (div x1 x2) (mod x1 x2)))
(div0 x1 x2)
div受限版(mod0 x1 x2)
mod受限版(div0-and-mod0 x1 x2)
(truncate real)
,向零取整;如果是NaN或者无穷大则返回real(floor real)
,向负无穷取整;如果是NaN或者无穷大则返回real(ceiling real)
,向上取整;如果是NaN或者无穷大则返回real(round real)
,就近取整;如果是NaN或者无穷大则返回real(abs real)
,相当于(lambda (x) (if (< x 0) (- x) x))
(max real1 real2 ...)
,找出最大值(min real1 real2 ...)
,找出最小值(gcd int ...)
,找出公约数(expt num1 num2)
,求指数值(inexact num)
,模糊化数字表达,等同于(exact->inexact num)
(exact num)
,精确化数字表达,等同于(inexact->exact num)
(rationalize real1 real2)
,有理数化(numerator rat)
,给出分子(denominator rat)
,给出分母(real-part num)
,给出实部(imag-part num)
,给出虚部(make-rectangular real1 real2)
,给出一个矩阵坐标系上的复数(make-polar real1 real2)
,给出一个极坐标系上的复数(angle num)
,给出一个极坐标系上的复数的角度(magnitude num)
,给出一个极坐标系上的复数的幅度(sqrt num)
(exact-integer-sqrt n)
)(exp num)
,欧拉数为底的指数(log num)
,欧拉数为底的指数(sin num)
,(cos num)
(tan num)
(asin num)
(acos num)
(atan num)
(atan real1 real2)
(bitwise-not exint)
(bitwise-and exint ...)
(bitwise-ior exint ...)
(bitwise-xor exint ...)
(bitwise-if exint1 exint2 exint3)
(bitwise-bit-count exint)
(bitwise-length exint)
(bitwise-first-bit-set exint)
(bitwise-bit-set? exint1 exint2
(bitwise-copy-bit exint1 exint2 exint3)
(bitwise-bit-field exint1 exint2 exint3)
(bitwise-copy-bit-field exint1 exint2 exint3 exint4)
(bitwise-arithmetic-shift-right exint1 exint2)
(bitwise-arithmetic-shift exint1 exint2)
(bitwise-rotate-bit-field exint1 exint2 exint3 exint4)
(bitwise-reverse-bit-field exint1 exint2 exint3)
(string->number string)
(string->number string radix)
(number->string num)
(number->string num radix)
(number->string num radix precision)
Section 6.5. Fixnums
Fixnum用来表示精确的整数,其有效值范围从 -2**(w - 1) 到2(w-2)-1。w代表有效比特数,最少24。通过fixnum-width可以打印w的值,在一台64位的机子上,其值可能是61。least-fixnum和greatest-fixnum打印其有效值范围。
相关执行诀:
(fixnum? obj)
(least-fixnum)
(greatest-fixnum)
(fixnum-width)
(fx=? fx1 fx2 fx3 ...)
(fx<? fx1 fx2 fx3 ...)
(fx>? fx1 fx2 fx3 ...)
(fx<=? fx1 fx2 fx3 ...)
(fx>=? fx1 fx2 fx3 ...)
(fxzero? fx)
(fxpositive? fx)
(fxnegative? fx)
(fxeven? fx)
(fxodd? fx)
(fxmin fx1 fx2 ....)
(fxmax fxa fx2 ...)
(fx+ fx1 fx2)
(fx- fx)
(fx- fx1 fx2)
(fx* fx1 fx2)
(fxdiv fx1 fx2)
(fxmod fx1 fx2)
(fxdiv-and-mod fx1 fx2)
(fxdiv0 fx1 fx2)
(fxmod0 fx1 fx2)
(fxdiv0-and-mod0 fx1 fx2)
(fx+/carry fx1 fx2 fx3)
(fx-/carry fx1 fx2 fx3)
(fx*/carry fx1 fx2 fx3)
(fxnot fx)
(fxand fx ...)
(fxior fx ...)
(fxxor fx ...)
(fxif fx1 fx2 fx3)
(fxbit-count fx)
(fxlength fx)
(fxfirst-bit-set fx)
(fxbit-set? fx1 fx2)
(fxcopy-bit fx1 fx2 fx3)
(fxbit-field fx1 fx2 fx3)
(fxcopy-bit-filed fx1 fx2 fx3 fx4)
(fxarithmetic-shift-right fx1 fx2)
(fxarithmetic-shift-left fx1 fx2)
(fxarithmetic-shift fx1 fx2)
(fxrotate-bit-field fx1 fx2 fx3 fx4)
(fxreverse-bit-field fx1 fx2 fx3)
Section 6.6. Flonums
Flonum用来表示不精确实数。其通常用IEEE 754双精度浮点数表示,但不做具体限定。任何实数,只要文法上不带竖线或者e以外的指数,都要求用flonum表示。
相关执行诀:
(flonum? obj)
(fl=? fl1 fl2 fl3 ...)
(fl<? fl1 fl2 fl3 ...)
(fl>? fl1 fl2 fl3 ...)
(fl<=? fl1 fl2 fl3 ...)
(fl>=? fl1 fl2 fl3 ...)
(flzero? fl)
(flpositive? fl)
(flnegative? fl)
(flinteger? fl)
(flfinite? fl)
(flinfinite? fl)
(flnan? fl)
(fleven? fl-int)
(flodd? fl-int)
(flmin fl1 fl2 ...)
(flmax fl1 fl2 ...)
(fl+ fl ...)
(fl- fl)
(fl- fl1 fl2 fl3 ...)
(fl* fl ...)
(fl/ fl)
(fl/ fl1 fl2 fl3 ...)
(fldiv fl1 fl2)
(flmod fl1 fl2)
(fldiv-and-mod fl1 fl2)
(fldiv0 fl1 fl2)
(flmod0 fl1 fl2)
(fldiv0-and-mod0 fl1 fl2)
(flround fl)
(fltruncate fl)
(flfloor fl)
(flceiling fl)
(flnumerator fl)
(fldenominator fl)
(flabs fl)
(flexp fl)
(fllog fl)
(fllog fl1 fl2)
(flsin fl)
(flcos fl)
(fltan fl)
(flasin fl)
(flacos fl)
(flatan fl)
(flatan fl1 fl2)
(flsqrt fl)
(flexpt fl1 fl2)
(fixnum->flonum fx)
(real->flonum real)
(本篇完)