计算

Last updated 27 days ago

以后你们是要跟计算机打交道的。

计算机(Computer)是干嘛的?当然是计算(Computing)啦。

#include <stdio.h>
int main(){
printf("%d", 1+1);
}

如果你还记得在环境搭建的时候我给你说的printf的话,现在你看到的就是printf真正的玩法了。

上面那段代码中"%d"就是格式化字符串,意思是说,按照十进制(decimal)的方式输出它后面的那个东西。

所以,编译运行,然后你会在黑框框里面看到2。

没错,这就是计算嘛。

然而,有什么用呢?

计算

简(pian)单(mian)地讲,一切问题都能抽出来一套数学模型,同样能够抽出来的数学模型都可以转化成计算问题,然后让计算机来解决。

所以计算可以看成是整个程序设计的基础。同样像我们之前提到的,计算机,本身也就是拿来做计算的。

那么我们都可以做哪些计算呢?

当然普通的+×()÷(/)+ - \times(*) \div(/)不用说,你还可以把一些复杂的计算规则定义成函数:

int pow5(int num) {
return num * num * num * num * num;
}

比如pow5的意思就是求一个数的5次方,当然计算方式就是让5个该数字相乘。int的意思是integer,就是整数的意思,当然,由于实际实现上的原因,这里的整数跟数学上的整数的概念是有区别的,后面我们会提到。

我们定义了一个函数,怎么用呢?

还是回到之前的代码:

#include <stdio.h>
int pow5(int num) {
return num * num * num * num * num;
}
int main(){
printf("%d", pow5(1));
}

嗯,没错,输出了我们期望的结果(1)。

组合

什么是组合呢?

想想我们学习函数和多项式的时候,

f(x)=x2+2x+1f(x) = x^2 + 2x + 1
g(x)=(x+3)(x5)g(x) = (x+3)(x-5)

然后我们要定义一个h(x)=f(g(x))h(x) = f(g(x)),那么定义出来的h(x)h(x)长什么样呢?

我们首先要把g(x)g(x)代入f(x)f(x)

f(g(x))=g(x)2+2g(x)+1f(g(x)) = g(x)^2 + 2g(x) + 1

f(g(x))=[(x+3)(x5)]2+2(x+3)(x5)+1f(g(x)) = [(x+3)(x-5)]^2 + 2 * (x+3)(x-5) + 1

好吧我编不下去了。总之展开来以后很丑的。

而且如果以后遇到σ(x,y,z)\sigma(x,y,z)θ(a,b,c)\theta(a,b,c)组合的,或者是更多参数的函数组合,结果更是可想而知。

但是在C++里面我们并不用担心这个问题。

比如我们有两个函数

int f(int x) {
return x*x + 2*x + 1;
}
int g(int x) {
return (x+3) (x-5);
}

那么我们的h(x)h(x)可以很简单的写成这个样子:

int h(int x) {
return f(g(x));
}

或者你也可以不用专门去定义,在用到的时候直接用就可以:

printf("%d", f(g(5)));
// or
printf("%d", h(5));

组合的意义

比如我们可以考虑一个更复杂的函数,

σ(x,y)=x2+xy+(x+y)×5\sigma(x,y) = x^2 + x*y + (x+y)\times5

x,yx, y 分别为 4x+1,5x34x+1,5x-3的时候。你可以试着自己去展开一下这个算式:

σ(4x+1,5x3)(4x+1)2+(4x+1)(5x3)+(4x+1+5x3)×5\sigma(4x+1, 5x-3) = (4x+1)^2 + (4x+1)(5x-3)+(4x+1+5x-3)\times 5

总之,无论他有多长,都会在算式里面充斥着4x+14x+15x35x-3,而且,计算机并没有那么聪明,所以只好按照特定的顺序一次次的计算。

于是4x+14x+1被重复计算了三次,5x35x-3被重复计算了两次。对于pow5那种情况,更是会被求值5次。

令人欣慰的是,组合帮我们保证了这个问题不会有这些重复。

当你计算pow5(3+1)的时候,C++并不会去展开成(3+1)*(3+1)...*(3+1)这种形式,而是直接变成了pow5(4),最后的结果是1024。

也就是说,函数的每个自变量(编程语言里面我们会把他们叫做参数(argument))会先被求值,得到他们的值以后才会带入计算。

*请记住我们现在讨论的这个问题,后面会展开并且深入地探讨。

练习

  • 请思考,如果函数的参数在代入函数之前并不进行强制求值,pow5(pow5(pow5(pow5(5))))展开后共计算多少次乘法。

  • 强制求值可能会造成的问题有哪些。