第五章 分支

2018-02-24 15:45 更新

5.1?簡介

上一章中,我講解了如何定義函數(shù)。本章中,我會(huì)講解如何通過條件編寫過程。這個(gè)是編寫使用程序很重要的一步。

5.2?if表達(dá)式

if表達(dá)式將過程分為兩個(gè)部分。if的格式如下:

(if predicate then_value else_value)

如果predicate部分為真,那么then_value部分被求值,否則else_value部分被求值,并且求得的值會(huì)返回給if語句的括號外。true是除false以外的任意值,true使用#t表示,false#f表示。

在R5RS中,false#f)和空表(’())是兩個(gè)不同的對象。然而,在MIT-Scheme中,這兩個(gè)為同一對象。這個(gè)不同可能是歷史遺留問題,在以前的標(biāo)準(zhǔn)——R4RS中,#f’()被定義為同一對象。

因此,從兼容性角度考慮,你不應(yīng)該使用表目錄作為謂詞。使用函數(shù)null?來判斷表是否為空。

(null? '())
;Value: #t

(null? '(a b c))
;Value: ()   ;#f

函數(shù)not可用于對謂詞取反。此函數(shù)只有一個(gè)參數(shù)且如果參數(shù)值為#f則返回#t,反之,參數(shù)值為#t則返回#f。if表達(dá)式是一個(gè)特殊形式,因?yàn)樗粚λ械膮?shù)求值。因?yàn)槿绻?code>predicate為真,則只有then_value部分被求值。另一方面,如果predicate為假,只有else_value部分被求值。

例:首項(xiàng)為a0,增長率r,項(xiàng)數(shù)為n的幾何增長(geometric progression)數(shù)列之和

(define (sum-gp a0 r n)
  (* a0
     (if (= r 1)
         n
         (/ (- 1 (expt r n)) (- 1 r)))))   ; !!

通常來說,幾何增長數(shù)列的求和公式如下:

a0 * (1 - r^n) / (1 - r)                      (r ≠ 1)
a0 * n                                        (r = 1)

如果if表達(dá)式對所有參數(shù)求值的話,那么有;!!注釋的那行就算在r=1時(shí)也會(huì)被求值,這將導(dǎo)致產(chǎn)生一個(gè)“除數(shù)為0”的錯(cuò)誤。

你也可以省去else_value項(xiàng)。這樣的話,當(dāng)predicate為假時(shí),返回值就沒有被指定。如果你希望當(dāng)predicate為假時(shí)返回#f,那么就要明確地將它寫出來。

then_valueelse_value都應(yīng)該是S-表達(dá)式。如果你需要副作用,那么就應(yīng)該使用begin表達(dá)式。我們將在下一章討論begin表達(dá)式。

練習(xí)1

編寫下面的函數(shù)。閱讀第五節(jié)了解如何編寫謂詞。

  • 返回一個(gè)實(shí)數(shù)絕對值的函數(shù)。
  • 返回一個(gè)實(shí)數(shù)的倒數(shù)的函數(shù)。如果參數(shù)為0,則返回#f。
  • 將一個(gè)整數(shù)轉(zhuǎn)化為ASCII碼字符的函數(shù)。整數(shù)可以被轉(zhuǎn)化為33-126號之間的ASCII碼。使用integer->char可以將整數(shù)轉(zhuǎn)化為字符。如果給定的整數(shù)不能夠轉(zhuǎn)化為字符,那么就返回#f。

5.3?and和or

andor是用于組合條件的兩個(gè)特殊形式。Scheme中的andor不同于C語言中的約定。它們不返回一個(gè)布爾值(#t#f),而是返回給定的參數(shù)之一。andor可以使你的代碼更加短小。

5.3.1?and

and具有任意個(gè)數(shù)的參數(shù),并從左到右對它們求值。如果某一參數(shù)為#f,那么它就返回#f,而不對剩余參數(shù)求值。反過來說,如果所有的參數(shù)都不是#f,那么就返回最后一個(gè)參數(shù)的值。

(and #f 0)
;Value: ()

(and 1 2 3)
;Value: 3

(and 1 2 3 #f)
;Value: ()

5.3.2?or

or具有可變個(gè)數(shù)的參數(shù),并從左到右對它們求值。它返回第一個(gè)不是值#f的參數(shù),而余下的參數(shù)不會(huì)被求值。如果所有的參數(shù)的值都是#f的話,則返回最后一個(gè)參數(shù)的值。

(or #f 0)
;Value: 0

(or 1 2 3)
;Value: 1

(or #f 1 2 3)
;Value: 1

(or #f #f #f)
;Value: ()

練習(xí)2

編寫下面的函數(shù)。

  • 一個(gè)接受三個(gè)實(shí)數(shù)作為參數(shù)的函數(shù),若參數(shù)皆為正數(shù)則返回它們的乘積。
  • 一個(gè)接受三個(gè)實(shí)數(shù)作為參數(shù)的函數(shù),若參數(shù)至少一個(gè)為負(fù)數(shù)則返回它們的乘積。

5.4?cond表達(dá)式

盡管所有的分支都可以用if表達(dá)式表達(dá),但當(dāng)條件有更多的可能性時(shí),你就需要使用嵌套的if表達(dá)式了,這將使代碼變得復(fù)雜。處理這種情況可以使用cond表達(dá)式。cond表達(dá)式的格式如下:

(cond
  (predicate_1 clauses_1)
  (predicate_2 clauses_2)
    ......
  (predicate_n clauses_n)
  (else        clauses_else))

cond表達(dá)式中,predicates_i是按照從上到下的順序求值,而當(dāng)predicates_i為真時(shí),clause_i會(huì)被求值并返回。i之后的predicatesclauses不會(huì)被求值。如果所有的predicates_i都是假的話,則返回cluase_else。在一個(gè)子句中,你可以寫數(shù)條S-表達(dá)式,而clause的值是最后一條S-表達(dá)式。

例:城市游泳池的收費(fèi)。

Foo市的城市游泳池按照顧客的年齡收費(fèi):

如果?age?≤ 3 或者?age?≥ 65 則 免費(fèi);
如果 介于 4 ≤?age?≤ 6 則 0.5美元;
如果 介于 7 ≤?age?≤ 12 則 1.0美元;
如果 介于 13 ≤?age?≤ 15 則 1.5美元;
如果 介于 16 ≤?age?≤ 18 則 1.8美元;
其它 則 2.0美元;

那么,一個(gè)返回城市游泳池收費(fèi)的函數(shù)如下:

(define (fee age)
  (cond
   ((or (<= age 3) (>= age 65)) 0)
   ((<= 4 age 6) 0.5)
   ((<= 7 age 12) 1.0)
   ((<= 13 age 15) 1.5)
   ((<= 16 age 18) 1.8)
   (else 2.0)))

練習(xí) 3

編寫下列函數(shù)。

成績(A-D)是由分?jǐn)?shù)決定的。編寫一個(gè)將分?jǐn)?shù)映射為成績的函數(shù),映射規(guī)則如下:

  • A 如果?score?≥ 80
  • B 如果 60 ≤?score?≤ 79
  • C 如果 40 ≤?score?≤ 59
  • D 如果?score?< 40

5.5?做出判斷的函數(shù)

我將介紹一些用于做判斷的函數(shù)。這些函數(shù)的名字都以'?'結(jié)尾。

eq?、eqv?和equal?

基本函數(shù)eq?、eqv?equal?具有兩個(gè)參數(shù),用于檢查這兩個(gè)參數(shù)是否“一致”。這三個(gè)函數(shù)之間略微有些區(qū)別。

eq?
該函數(shù)比較兩個(gè)對象的地址,如果相同的話就返回#t。例如,(eq? str str)返回#t,因?yàn)?code>str本身的地址是一致的。與此相對的,因?yàn)樽址?code>”hello”和”hello”被儲(chǔ)存在了不同的地址中,函數(shù)將返回#f。不要使用eq?來比較數(shù)字,因?yàn)椴粌H在R5RS中,甚至在MIT-Scheme實(shí)現(xiàn)中,它都沒有指定返回值。使用eqv?或者=替代。

(define str "hello")
;Value: str

(eq? str str)
;Value: #t

(eq? "hello" "hello")
;Value: ()             ← It should be #f in R5RS 

;;; comparing numbers depends on implementations
(eq? 1 1)
;Value: #t

(eq? 1.0 1.0)
;Value: ()

eqv?
該函數(shù)比較兩個(gè)存儲(chǔ)在內(nèi)存中的對象的類型和值。如果類型和值都一致的話就返回#t。對于過程(lambda表達(dá)式)的比較依賴于具體的實(shí)現(xiàn)。這個(gè)函數(shù)不能用于類似于表和字符串一類的序列比較,因?yàn)楸M管這些序列看起來是一致的,但它們的值是存儲(chǔ)在不同的地址中。

(eqv? 1.0 1.0)
;Value: #t

(eqv? 1 1.0)
;Value: ()

;;; don't use it to compare sequences
(eqv? (list 1 2 3) (list 1 2 3))
;Value: ()

(eqv? "hello" "hello")
;Value: ()

;;; the following depends on implementations
(eqv? (lambda(x) x) (lambda (x) x))
;Value: ()

equal?
該函數(shù)用于比較類似于表或者字符串一類的序列。

(equal? (list 1 2 3) (list 1 2 3))
;Value: #t

(equal? "hello" "hello")
;Value: #t

5.5.2?用于檢查數(shù)據(jù)類型的函數(shù)

下面列舉了幾個(gè)用于檢查類型的函數(shù)。這些函數(shù)都只有一個(gè)參數(shù)。

  • pair??如果對象為序?qū)t返回#t;
  • list??如果對象是一個(gè)表則返回#t。要小心的是空表’()是一個(gè)表但是不是一個(gè)序?qū)Α?/li>
  • null??如果對象是空表’()的話就返回#t。
  • symbol??如果對象是一個(gè)符號則返回#t。
  • char??如果對象是一個(gè)字符則返回#t。
  • string??如果對象是一個(gè)字符串則返回#t。
  • number??如果對象是一個(gè)數(shù)字則返回#t。
  • complex??如果對象是一個(gè)復(fù)數(shù)則返回#t。
  • real??如果對象是一個(gè)實(shí)數(shù)則返回#t。
  • rational??如果對象是一個(gè)有理數(shù)則返回#t。
  • integer??如果對象是一個(gè)整數(shù)則返回#t。
  • exact??如果對象不是一個(gè)浮點(diǎn)數(shù)的話則返回#t。
  • inexact??如果對象是一個(gè)浮點(diǎn)數(shù)的話則返回#t。

5.5.3?用于比較數(shù)的函數(shù)

=、><、<=、>=
這些函數(shù)都有任意個(gè)數(shù)的參數(shù)。如果參數(shù)是按照這些函數(shù)的名字排序的話,函數(shù)就返回#t。

(= 1 1 1.0)
;Value: #t

(< 1 2 3)
;Value: #t
(< 1)
;Value: #t
(<)
;Value: #t

(= 2 2 2)
;Value: #t

(< 2 3 3.1)
;Value: #t

(> 4 1 -0.2)
;Value: #t

(<= 1 1 1.1)
;Value: #t

(>= 2 1 1.0)
;Value: #t

(< 3 4 3.9)
;Value: ()

odd?even?、positive?negative?、zero?
這些函數(shù)僅有一個(gè)參數(shù),如果這些參數(shù)滿足函數(shù)名所指示的條件話就返回#t

5.5.4?用于比較符號的函數(shù)

在比較字符的時(shí)候可以使用char=?、char<?char>?、char<=?以及char>=?函數(shù)。具體的細(xì)節(jié)請參見R5RS。

5.5.5?用于比較字符串的函數(shù)

比較字符串時(shí),可以使用string=?string-ci=?等函數(shù)。具體細(xì)節(jié)請參見R5RS。

5.6?總結(jié)

在這一章中,我總結(jié)了關(guān)于分支的知識(shí)點(diǎn)。編寫分支程序可以使用if表達(dá)式和cond表達(dá)式。

下一章我將講解局部變量。

5.7?習(xí)題解答

5.7.1?答案1

; 1
(define (my-abs n)
  (* n
     (if (positive? n) 1 -1)))

; 2     
(define (inv n)
  (if (not (zero? n))
      (/ n)
      #f))

; 3
(define (i2a n)
  (if (<= 33 n 126)
      (integer->char n)
      #f))

5.7.2?答案2

; 1
(define (pro3and a b c)
  (and (positive? a)
       (positive? b)
       (positive? c)
       (* a b c)))

; 2
(define (pro3or a b c)
  (if (or (negative? a)
      (negative? b)
      (negative? c))
      (* a b c)))

5.7.3?答案3

(define (score n)
  (cond
   ((>= n 80) 'A)
   ((<= 60 n 79) 'B)
   ((<= 40 n 59) 'C)
   (else 'D)))
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號