词与句子

学习 Racket 中词与句子的创建、quote 特殊形式,以及 first、butfirst、word、sentence 等词句操作过程。

导言

一想到函数,我们就会自动联想到数学和数字。事实上,在 Racket 和任何其他函数式编程语言中,我们可以编写操作非数值数据的函数。

词

假设你定义了一个名为 square 的过程:

(define (square x) (* x x))

但之后你想访问实际的词 'square 而不是那个过程,这时只需输入 'square(单引号后面跟词 square),就能得到字面词(literal word)。注意,处理单个词时,不需要在表达式外面加括号。

句子

句子(sentence)就是用括号组合在一起的一组词。创建句子时,需要在括号外面加一个引号,像这样:'(hi hey hello)。试着写一两个词和句子来练习一下。

检验理解

在 Racket 解释器中尝试以下每一个。

'61AS

'(I love 61AS!)

('I 'love '61AS!)

quote

你在上面几节看到的 ',实际上是名为 quote 的过程的缩写。这意味着:

  • 'x 等价于 (quote x)
  • '(hi hey hello) 等价于 (quote (hi hey hello))

quote 与大多数其他过程不同,它不求值自己的实参。表现出这类行为的函数称为特殊形式(special forms)。你现在还不需要理解特殊形式;后面的小节会更深入地讨论这个话题。目前只需知道:quote 是一个接收一个实参、并将其作为词或句子返回的函数。看下面的例子:

-> (define x 4)
x
-> x
4
-> (quote x)
x
-> 'x
x

由于 quote 使用非常频繁,它被赋予了缩写形式 ',即一个单引号。请记住,尽管缩写形式看起来不太像,quote 只是一个过程,可以像 Racket 中其他函数一样调用。

词和句子的选择器

处理词和句子时,如果能有一些操作它们的过程就好了。这些过程本身很简单,难点在于如何正确地组合它们来达成你的目标。下面列出了一批可以从词或句子中选取数据的过程,供你使用。

first

first 接收一个词,返回这个词的第一个字母;或接收一个句子,返回句子的第一个词。

-> (first 'hello)
'h
-> (first '(hi hey hello))
'hi

last

last 接收一个词,返回这个词的最后一个字母;或接收一个句子,返回句子的最后一个词。

-> (last 'hello)
'o
-> (last '(hi hey hello))
'hello

butfirst 或 bf

butfirst(缩写为 bf)接收一个词,返回除第一个字母外的所有字母;或接收一个句子,返回除第一个词外的所有词。

-> (butfirst 'hello)
'ello
-> (bf 'hello)
'ello
-> (butfirst '(hi hey hello))
'(hey hello)
-> (bf '(hi hey hello))
'(hey hello)

butlast 或 bl

butlast(缩写为 bl)接收一个词,返回除最后一个字母外的所有字母;或接收一个句子,返回除最后一个词外的所有词。

-> (butlast 'hello)
'hell
-> (bl 'hello)
'hell
-> (butlast '(hi hey hello))
'(hi hey)
-> (bl '(hi hey hello))
'(hi hey)

item

item 接收一个数 n 和一个词,返回词中第 n 个字母;或接收一个数 n 和一个句子,返回句子中第 n 个词。

-> (item 2 'hello)
'e
-> (item 2 '(hi hey hello))
'hey

检验理解

先猜猜 Racket 对以下表达式会输出什么,再用 Racket 解释器核对答案。

(first '(foo foo))

(bf '(foo foo))

(equal? (first '(foo foo)) (bf '(foo foo)))

equal? 是一个检查两个元素是否相同的函数。

词和句子的构造器

现在我们学会了拆开词或句子,再来学习怎么把它们组合起来。

word

word 接收任意数量的词作为实参,把它们拼接成一个大词。

-> (word 'play 'ground)
'playground
-> (word 'fo 'o 'b 'ar)
'foobar
-> (word 'cs '61 'as)
'cs61as

sentence 或 se

sentence(缩写为 se)接收任意数量的词或句子作为实参,把它们组合成一个句子。

-> (sentence 'I 'love 'cs '61as!)
'(I love cs 61as!)
-> (se 'foo 'bar)
'(foo bar)
-> (se 'foo '(foo bar) 'bar)
'(foo foo bar bar)

空词

有一个空词,可以与其他词组合,使用时不会产生任何影响。它用 "" 表示。

-> (word 'foo "")
'foo
-> (word "" 'foo)
'foo
-> (word "" "")
""

空句子

也有一个空句子,可以与其他句子组合,使用时不会产生任何影响。它用 '() 表示。

-> (se 'hi 'there '())
(hi there)
-> (se '() 'hi 'there)
(hi there)
-> (se 'hi '() 'there)
(hi there)
-> (se '() '() '())
'()

目前你可能还不太清楚为什么需要这些空词和空句子。先记住它们,等第 0-3 课学习递归时,它们会非常有用。

检验理解

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

我们来定义一些处理词和句子的函数。second 过程由我们提供——它返回词中的第二个字母,或句子中的第二个词。

(define (second item)
    (first (bf item)))
  1. 写一个过程 first-two,接收一个词作为实参,返回由实参前两个字母组成的双字母词。
  2. 写一个过程 two-first,接收两个词作为实参,返回由这两个实参的首字母组成的双字母词。
  3. 再写一个过程 two-first-sent,接收一个双词句子作为实参,返回由这两个词的首字母组成的双字母词。

易错点

处理词和句子时,基本上唯一可以使用的标点符号是 ! 和 ?。你已经看到,引号 ' 在 Racket 中有特殊含义。句号和逗号也有特殊含义,所以同样不能使用。

正如你在前面的练习中看到的,词和只含一个词的句子是有区别的。例如,人们常常误以为 (computer science) 这样的双词句子的 butfirst 是 'science。实际上,它是一个只含一个词的句子:(science)。另一种证明词与单词句子区别的方法是对两者使用 count:

-> (bf '(computer science))
'(science)

-> (count (bf '(computer science)))
1 ;; 因为句子里只有一个词。

-> (first (bf '(computer science)))
'science

> (count (first (bf '(computer science))))
7 ;; 因为词 'science 有七个字母。

要点总结

  • 我们可以分别用 word 和 sentence 构造词和句子。
  • 我们也可以用引号来构造词和句子。
  • 我们可以用 first、butfirst、last 和 butlast 等过程获取词或句子的组成部分。