本节要点:
- TLA+中,
f[x∈S]≜e是
(6.3)
f≜CHOOSE f:f=[x∈S↦e]的缩写形式;
- TLA+允许我们写“笨定义”,例如,我们可以写
(6.4)
circ[n∈Nat]≜CHOOSE y:y=circ[n],不过在TLA+中,函数
circ的值不确定;
- TLA+不允许相互循环定义。不过,我们可以按TLA+允许的方式这样定义函数
f和
g: 先定义一个函数
mr使得
mr[n]是一个record,它的
f和
g域分别为
f[n]和
g[n]
mr[n∈S]≜[f↦IF n=0THEN 17 ELSE mr[n−1].f∗mr[n].g,g↦IF n=0THEN 42 ELSE mr[n−1].f+mr[n−1].g]
之后我们可以根据
mr将
f和
g定义为:
f[n∈Nat]≜mr[n].fg[n∈Nat]≜mr[n].g这个小技巧可以将任何相互循环函数定义转换为 定义一个独立的record-valued 循环定义函数,这个函数的域即为想要相互循环定义的函数。
-第5.5章有引入递归函数定义。我们现在看一下这些定义的数学含义。数学家们将阶乘函数定义如下:
fact[n]=IF n=0 THEN 1 ELSE n∗fact[n−1],foralln∈Nat上述通常被认为是唯一定义了
Nat域上的函数
fact,换句话说,
fact是唯一满足
(6.1)
fact[n]=IF n=0 THEN 1 ELSE n∗fact[n−1]公式的值。
5.1节P47-48页引入的
CHOOSE 操作符, 让我们可以用
CHOOSE x:p表达 “选个x满足属性p”的需求。因此我们定义
fact如下:
(6.2)
fact≜CHOOSE fact:fact=[n∈Nat↦IF n=0 THEN 1 ELSE n∗fact[n−1]
(因为符号
fact在
≜右侧引入之前尚未在表达式中有定义,我们只能在
CHOOSE表达式中将它用做绑定标识符)。TLA+的定义
fact[n∈Nat]≜IF n=0 THEN 1 ELSE n∗fact[n−1]只是(6.2)的缩写形式。再推广一下,
f[x∈S]≜e是
(6.3)
f≜CHOOSE f:f=[x∈S↦e]的缩写形式。
TLA+允许我们写“笨定义”,例如,我们可以写
(6.4)
circ[n∈Nat]≜CHOOSE y:y=circ[n],意思是 对任意自然数
n, 定义一个函数
circ,使得
circ[n]=circ[n]。 很显然没有这样的函数,所以
circ不可能定义出来。在一个递归函数定义中不是很有必要再定义一个函数。如果没有函数
f等于
[x∈S↦e], 则(6.3) 定义
f为一个不确定的值,这样,(6.4)也将
circ定义为一个未知数。
虽然TLA+允许递归函数定义具有明显的循环性,但它不允许循环定义(在循环定义中,两个或多个函数是相互定义的)。数学家们偶尔会写这样的循环定义。例如,他们会这样定义自然数域上的函数
f和
g:
f[n]=ifn=0then17elsef[n−1]∗g[n],g[n]=ifn=0then42elsef[n−1]∗g[n−1]
TLA+不允许相互循环定义。不过,我们可以按TLA+允许的方式这样定义函数
f和
g:先定义一个函数
mr使得
mr[n]是一个record,它的
f和
g域分别为
f[n]和
g[n]
mr[n∈S]≜[f↦IF n=0THEN 17 ELSE mr[n−1].f∗mr[n].g,g↦IF n=0THEN 42 ELSE mr[n−1].f+mr[n−1].g]
之后我们可以根据
mr将
f和
g定义为:
f[n∈Nat]≜mr[n].fg[n∈Nat]≜mr[n].g
这个小技巧可以将任何相互循环函数定义转换为 定义一个独立的record-valued 循环定义函数,这个函数的域即为想要相互循环定义的函数。
如果我们想推导出由
f[x∈S]≜e定义的函数
f,我们需要证明存在一个
f等于
[x∈S↦e]。
如果
f没有出现在
e中,
f的存在性是很明显的。如果它出现了,那么这就是一个递归定义,然后就需要得到证明。不过本书中我们不讨论如何证明它。直观上,你必须检查一下,就像在阶乘函数的情况下,这个定义唯一地确定了
S中每一个
x对应的
f[x]的值。
递归是一种常见的编程技术,因为程序必须使用少量简单基本的操作来计算数值。它在数学定义中不常用,在数学定义中我们不必担心如何计算数值,因为可以使用强大的工具如逻辑运算符和集合论来计算。例如,在5.4节中定义了操作符Head、Tail和o,但没有使用递归(尽管计算机科学家通常使用递归的方式来定义它们)。尽管如此,使用递归函数定义还是可以最好地归纳定义某些东西。