2.7 Abstraction: "abstraction.rkt"
(require 2htdp/abstraction) | package: htdp-lib |
The abstract.rkt teachpack provides some additional abstraction facilities: comprehensions and loops, matching, and algebraic data types. Most of these are restricted versions of full-featured constructs in other members of the Racket family so that students of HtDP/2e don’t stumble across syntactic oddities.
HtDP/2e introduces loops and matching in an intermezzo, with the sole purpose of acknowledging the existence of powerful linguistic mechanisms.
Algebraic data types are provided for those who think teaching the features of functional programming is more important than teaching universally applicable ideas of program design.
Added in version 1.1 of package htdp-lib.
2.7.1 Loops and Comprehensions
syntax
(for/list (comprehension-clause comprehension-clause ...) body-expr)
comprehension-clause = (name clause-expr)
Each comprehension-clause binds its name in body-expr.
list, its items make up the sequence values;
natural number n, the sequence of values consists of the numbers 0, 1, ..., (- n 1);
string, its one-character strings are the sequence items.
> (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")
syntax
(for*/list (comprehension-clause comprehension-clause ...) body-expr)
Each comprehension-clause binds its name in the expressions of the following comprehension-clauses as well as body-expr.
> (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))
syntax
(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
syntax
(for*/or (comprehension-clause comprehension-clause ...) body-expr)
> (for*/or ([i 2][j i]) (if (> j i) (list i j) #false)) #f
syntax
(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
syntax
(for*/and (comprehension-clause comprehension-clause ...) body-expr)
> (for*/and ([i 2][j i]) (if (< j i) (list i j) #false)) '(1 0)
syntax
(for/sum (comprehension-clause comprehension-clause ...) body-expr)
> (for/sum ([i 2][j 8]) (max i j)) 1
syntax
(for*/sum (comprehension-clause comprehension-clause ...) body-expr)
> (for*/sum ([i 2][j i]) (min i j)) 0
syntax
(for/product (comprehension-clause comprehension-clause ...) body-expr)
> (for/product ([i 2][j 3]) (+ i j 1)) 3
syntax
(for*/product (comprehension-clause comprehension-clause ...) body-expr)
> (for*/product ([i 2][j i]) (+ i j 1)) 2
syntax
(for/string (comprehension-clause comprehension-clause ...) body-expr)
> (for/string ([i "abc"]) (int->string (+ (string->int i) 1))) "bcd"
syntax
(for*/string (comprehension-clause comprehension-clause ...) body-expr)
> (for*/string ([i "ab"][j (- (string->int i) 90)]) (int->string (+ (string->int i) j))) "abcdefgbcdefghi"
procedure
start : natural-number/c end : natural-number/c step : natural-number/c (in-range end) → sequence? end : natural-number/c
If start, end, and step are provided, the sequence consists of start, (+ start step), (+ start step step), ... until the sum is greater than or equal to end.
> (for/list ([i (in-range 1 10 3)]) i) '(1 4 7)
procedure
(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 Pattern Matching
syntax
(match case-expr (pattern body-expr) ...)
pattern = name | literal-constant | (cons pattern pattern) | (list pattern ...) | (name pattern ...) | (? name)
The literal constants commonly used are numbers, strings, symbols, and '().
Each pattern that contains names binds these names in the corresponding body-expr.
name, it matches any value;
literal-constant, it matches only the literal constant;
(cons pattern_1 pattern_2), it matches when the value is an instance of cons, and its first/rest fields match pattern_1 and pattern_2, respectively;
(list pattern ...), it matches when the value is a list, and each element matches its corresponding pattern;
(name pattern ...), it matches when the value is an instance of the name structure type, and its field values match pattern ...;
(? name), it matches when name refers to a predicate function and the latter produces #true on the given value.
> (define (last-item l) (match l [(cons lst '()) lst] [(cons fst rst) (last-item rst)])) > (last-item '(a b c)) 'c
> (define (get-title page) (match page [(list 'html (list 'head (list 'title title)) body) title] [anything "Untitled"])) > (get-title '(html (head (title "hello")) (body (p "world")))) "hello"
> (get-title '(html (head) (body (p "world")))) "Untitled"
> (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 Algebraic Data Types
syntax
(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
syntax
(type-case type case-expr (variant (field ...) body-expr) ...)
A type-case expression also ensures that (1) the collection variant cases covers all variant structure type definitions in type and (2) that each variant clauses specifies as many fields as the definition of type specifies.
(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