跳转到内容

Scheme 编程/条件语句

来自维基教科书,开放的书籍,为开放的世界
Scheme 编程
 ← 使用变量 条件语句 过程 → 

在本节中,我们介绍 Scheme 的条件表达式。这些形式使我们能够做出选择——这是我们迄今为止看到的简单程序中所缺乏的东西。

Scheme 中的真值

[编辑 | 编辑源代码]

我们已经在前面的例子中看到了布尔值 #t(“真”)和 #f(“假”),作为诸如 (< 3 5) 这样的表达式的值。布尔值只有少数过程[1];最常见的可能是 not,它对其参数取反。

> (not #t)
#f
> (not #f)
#t

在 Scheme 中,布尔值不是唯一具有真值的物件;Scheme 的惯例是将 #f 视为假,并将所有其他 Scheme 值视为真。Scheme 中一个非常常见的习惯用法是,过程在成功时返回一些有用的值(很少是 #t),而在失败时返回 #f。虽然我们的过程将坚持返回 #t 的做法,但了解这种习惯用法很重要。

andor 形式[2] 使我们能够以熟悉的方式操作真值。

> (and #t #f)
#f
> (or #f (not #f))
#t
> (and (> 3 2) #t)
#t
> (or (<= 3 2) (zero? (- 10 3)))
#f

在这种基本用法中,这些形式分别为我们提供了它们的两个参数(称为测试)的布尔 AND 和 OR。这通常已经足够了。然而,andor 形式更灵活一些。首先,它们可以接受任意数量的测试。

> (and (> 3 2) (>= 5 4) (> 10 9))
#t
> (or (<= 2 3)
      (zero? (- 10 3))
      #f
      (> 3 5))
#t

因此,当所有测试都为真时,and 表达式为真;当至少一个测试为真时,or 表达式为真。

如上所述,我们还可以进行评估结果不为 #t#f 的测试。

> (and (> 3 2) 5)
5  ; ?

由于第一个测试表达式评估为 #t,而第二个测试表达式评估为 5(它不是 #f#f 是 Scheme 中唯一被认为是假的的值),因此这个 and 表达式必须为真——但是它为什么评估为 5 呢?Scheme 的约定是,当所有测试都为真时,and 返回最后一个测试的值;or 返回第一个真测试的值。通过这种方式,我们可以在不影响表达式真值的情况下返回比 #t 更实用的值。

简单的条件语句if

[编辑 | 编辑源代码]

Scheme 中最简单的条件语句是 if 形式。以下是一些示例。

> (if #t 1 0)
1
> (if (>= 5 8) 3 (+ 7 2))
9

andor 一样,if 是一种具有自身语法的特殊形式。以下是通用形式。

(if test consequent alternative)

if 表达式的第一个组件是测试表达式,它首先被评估。如果它的值为真,则返回第二个组件表达式的值(结果)。另一方面,如果测试评估为假,那么我们得到第三个组件的值(备选)。[3] 让我们看看这在上面的例子中是如何工作的。在第一个例子中,测试表达式只是 #t,因此 if 形式的值是结果表达式的值,即 1。为了评估第二个例子,我们首先考虑测试 (>= 5 8)。这评估为 #f,因此我们评估备选 (+ 7 2) 并将其值作为整个 if 表达式的值返回。

使用 if,我们可以编写一些有用的过程。一个简单的示例。

(define (absolute-value n)
  (if (positive? n)
      n
      (- n)))  ; with one argument, - gives the additive inverse

在测试表达式中使用 andor 形式,我们可以组合多个测试。

(define (days-in-year y)
  (if (or (= (floor-remainder y 400) 0)
          (and (= (floor-remainder y 4) 0)
               (not (= (floor-remainder y 100) 0))))
      366  ; leap year
      365))
练习
  1. 在不使用 andor 的情况下重写 days-in-year;仅使用 if 表达测试。

我们可以使用 if 编写大量的有用 Scheme 程序。实际上,通过将 if 连在一起,我们可以编写任何我们想要的条件表达式。不幸的是,这些表达式很快就会变得复杂。当嵌套 if 时,很难跟踪所有子句,并且可能需要多次编写结果。(例如,在最后一个练习中,有多少种情况下 366 是答案?)在这些情况下,我们需要一些更方便的东西。

为此,Scheme 提供了 cond,这是一种非常灵活的条件形式,它允许轻松地表达多路选择。以下是一个简短的示例。

(cond ((> 5 6) 7)
      ((= 3 (+ 1 2)) 4)
      (else 8))

在最简单的形式中,cond 接受多个子句,每个子句都包含一个测试和一个结果表达式。为了评估 cond 表达式,我们依次评估每个子句的测试;如果其中一个测试评估为真,则 cond 返回该子句的结果表达式的值,并跳过任何剩余的子句。一个仅仅是 else 的测试总是为真,因此 cond 将始终“选择”一个以 else 作为其测试的子句的结果。通常,cond 的最后一个子句是“else 子句”。

cond 表达式的简化形式是

(cond clause1 clause2 ...)

其中每个子句的形式为 (test result)(else result)[4]

让我们逐步分析一个简单的 cond 表达式的评估过程。

(cond ((>= 5 8) 3)
      (else (+ 7 2)))

为了评估这个表达式,我们查看第一个子句 ((>= 5 8) 3)。这个子句的测试表达式是 (>= 5 8),因此我们评估它;由于它的值为 #f,因此我们跳过这个子句,并继续下一个子句 (else (+ 7 2))。这个子句的测试是 else,它总是为“真”。我们评估这个子句的结果表达式 (+ 7 2),并将它的值 9 作为整个 cond 表达式的值返回。由于这个 cond 表达式在第一个子句的测试为真时返回 3,否则返回 9,因此它与以下 if 形式完全等效。

(if (>= 5 8) 3 (+ 7 2))

因此,我们可以用一个等效的 cond 替换任何 if 表达式。实际上,cond 非常通用,我们可以用它来代替任何其他条件形式。

  1. R7RS § 6.3
  2. 虽然 andor 表达式看起来非常像应用,但它们不是过程,而是特殊形式。Scheme 具有许多特殊形式,每种形式都有自己的语法和语义。本节中描述的条件形式是一些最常见的特殊形式。
  3. 第三个表达式是可选的。形式为 (if b x) 的表达式,有时被称为“单臂 if”,是合法的 Scheme。如果 b 为真,则它给出 x 的值,否则给出未指定的值(可以是任何值)。我们将在“高级 Scheme”一章中看到如何使用它;现在,我们始终将包含备选。
  4. 如上所述,这是简化的。一个子句实际上可以接受任意数量的结果表达式,从而得到通用形式 (test expression1 expression2 ...)。如果test 为真,则依次评估这些表达式,并返回最后一个表达式的值。如果只有一个结果表达式,则等效于我们上面描述的内容。cond 还提供了一种称为“cond 箭头”的表示法,我们将在“高级 Scheme”一章中讨论。
华夏公益教科书