第四章,條件語句
=====
和其它的編程語句一樣,Scheme 也包含條件語句。
最基本的結構就是if:
```scheme
(if 測試條件
then-分支
else-分支)
```
如果測試條件運算的結果是真(即,非`#f`的任何其它值),`then`分支將會被運行(即滿足條件時的運行分支)。否則,`else`分支會被運行。`else`分支是可選的。
```scheme
(define p 80)
(if (> p 70)
'safe
'unsafe)
=> safe
(if (< p 90)
'low-pressure) ;no ``else'' branch
=> low-pressure
```
為了方便,Scheme還提供了一些其它的條件結構語句。它們可以被定義成宏來擴充if表達式。
## 4.1 when 和 unless
當我們只需要一個基本條件語句分支時(”then”分支或”else”分支),使用when 和 unless會更方便。(這里的示例已經更換,原示例)
```scheme
(define a 10)
(define b 20)
(when (< a b)
(display “a是”)
(display a)
(display “b是”)
(display b)
(display “a大于b” ) )
```
先判斷a是否小于b,這個條件成立時會輸出5條信息。
使用if實現相同的程序會是這樣:
```scheme
(define a 10)
(define b 20)
(if (< a b)
(begin
(display “a是”)
(display a)
(display “b是”)
(display b)
(display “a大于b” ) ))
```
注意`when`的分支是一個隱式的`begin`語句結構,
而如果`if`的分支有多個代碼結構時,需要一個顯式的`begin`代碼結構。
同樣的功能還可以像下面這樣用`unless`來寫(`unless`和`when`的意思正好相反):
```scheme
(define a 10)
(define b 20)
(unless (>= a b)
(display “a是”)
(display a)
(display “b是”)
(display b)
(display “a大于b” ) )
```
并不是所有的Scheme環境都提供`when`和`unless`。
如果你的Scheme中沒有,你可以用宏來自定義出`when`和`unless`(宏,見第8章)。
## 4.2 cond
`cond`結構在表示多重`if`表達式時很方便,
多重`if`結構除了最后一個`else`分支以外的其余分支都會包含一個新的`if`條件。因此,
```scheme
(if (char<? c #\c) -1
(if (char=? c #\c) 0
1))
```
這樣的結構都可以使用`cond`來這樣寫:
```scheme
(cond ((char<? c #\c) -1)
((char=? c #\c) 0)
(else 1))
```
`cond`就是這樣的一種多分支條件結構。每個從句都包含一個判斷條件和一個相關的操作。第一個判斷成立的從句將會引發它相關的操作執行。如果任何一個分支的條件判斷都不成立則最后一個`else`分支將會執行(`else`分支語句是可選的)。
cond的分支操作都是`begin`結構。
## 4.3 case
當`cond`結構的每個測試條件是一個測試條件的分支條件時,可以縮減為一個`case`表達式。
```scheme
(define c #\c)
(case c
((#\a) 1)
((#\b) 2)
((#\c) 3)
(else 4))
=> 3
```
分支頭值是` #\c` 的分支將被執行。
## 4.4 and 和 or
Scheme提供了對boolean值進行邏輯與`and`和邏輯或`or`運算的結構。(我們已經見過了布爾類型的求反運算not過程。)
當所有子結構的值都是真時,`and`的返回值是真,實際上,`and`的運行結果是最后一個子結構的值。如果任何一個子結構的值都是假,則返回`#f`。
```scheme
(and 1 2) => 2
(and #f 1) => #f
```
而`or`會返回它第一個為值為真的子結構的結果。如果所有的子結構的值都為假,`or`則返回`#f`。
```scheme
(or 1 2) => 1
(or #f 1) => 1
```
`and`和`or`都是從左向右運算。當某個子結構可以決定最終結果時,`and`和`or`會忽略剩余的子結構,即它們是“短路”的。
```scheme
(and 1 #f expression-guaranteed-to-cause-error)
=> #f
(or 1 #f expression-guaranteed-to-cause-error)
=> 1
```