跳轉到內容

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”一章中討論。
華夏公益教科書