On this page:
4.7.1 Simple Branching:   if
4.7.2 Combining Tests:   and and or
4.7.3 Chaining Tests:   cond

4.7 Conditionals🔗

大多数用于分支的函数,如 <string?,产生 #t#f。然而,Racket 的分支形式将 #f 以外的任何值都视为真。我们说一个真值是指 #f 以外的任何值。

这种关于"真值"的约定与 #f 可以表示失败或指示可选值未提供的协议很好地配合。(注意不要过度使用这个技巧,并且记住异常通常是报告失败的更好机制。)

例如,member 函数具有双重功能:它可以用来查找以特定项开头的列表的尾部,也可以简单地用来检查某个项是否存在于列表中:

> (member "Groucho" '("Harpo" "Zeppo"))

#f

> (member "Groucho" '("Harpo" "Groucho" "Zeppo"))

'("Groucho" "Zeppo")

> (if (member "Groucho" '("Harpo" "Zeppo"))
      'yep
      'nope)

'nope

> (if (member "Groucho" '("Harpo" "Groucho" "Zeppo"))
      'yep
      'nope)

'yep

4.7.1 Simple Branching: if🔗

+(part ("(lib scribblings/reference/reference.scrbl)" "if")) in (part ("(lib scribblings/reference/reference.scrbl)" "top")) also documents if.

if 形式中,

(if test-expr then-expr else-expr)

test-expr 总是被求值。如果它产生 #f 以外的任何值,则对 then-expr 进行求值。否则,对 else-expr 进行求值。

if 形式必须同时有 then-exprelse-expr;后者不是可选的。要根据 test-expr 执行(或跳过)副作用,请使用 whenunless,我们将在 Sequencing 中描述它们。

4.7.2 Combining Tests: and and or🔗

+(part ("(lib scribblings/reference/reference.scrbl)" "if")) in (part ("(lib scribblings/reference/reference.scrbl)" "top")) also documents and and or.

Racket 的 andor 是语法形式,而不是函数。与函数不同,andor 形式可以在前面的表达式确定答案时跳过后续表达式的求值。

(and expr ...)

如果任何 expr 产生 #f,则 and 形式产生 #f。否则,它产生最后一个 expr 的值。作为特例,(and) 产生 #t

(or expr ...)

如果所有 expr 都产生 #f,则 or 形式产生 #f。否则,它产生其 expr 中第一个非 #f 的值。作为特例,(or) 产生 #f

Examples:
> (define (got-milk? lst)
    (and (not (null? lst))
         (or (eq? 'milk (car lst))
             (got-milk? (cdr lst))))) ; recurs only if needed
> (got-milk? '(apple banana))

#f

> (got-milk? '(apple milk banana))

#t

如果求值到达 andor 形式的最后一个 expr,那么该 expr 的值直接决定 andor 的结果。因此,最后一个 expr 处于尾部位置,这意味着上面的 got-milk? 函数在常量空间中运行。

+Tail Recursion introduces tail calls and tail positions.

4.7.3 Chaining Tests: cond🔗

cond 形式将一系列测试链接起来以选择结果表达式。粗略地说,cond 的语法如下:

+(part ("(lib scribblings/reference/reference.scrbl)" "if")) in (part ("(lib scribblings/reference/reference.scrbl)" "top")) also documents cond.

(cond [test-expr body ...+]
      ...)

每个 test-expr 按顺序求值。如果它产生 #f,则忽略相应的 body,继续对下一个 test-expr 求值。一旦某个 test-expr 产生真值,就对关联的 body 求值以产生 cond 表达式的结果,并且不再对后续的 test-expr 求值。

cond 中最后一个 test-expr 可以用 else 替代。就求值而言,else#t 的同义词,但它表明最后一个子句旨在捕获所有剩余情况。如果不使用 else,则可能没有任何 test-expr 产生真值;在这种情况下,cond 表达式的结果为 #<void>

Examples:
> (cond
   [(= 2 3) (error "wrong!")]
   [(= 2 2) 'ok])

'ok

> (cond
   [(= 2 3) (error "wrong!")])
> (cond
   [(= 2 3) (error "wrong!")]
   [else 'ok])

'ok

(define (got-milk? lst)
  (cond
    [(null? lst) #f]
    [(eq? 'milk (car lst)) #t]
    [else (got-milk? (cdr lst))]))

 

> (got-milk? '(apple banana))

#f

> (got-milk? '(apple milk banana))

#t

cond 的完整语法还包括另外两种子句:

(cond cond-clause ...)
 
cond-clause = [test-expr then-body ...+]
  | [else then-body ...+]
  | [test-expr => proc-expr]
  | [test-expr]

=> 变体捕获其 test-expr 的真值结果,并将其传递给 proc-expr 的结果,后者必须是一个单参数函数。

Examples:
> (define (after-groucho lst)
    (cond
      [(member "Groucho" lst) => cdr]
      [else (error "not there")]))
> (after-groucho '("Harpo" "Groucho" "Zeppo"))

'("Zeppo")

> (after-groucho '("Harpo" "Zeppo"))

not there

仅包含 test-expr 的子句很少使用。它捕获 test-expr 的真值结果,并直接返回整个 cond 表达式的结果。