Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

19 Feb 2021

The Scheme Programming Language读书笔记【五】

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)

(本篇完)

Categories