2.1 变换
虽然从任意一组类型到任意类型的函数都存在,但是有些具有特殊签名的函数类更为常用.本书中经常用的是两类函数:同源谓词和同源运算.同源谓词在形式上都是Tו••× T → bool;同源运算都是形如Tו••× T → T的函数.一般而言存在着任意的n元谓词和n元运算,但实际中遇到最多的还是一元和二元的同源谓词,一元和二元的同源运算.
谓词是返回真值的函数:
Predicate(P).FunctionalProcedure(P)∧Codomain(P)=bool
同源谓词也是同源函数:
HomogeneousPredicate(P).
Predicate(P)∧HomogeneousFunction(P)
一元谓词只有一个参数:
UnaryPredicate(P).Predicate(P)∧UnaryFunction(P)
运算是同源函数,其值域与作用域相同:
Operation(Op).HomogeneousFunction(Op)∧Codomain(Op)=Domain(Op)
下面是几个同源运算的实例:
int abs(int x) {
if (x < 0) return -x; else return x;
} //一元运算
double euclidean norm(double x, double y) {
return sqrt(x * x + y * y);
} //二元运算
double euclidean norm(double x, double y, double z) {
return sqrt(x * x + y * y + z * z);
} //三元运算
引理2.1euclidean_norm(x,y,z)=euclidean_norm(euclidean_norm(x,y),z)
这一引理说明上面的三元运算可以从相应的二元版本得到.但是,由于效率,表达方便,或者准确性等原因,这样的三元运算也可以纳入处理三维空间的程序的计算基.
如果一个过程的定义空间是其输入类型的直积的子集,就称该过程为部分的(partial);如果其定义空间等于其输入类型的直积,则说它是全的.按标准的
数学习惯,我们也认为部分过程包括全过程,并称那些不是全的部分过程为非全的(nontotal).由于计算机表示的有穷性,有些全函数在计算机里的实现却是非全的.例如,有符号的32位整数加法就是非全的.
一个非全过程需要有一个描述其定义空间的前条件.要验证对这个过程的一个调用是正确的,必须确定所给的实参满足这一前条件.有时我们会把部分过程作为参数传给一个算法,因此需要在运行时确定这种过程参数的定义空间问题.为了处理这类情况,我们将定义一个定义空间谓词(de.nitionspacepredicate),它与这一过程参数的输入相同,当且仅当实际输入在这个过程的定义空间里时,让这个谓词返回真.在调用一个非全过程之前,或者其前条件必须满足,或者该调用是用这个过程的定义空间谓词保护的.
练习2.1请为32位有符号整数的加法实现一个定义空间谓词.
本章研究一元运算,我们称其为变换(transformation):
Transformation(F).
Operation(F)
∧UnaryFunction(F)
∧DistanceType:TransformationInteger
→
这里的DistanceType将在下一节讨论.
变换可以自组合(selfcomposable),例如可以写f(x),f(f(x)),f(f(f(x))),等等.f(f(x))的定义空间是f的定义空间和结果空间的交.这种自组合能力和检查相等能力的结合,使我们可以定义许多有趣的算法.
设f是一个变换,它的幂(power)定义如下:
.
.x if n = 0, fn(x)=.
.fn.1(f(x))ifn>0
.
要实现计算fn(x)的算法,就要描述对一个整数类型的需求.第5章将研究描述与整数有关的一些概念,现在我们暂时依靠对整数的一些直观理解.有符号和无符号的整数类型都是整数的模型,还有任意精度的整数等,它们都包含下面的运算和文字量:
18 第2 章变换及其轨道
其中的I是一个整数类型.
这样就可以写出下面算法:
template requires(Transformation(F) && Integer(N)) Domain(F) power unary(Domain(F) x, N n, F f)
{
// 前条件: n . 0 ∧ (.i ∈ N) 0 while (n != N(0)) {n = n -N(1);x = f(x);
}
return x;
}