词与句子
学习 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)))
- 写一个过程
first-two,接收一个词作为实参,返回由实参前两个字母组成的双字母词。 - 写一个过程
two-first,接收两个词作为实参,返回由这两个实参的首字母组成的双字母词。 - 再写一个过程
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等过程获取词或句子的组成部分。