布尔值与谓词

介绍布尔值 #t 与 #f、常用谓词、逻辑运算符 and/or/not 等,以及「非假即真」的真值规则。

布尔值:真与假

布尔值(boolean)的严格定义是:只有”真”和”假”两种可能取值的二元变量。布尔值在表达条件表达式(conditional)时极其有用——条件表达式就是根据测试结果选择行动的指令。一个符合逻辑的例子是:如果我们没有牛奶了,就去商店;否则,把牛奶加进麦片里,享用吧。

为了测试我们是否没有牛奶了,我们需要用到布尔值。Racket 中的”真”用 #t 或 true 表示,“假”用 #f 或 false 表示。

-> (= 1 1)
#t

-> (= 1 2)
#f

> (if #t
    '(the condition was true)
    '(the condition was false))
(the condition was true)

> (if #f
    '(the condition was true)
    '(the condition was false))
(the condition was false)

> (if (= 1 1)
    '(the condition was true)
    '(the condition was false))
(the condition was true)

谓词

调用时返回 true 或 false 的函数称为谓词(predicate)。例如,even? 就是一个用来测试数是否为偶数的谓词。

-> (even? 2)
#t
-> (even? 3)
#f

谓词永远不会返回 #t 或 #f 以外的值。下面列出了一些 Racket 中实用的预定义谓词。这个列表并不全面,在以后的课程中你肯定还会发现更多谓词。

数学运算符

Racket 提供了比较数值所需的标准数学运算符:

  • < 当第一个实参小于第二个时返回 #t。
  • > 当第一个实参大于第二个时返回 #t。
  • = 当两个实参相等时返回 #t。
  • <= 当第一个实参小于或等于第二个时返回 #t。
  • >= 当第一个实参大于或等于第二个时返回 #t。

警告

这些谓词只对数有效。用它们比较词、句子或任何其他类型的值都会产生错误。

-> (= (+ 3 3) 6)
#t
-> (= 'foo 'foo)
; =: contract violation
;   expected: number?
;   given: 'foo
;   argument position: 1st
; [,bt for context]

member?

member? 接收一个字母和一个词时,若词中包含该字母则返回 #t,否则返回 #f。member? 接收一个词和一个句子时,若句子中包含该词则返回 #t,否则返回 #f。

-> (member? 'a 'aeiou)
#t
-> (member? 'b 'aeiou)
#f

-> (member? 'foo '(foo bar baz))
#t
-> (member? 'foobar '(foo bar baz))
#f

empty?

谓词 empty? 接收一个任意类型的实参,若实参是空词 "" 或空句子 '() 则返回 #t,否则返回 #f。

-> (empty? "")
#t
-> (empty? 'foo)
#f

-> (empty? '())
#t
-> (empty? '(foo bar baz))
#f

-> (empty? 3)
#f

equal?

equal? 接收两个任意类型的实参,若它们相同则返回 #t,否则返回 #f。

-> (equal? (+ 1 1) 2)
#t
-> (equal? 3 1)
#f

-> (equal? 'foo 'foo)
#t
-> (equal? '(foo bar baz) '(foo bar baz))
#t

-> (equal? + +)
#t

类型检查器

Racket 还提供了一类谓词,用于检查值是否属于某种特定类型:

  • number? 检查值是否为数。
  • word? 检查值是否为词。
  • sentence? 检查值是否为句子。
  • boolean? 检查值是否为布尔值。

用作谓词的复合过程

你完全可以创建自己的谓词,因为它们本质上就是过程。例如:

(define (vowel? letter)
  (member? letter 'aeiou))

vowel? 检查实参字母是否为元音。

检验理解

注意: 这是作业中的练习 2。

写一个名为 teen? 的谓词,接收一个数作为实参,若该数在 13 到 19 之间(含两端)则返回 #t。

非假即真

判断一个表达式为真还是为假时,务必记住:除非为假,任何值都算真。这意味着所有的数、词、句子和过程都是真的,包括 ""、'() 和 0。下面是一些违反直觉的例子,不妨思考一下:

-> (false? "") ;; "" 是假吗?
#f ;; 不,不是
-> (false? '())
#f
-> (false? 0)
#f
-> (false? false?) ;; 过程 false? 是假吗?
#f ;; 不,这个过程本身不是假

逻辑运算符

我们还可以在 Racket 中使用逻辑运算。

and

and 是一个可接收任意数量、任意类型实参的谓词。如果所有实参都不为假,它返回最后一个实参;否则返回 #f。例如:

-> (and 1 2 3)
3
-> (and (= 1 1) (member? 'a 'aeiou))
#t
-> (and (number? 'hi) 2 3)
#f

or

or 是一个可接收任意数量、任意类型实参的谓词。它返回第一个不为假的实参;如果所有实参都不为真,则返回 #f。例如:

-> (or (even? 4) (= 1 1))
#t
-> (or 1 #f 2)
1
-> (or (even? 1) #f (number? 'foo))
#f

not

not 接收一个任意类型的实参,简单地对它取反。例如:

-> (not #f)
#t
-> (not #t)
#f
-> (not 3)
#f
-> (not (and (and 3 3) (or #f #f)))
#t

nand

nand 等价于 (not (and ...。例如:

-> (nand #f #t)
...(not (and #f #t))
...(not #f)
#t

nor

nor 想必你已经猜到了,等价于 (not (or ...。例如:

-> (nor #f #t)
...(not (or #f #t))
...(not #t)
#f

xor

xor 接收两个任意类型的实参,若恰好只有一个(不多不少)实参不是 #f,则返回该实参;否则返回 #f。

-> (xor 11 #f)
11
-> (xor #f 11)
11
-> (xor 11 22)
#f
-> (xor #f #f)
#f