2.7 抽象: "abstraction.rkt"
(require 2htdp/abstraction) | package: htdp-lib |
abstract.rkt教学包提供一些额外的抽象工具:解析(comprehension)和循环、匹配及代数数据类型。 其中大多数是其他Racket系列语言中完整功能的受限版本, 因此HtDP/2e(《程序设计方法》第二版)的学生不必纠结于复杂的语法。
HtDP/2e在一独立章节中介绍了循环和匹配,其唯一目的是让学生知悉强大语言机制的存在。
提供代数数据类型的理由是,有些人认为教授函数式编程的特性比教授普遍适用的程序设计思想更重要。
添加于package htdp-lib的1.1版本。
2.7.1 循环和解析
语法
(for/list (comprehension-clause comprehension-clause ...) body-expr)
comprehension-clause = (name clause-expr)
comprehension-clause(解析子句)在body-expr中绑定它的name。
链表,其项组成序列值;
自然数n,值的序列由数字0、1、…、(- n 1)组成;
字符串,序列的项为其中每个字符构成的字符串。
> (for/list ((i 10)) i) '(0 1 2 3 4 5 6 7 8 9)
> (for/list ((i 2) (j '(a b))) (list i j)) '((0 a) (1 b))
> (for/list ((c "abc")) c) '("a" "b" "c")
语法
(for*/list (comprehension-clause comprehension-clause ...) body-expr)
comprehension-clause在body-expr中, 以及后续的comprehension-clause中绑定它的name。
> (for*/list ((i 2) (j '(a b))) (list i j)) '((0 a) (0 b) (1 a) (1 b))
> (for*/list ((i 5) (j i)) (list i j)) '((1 0) (2 0) (2 1) (3 0) (3 1) (3 2) (4 0) (4 1) (4 2) (4 3))
> (for*/list ((i 2) (j '(a b c d e))) (list i j)) '((0 a) (0 b) (0 c) (0 d) (0 e) (1 a) (1 b) (1 c) (1 d) (1 e))
语法
(for/or (comprehension-clause comprehension-clause ...) body-expr)
> (for/or ([c "abcd"]) (if (string=? "x" c) c #false)) #f
> (for/or ([c (list #false 1 #false 2)]) c) 1
语法
(for*/or (comprehension-clause comprehension-clause ...) body-expr)
> (for*/or ([i 2][j i]) (if (> j i) (list i j) #false)) #f
语法
(for/and (comprehension-clause comprehension-clause ...) body-expr)
> (for/and ([c '(1 2 3)]) (if (> c 4) c #false)) #f
> (for/and ([c '(1 2 3)]) (if (< c 4) c #false)) 3
语法
(for*/and (comprehension-clause comprehension-clause ...) body-expr)
> (for*/and ([i 2][j i]) (if (< j i) (list i j) #false)) '(1 0)
语法
(for/sum (comprehension-clause comprehension-clause ...) body-expr)
> (for/sum ([i 2][j 8]) (max i j)) 1
语法
(for*/sum (comprehension-clause comprehension-clause ...) body-expr)
> (for*/sum ([i 2][j i]) (min i j)) 0
语法
(for/product (comprehension-clause comprehension-clause ...) body-expr)
> (for/product ([i 2][j 3]) (+ i j 1)) 3
语法
(for*/product (comprehension-clause comprehension-clause ...) body-expr)
> (for*/product ([i 2][j i]) (+ i j 1)) 2
语法
(for/string (comprehension-clause comprehension-clause ...) body-expr)
> (for/string ([i "abc"]) (int->string (+ (string->int i) 1))) "bcd"
语法
(for*/string (comprehension-clause comprehension-clause ...) body-expr)
> (for*/string ([i "ab"][j (- (string->int i) 90)]) (int->string (+ (string->int i) j))) "abcdefgbcdefghi"
函数
start : natural-number/c end : natural-number/c step : natural-number/c (in-range end) → sequence? end : natural-number/c
如果提供了start、end和step, 序列就是start、(+ start step)、(+ start step step)、…直到总和大于或等于end。
> (for/list ([i (in-range 1 10 3)]) i) '(1 4 7)
函数
(in-naturals start) → sequence?
start : natural-number/c
> (define (enumerate a-list) (for/list ([x a-list][i (in-naturals 1)]) (list i x))) > (enumerate '(Maxwell Einstein Planck Heisenberg Feynman)) '((1 Maxwell) (2 Einstein) (3 Planck) (4 Heisenberg) (5 Feynman))
> (enumerate '("Pinot noir" "Pinot gris" "Pinot blanc")) '((1 "Pinot noir") (2 "Pinot gris") (3 "Pinot blanc"))
2.7.2 模式匹配
语法
(match case-expr (pattern body-expr) ...)
pattern = name | literal-constant | (cons pattern pattern) | (name pattern ...) | (? name)
常见的文字常量是数字、字符串、符号和'()。
每个包含name的模式都会在相应的body-expr中绑定这些名称。
name,它匹配任何值;
literal-constant,它只匹配本文常量;
(cons pattern_1 pattern_2),它匹配cons实例, 并且其first/rest字段能和pattern_1及pattern_2匹配;
(name pattern ...),它匹配name结构体类型的实例, 并且其字段值能和pattern ...匹配;
(? name),如果name是个谓词函数,并且它对给定的值返回#true, 那么匹配成功。
> (define (last-item l) (match l [(cons lst '()) lst] [(cons fst rst) (last-item rst)])) > (last-item '(a b c)) 'c
> (define (is-it-odd-or-even l) (match l [(? even?) 'even] [(? odd?) 'odd])) > (is-it-odd-or-even '1) 'odd
> (is-it-odd-or-even '2) 'even
> (define-struct doll (layer))
> (define (inside a-doll) (match a-doll [(? symbol?) a-doll] [(doll below) (inside below)])) > (inside (make-doll (make-doll 'wood))) 'wood
2.7.3 代数数据类型
语法
(define-type type (variant (field predicate) ...) ...)
type = name variant = name field = name predicate = name
(define-type BTree (leaf (info number?)) (node (left BTree?) (right BTree?)))
(define-struct leaf (info)) (define-struct node (left right))
> (make-leaf 42) (leaf 42)
> (make-node (make-leaf 42) (make-leaf 21)) (node (leaf 42) (leaf 21))
> (BTree? (make-node (make-leaf 42) (make-leaf 21))) #t
> (make-leaf 'four) make-leaf: contract violation
expected: (or/c undefined? number?)
given: 'four
in: the 1st argument of
(-> (or/c undefined? number?) leaf?)
contract from: make-leaf
blaming: use
(assuming the contract is correct)
at: program:2.0
语法
(type-case type case-expr (variant (field ...) body-expr) ...)
type-case表达式还确保 (1)variant子句的集合覆盖type中的所有变体的结构体类型定义,以及 (2)每个variant子句指定的字段数与type定义指定的一样多。
(define (depth t) (type-case BTree t [leaf (info) 0] [node (left right) (+ (max (depth left) (depth right)) 1)]))
> (depth (make-leaf 42)) 0
> (depth (make-node (make-leaf 42) (make-leaf 21))) 1