本书是来自STL之父的大作,作者的观念是编程源于数学,没有好的数学基础,只能玩玩程序的小技巧,很难写出STL这样伟大的程序,书的英文名称叫做elements of programming, elements 其实就是事物背后的本质的意思,发人深省。
第一章: 基础
本章作者介绍了一个数学系统,理解这个系统才获取了看懂这本书的钥匙。
过程(Procedure): 改变对象状态的一组指令序列,通俗的来讲就是一个函数,比如比较相等不相等,析构赋值等对对象操作的函数都叫过程。
规范类型:相等检查,赋值,析构,默认构造,拷贝,全序判断,基础类型。这个和设计C++的class的概念不谋而合。
规范过程(Regular):就是保证输入输出的一致性的过程,理解这个概念要从它的反面了解,比如返回对象的指针就是一个非规范类型,因为相同的对象可能返回不同的指针,返回当前的时间,返回随机数。
函数过程(functional procedure):是在规范类型的基础上定义的规范过程,有多个参数和一个返回值,输入分成两大类,一类是数值的直接传递,一类是间接传递,比如引用指针。
类型函数:一个类型到另一个从属类型的映射,比如T到T*的映射。
下面介绍一些数学术语
F:过程;
Arity(F):过程的输入参数个数;
Codomain(F): 这个函数映射表明,F过程的返回的结果的类型;
InputType(F, i): 返回其第i个参数的类型;
下面描述一个一元函数过程:
UnaryFunction(F)=
FunctionalProcedure(F)
^Arity(F)=1
^Domain: UnaryFunction->Regular
F->InputType(F,0)
下面描述一个同源的函数过程:(同源函数的意思就是输入类型都是同一参数)
HomogenenousFunction(F)=
FunctionalProcedure(F)
^Arity(F) > 0
^({i,j,N})(i,j<Arity(F))=>(InputType(F,i)=InputType(F,j)) // {i,j,N} i j 都是自然数(N)
^Domain: HomogenenousFunction->Regular
F->InputType(F, 0)
第二章 变换和其轨道(Orbit)
这里的轨道指的是一系列持续的变换过程组成的轨道的意思。
Predicate(P)= // 谓词: 就是返回Bool的函数
FunctionalProcedure(P)
^Codomain(P) = bool
HomogeneousPredicate(P)= // 同源谓词
Predicate(P)
^HomogeneousFunction(P)
运算是一种同源函数,这是很显然的定义
Operation(Op)=
HomogeneousFunction(Op)
^Codomain(Op) = Domain(Op) // 运算的值域等于定义域
Transformation(F) = // 变换,一种值域等于定义域的一元运算
Operation(F)
^UnaryFunction(F)
^DistanceType:Transformation->Interger // Distancetype 就是变换的作用域
变换的轨道就是从一个元素出发,反复的使用同一个变换所能达到所有的点的集合。
若x=f^n (x), 就是说x在f下面是环路的(cyclic),如果x不在f的定义域之内,那么x就是f的中止点(terminal).
变换的轨道有4种:1. 无穷 2. 中止 3. 环路 4.P 型(勺子形状的轨道)
碰撞点(快慢车):用来测试轨道的大致形状,y=f^n (x)=f^2n+1 (x)
第三章 可结合的运算
二元运算
BinaryOperation(Op)=
Operation(Op)
^Arity(Op)=2
很多二元元算都是可结合的,比如加法乘法之类的
下面利用可结合性来优化幂的运算,下面的算法可以讲幂的运算提高到logN,注意这里不是2倍
a^n=a (n=1)
a^n=(a^2)^(n/2) (n 是偶数)
a^n=(a^2)^(n/2)a (n是奇数,这里的n/2要向上取整)
template<typename I, typename Op> requries(Integer(I)&&BinaryOperation(Op)) Domain(Op) pow_0(Domain(Op), I n, Op op){ if(n==I(1)) return a; if(n%I(2) == I(0)) return pow_0(op(a,a), n/I(2),op); return op(pow_0(op(a,a),n/I(2),op),a); }优化:除去递归,书中详细介绍了如何一步步的分析去除递归,这个方法很有用,但是我现在这里指写一下最后的优化结果。
template<typename I, typename Op> requires(Integer(I)&&BinaryOperation(Op)) Domain(Op) power_accumulate_positive(Domain(Op) r, Domain(Op) a, I n, Op op) { while(true){ if(odd(n)){ r=op(r,a); if(one(n)) return r; } a = op(a,a); n = half_nonnegative(n); } } template<typename I, typename Op> requires(Integer(I)&&BinaryOperation(Op)) Domain(Op) pow_accumulate(Domain(Op) r, Domain(Op) a, I n, Op op){ if(zero()) return r; return power_accumulate_positive(r, a, n, op); } template<typename I, typename Op> require(Integer(I)&&BinaryOperation(Op)) Domain(Op) power(Domain(Op) a, I n , Op op){ while(even(n)){ a=op(a,a); n=half_nonnegative(n); } n=half_nonnegative(n); if(zero(n)) return a; return power_accumulate_positive(a, op(a,a),n,op); }
template<typename I, typename Op> requires(Integer(I)&&BinaryOperation(Op)) Domain(Op) power(Domain(Op) a, I n, Op op, Domain(Op) id){ if(zero(n)) return id; return power(a,n,op); }
上面把最基本的类型都函数化了,因为这部分可能使用一些更好的办法来优化比如位运算。
线性递归就是递归的规则是线性的。
下面讲一个算Fiduccia的例子,算法很先进,理解起来有难度。
F={{1,1},{1,0}}; F^n={{fn+1, fn},{fn, fn-1}}
template<typename I> requires(Integer(I)) pair<I, I>fibonacci_matrix_multiply(const pair<I, I>&x, √const pair<I, I>&y ){ return pair<I, I>(x.m0*(y.m1+y.m0)+x.m1+y.m0, x.m0*y.m0+x.m1*y.m1); } template<typename I> requires(Integer(I)) I fibonacci(I n){ if(n==I(0)) return I(0); return power(pair<I, I>(I(1), I(0)), n,fibonacci_matrix_multiply<I> ).m0; }