本文共 1194 字,大约阅读时间需要 3 分钟。
关于循环优化的理论比较复杂,我们现在暂时切换到一些简单些的优化吧 我们经常在写代码时会使用递归调用,我们知道递归调用通常代价比较大, 但是递归调用比较符合我们的思考方式,写代码比较容易。 其实实际上对于一种比较特殊的递归调用,编译器可以直接帮助我们做优化, 将递归调用直接优化为非递归调用,这一类递归调用在编译器里面称为尾递归调用 (Tail Recursive Call). 比如函数 - int primes(int a, int b){
- if(a==0)return b==1;
- if(b==0)return a==1;
- return primes(b,a%b);
- }
复制代码 像上面的一个代码,只有在代码的最后一行调用函数自身的代码称为尾递归,大部分现代 的编译器都能够对上面的代码进行优化,将递归优化成一个循环,因为其做法只需要修改所有 函数输入值,然后跳到函数入口就可以了。 有些尾递归调用可能不是很容易看出来,比如 - int primes(int a, int b){
- if(a==0)return b==1;
- if(b==0)return a==1;
- if(a<b){
- return primes(a,b%a);
- }else{
- return primes(a%b,b);
- }
- }
复制代码 虽然上面代码有两次递归调用,但是两次都是尾递归,它们分别在两个不同分支的最后面。 此外另外一种比较常见的尾递归调用形式是: - double honic(int n){
- if(n==1)return 1.0;
- else return 1.0/n+honic(n-1);
- }
复制代码 这里,递归调用在一个表达式中间,虽然它是最后一次计算(+操作)的一个参数,但是由于加法 满足结合率,我们可以改变所有累加的顺序,所以上面的编译器也可以自动转化为循环。 同样,计算 - double honic(int n){
- if(n==1)return 1.0;
- else return honic(n-1)+1.0/(n*n);
- }
复制代码 编译器也可以自动优化,(虽然后面还有计算1.0/(n*n),但是编译器可以自动优化,加法满足交换率). 像下面代码 - double Fib(int n){
- if(n<=1)return 1.0;
- else return Fib(n-1)+Fib(n-2);
- }
复制代码 这个代码中,两次递归调用中,只有后面那次调用(Fib(n-2))可以转化为循环的一部分,但是前面那次递归调用无法转化。 所以对这样的代码,最好还是通过手工优化。(其实,这里的函数Fib不含有任何指针之类会引起歧异的代码,从理论上, 编译也应该能够优化掉,但是实际上现在通常的编译器不会做这样的优化) 转载于:https://my.oschina.net/u/218425/blog/51949