表达式与求值

介绍函数式编程思想、Racket 表达式的前缀表示法与嵌套,以及应用序求值规则。

导言

在本小节中,你将进一步学习函数式编程,还将学习表达式和求值。

函数式编程

在深入 Racket 语言本身之前,我们先来看看本课这个看似简单的”大思想”。简言之:求值一个表达式(expression)时,可以把一个函数返回的值用作另一个函数的实参(argument)。通过这样把两个函数”连接”起来,我们就发明了一个新的、第三个函数。举个例子,假设我们有一个函数,作用是在词的末尾加上字母 s(以伪代码表示):

add-s("run") = "runs"

还有一个函数,把两个词组合成一个句子:

sentence("day", "tripper") = "day tripper"

我们可以把这两个函数组合起来,创造一个新函数,用来表示动词的第三人称单数形式:

third-person(verb) = sentence("she", add-s(verb))

把这个通用公式用在某个具体的动词上,就是这样的:

third-person("sing") = "she sings"

用 Racket 来写则是这样:

(define (third-person verb)
  (sentence 'she (add-s verb)))

如果你觉得这一点令人困惑或违反直觉,别担心;关于这个概念,你会有大量的练习。尽管如此,我们将会看到,通过这种方式把函数连接起来,可以表达种类繁多的计算算法。这种”连接”就是我们所说的函数式编程。

表达式

大思想: 你可以向 Racket 提”问题”,这些问题称为表达式(expression)。Racket 解释器随后会”思考”你的问题,也就是求值(evaluate)你的表达式。然后你会得到答案,称为值(value)。我们输入 Racket 的所有内容(只要不报错)都是表达式。

当你希望 Racket 做 某件事时(例如把两个数相加),你要用前缀表示法(prefix notation)写一个表达式。尽管所有不报错的输入都是表达式,但最有意思的那类是过程调用(call to a procedure)。看看下面的例子:

(+ 3 4)

在这个例子中:

  • + 是过程(procedure),即表达式的运算符(operator)
  • 3 是 + 的实参,即表达式的操作数(operand)
  • 4 同样是一个实参/操作数

这种语法允许我们嵌套(nest)表达式:

(* (max 2 3) (/ 8 4))
  • *、max 和 / 都是过程
  • * 是大表达式的运算符,而 (max 2 3) 和 (/ 8 4) 是大表达式的操作数
  • max 是第一个子表达式的运算符,而 2 和 3 是第一个子表达式的操作数
  • / 是第二个子表达式的运算符,而 8 和 4 是第二个子表达式的操作数

检验理解

以下哪些是合法的 Racket 表达式?选择所有适用的项。

现在,打开你电脑上的 Racket 解释器,亲自尝试一些表达式吧。

求值

Racket 使用应用序(Applicative Order)(第 1 课会讲授)来求值表达式,规则如下:

  1. 求值运算符和操作数
  2. 将运算符应用于操作数

Racket 实际理解和求值表达式的方式相当复杂,我们会在第 11 课讲解。现在,继续进入下一小节吧!