-
-
Notifications
You must be signed in to change notification settings - Fork 657
/
cond.scrbl
166 lines (131 loc) · 5.62 KB
/
cond.scrbl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#lang scribble/doc
@(require scribble/manual scribble/eval "guide-utils.rkt")
@title[#:tag "conditionals"]{Conditionals}
Most functions used for branching, such as @racket[<] and
@racket[string?], produce either @racket[#t] or @racket[#f]. Racket's
branching forms, however, treat any value other than @racket[#f] as
true. We say a @defterm{true value} to mean any value other than
@racket[#f].
This convention for ``true value'' meshes well with protocols where
@racket[#f] can serve as failure or to indicate that an optional value
is not supplied. (Beware of overusing this trick, and remember that an
exception is usually a better mechanism to report failure.)
For example, the @racket[member] function serves double duty; it can
be used to find the tail of a list that starts with a particular item,
or it can be used to simply check whether an item is present in a
list:
@interaction[
(member "Groucho" '("Harpo" "Zeppo"))
(member "Groucho" '("Harpo" "Groucho" "Zeppo"))
(if (member "Groucho" '("Harpo" "Zeppo"))
'yep
'nope)
(if (member "Groucho" '("Harpo" "Groucho" "Zeppo"))
'yep
'nope)
]
@;------------------------------------------------------------------------
@section{Simple Branching: @racket[if]}
@refalso["if"]{@racket[if]}
In an @racket[if] form,
@specform[(if test-expr then-expr else-expr)]
the @racket[_test-expr] is always evaluated. If it produces any value
other than @racket[#f], then @racket[_then-expr] is
evaluated. Otherwise, @racket[_else-expr] is evaluated.
An @racket[if] form must have both a @racket[_then-expr] and an
@racket[_else-expr]; the latter is not optional. To perform (or skip)
side-effects based on a @racket[_test-expr], use @racket[when] or
@racket[unless], which we describe later in @secref["begin"].
@;------------------------------------------------------------------------
@section[#:tag "and+or"]{Combining Tests: @racket[and] and @racket[or]}
@refalso["if"]{@racket[and] and @racket[or]}
Racket's @racket[and] and @racket[or] are syntactic forms, rather than
functions. Unlike a function, the @racket[and] and @racket[or] forms
can skip evaluation of later expressions if an earlier one determines
the answer.
@specform[(and expr ...)]
An @racket[and] form produces @racket[#f] if any of its @racket[_expr]s
produces @racket[#f]. Otherwise, it produces the value of its last
@racket[_expr]. As a special case, @racket[(and)] produces
@racket[#t].
@specform[(or expr ...)]
The @racket[or] form produces @racket[#f] if all of its
@racket[_expr]s produce @racket[#f]. Otherwise, it produces the first
non-@racket[#f] value from its @racket[expr]s. As a special case,
@racket[(or)] produces @racket[#f].
@examples[
(code:line
(define (got-milk? lst)
(and (not (null? lst))
(or (eq? 'milk (car lst))
(got-milk? (cdr lst))))) (code:comment @#,t{recurs only if needed}))
(got-milk? '(apple banana))
(got-milk? '(apple milk banana))
]
If evaluation reaches the last @racket[_expr] of an @racket[and] or
@racket[or] form, then the @racket[_expr]'s value directly determines
the @racket[and] or @racket[or] result. Therefore, the last
@racket[_expr] is in tail position, which means that the above
@racket[got-milk?] function runs in constant space.
@guideother{@secref["tail-recursion"] introduces tail calls and tail positions.}
@;------------------------------------------------------------------------
@section[#:tag "cond"]{Chaining Tests: @racket[cond]}
The @racket[cond] form chains a series of tests to select a result
expression. To a first approximation, the syntax of @racket[cond] is
as follows:
@refalso["if"]{@racket[cond]}
@specform[(cond [test-expr body ...+]
...)]
Each @racket[_test-expr] is evaluated in order. If it produces
@racket[#f], the corresponding @racket[_body]s are ignored, and
evaluation proceeds to the next @racket[_test-expr]. As soon as a
@racket[_test-expr] produces a true value, the associated @racket[_body]s
are evaluated to produce the result for the @racket[cond] form, and no
further @racket[_test-expr]s are evaluated.
The last @racket[_test-expr] in a @racket[cond] can be replaced by
@racket[else]. In terms of evaluation, @racket[else] serves as a
synonym for @racket[#t], but it clarifies that the last clause is
meant to catch all remaining cases. If @racket[else] is not used, then
it is possible that no @racket[_test-expr]s produce a true value; in
that case, the result of the @racket[cond] expression is
@|void-const|.
@examples[
(cond
[(= 2 3) (error "wrong!")]
[(= 2 2) 'ok])
(cond
[(= 2 3) (error "wrong!")])
(cond
[(= 2 3) (error "wrong!")]
[else 'ok])
]
@def+int[
(define (got-milk? lst)
(cond
[(null? lst) #f]
[(eq? 'milk (car lst)) #t]
[else (got-milk? (cdr lst))]))
(got-milk? '(apple banana))
(got-milk? '(apple milk banana))
]
The full syntax of @racket[cond] includes two more kinds of clauses:
@specform/subs[#:literals (else =>)
(cond cond-clause ...)
([cond-clause [test-expr then-body ...+]
[else then-body ...+]
[test-expr => proc-expr]
[test-expr]])]
The @racket[=>] variant captures the true result of its
@racket[_test-expr] and passes it to the result of the
@racket[_proc-expr], which must be a function of one argument.
@examples[
(define (after-groucho lst)
(cond
[(member "Groucho" lst) => cdr]
[else (error "not there")]))
(after-groucho '("Harpo" "Groucho" "Zeppo"))
(after-groucho '("Harpo" "Zeppo"))
]
A clause that includes only a @racket[_test-expr] is rarely used. It
captures the true result of the @racket[_test-expr], and simply
returns the result for the whole @racket[cond] expression.