条件表达式与谓词
回顾条件表达式 if 与 cond 的用法、子句与谓词的概念,讲解谓词的代码风格,并通过 plural、max-sum-squares 和 Pig Latin 练习巩固。
条件表达式回顾
我们在第一次实验中用过 if 和条件表达式。本节将把更多细节讲清楚。
当我们希望函数根据某个条件表现不同时,通常会使用 if 或 cond。请注意,这两者在 Racket 中都是特殊形式(special form);我们不用”先把操作数完整求值,再应用运算符”的常规方法来求值它们。
cond 示例
cond 表达式的一般形式是:
(cond (<test1> <result1>)
(<test2> <result2>)
...
(<testn> <resultn>)
(else <default>)) ;; 'else' 分支是可选的
每一对 (<test> <result>) 称为一个子句(clause)。每对的前半部分(<test>)是一个谓词(predicate)——即求值结果必须为真或假的表达式。
求值 cond 表达式时,先求值 <test1>。如果为真,求值并返回 <result1>。如果 <test1> 为假,接着试 <test2>,如此继续,直到没有更多测试。如果遇到 else,就返回与之对应的值(即”默认值”)。
你可以把 cond 表达式写成一串 if 语句:
(if <test1>
<result1>
(if <test2>
<result2>
...
(if <testn>
<resultn>
<default>) ;; 省略了右括号
练习
下面的 plural 函数接受一个单词,返回它的复数形式。例如,(plural 'carrot) 返回 'carrots,(plural 'body) 返回 'bodies。但它对 (plural 'boy) 的处理不正确——它应该返回 'boys,而下面这个带错误的版本返回的是 'boies。
(define (plural wd)
(if (equal? (last wd) 'y)
(word (bl wd) 'ies)
(word wd 's)))
选择哪一行代码填入下面的空白,才能使 (plural 'boy) 表现正确(即返回 boys)?假设 vowel? 和之前一样已经定义好。
(define (plural wd)
(if __________________
(word (bl wd) 'ies)
(word wd 's)))
选项
A. (and (equal? (last wd) 'y) (not (vowel? (last (bl wd))))) —— 如果单词只有一个字母长怎么办?
B. (or (equal? (last wd) 'y)) (> (length word) 1) (not (vowel? (last (bl wd))))) —— 这样任何以 y 结尾的单词都会返回真。
C. (and (equal? (last wd) 'y)) (> (length word) 1) (not (vowel? (last (bl wd))))) —— 没错!
D. (equal? (last wd) 'y) —— 这不就是原来的代码嘛!
谓词与代码风格
谓词是任何返回真或假的表达式。例如 (< 3 4)、(> 10 -2) 和 (= 'apple 'orange)。你可以用 and、or 和 not 组成复合谓词。
下面是一个谓词的例子:
(define (even? x)
(= (remainder x 2) 0))
定义谓词时,惯例是在过程名末尾加上问号(?)。
这是定义 even? 的另一种写法:
(define (even? x)
(if (= (remainder x 2) 0)
#t
#f))
虽然这个定义与上面的定义等价,但它含有冗余。我们强烈建议你不要这样写代码。冗余的代码会让程序更难理解,通常被认为是糟糕的编程风格。
练习
我们要定义一个过程,接受三个数作为实参,返回其中较大两个数的平方和。
例如,(max-sum-squares 1 2 3) 返回 13,即 4 + 9。
为什么下面这段代码不正确?
(define (square x) (* x x))
(define (max-sum-squares a b c) (max (+ (square a) (square b)) (+ (square b) (square c)) (+ (square a) (square c))))
选项
A. 对于小于 1 的实参,这个定义会出问题。—— 答对了!
B. 这个定义不行,因为 max 只接受两个实参。—— 不对!max 可以接受任意多个实参。
C. 这个定义不行,因为 square 需要定义在 max-sum-squares 的过程体内部。—— 因为 square 定义在所谓的全局环境中,我们可以在任何过程的过程体内使用它。
练习题: 写一个过程 pigl,接受一个单词作为实参,返回它的 Pig Latin(儿童黑话)形式。Pig Latin 的规则如下:
如果输入的单词以元音开头,就在末尾加上 “ay”。
如果输入的单词以辅音开头,就把开头的所有辅音移到词尾,再在末尾加上 “ay”。
例如:
(pigl 'hello) ; ellohay
(pigl 'open) ; openay
(pigl 'scheme) ; emeschay
如果我们的输入没有元音,比如 (pigl 'my),该怎么办?确保你的 pigl 会检查单词是否没有元音,如果没有,就直接返回该单词。
(pigl my) ; my
用上面的例子在 Racket 解释器中检验你的答案吧!