我们程序员的主要工作就是将自然语言翻译为形式语言
•自然语言
–人类所说的语言,如汉语,英语,法语等
–有语法和语义
–多义性,环境不同,意义就不同
–冗余性,语法错了一点也没关系
•形式语言
–用精确的数学或机器可处理的公式定义的语言,例如计算机编程语言
–语法非常严格
计算机语言还分为高级语言和低级语言
•高级语言
–Java,Python, C, C++, Perl , Ruby , C#, VB.NET等
–编程容易、维护容易,具备可移植性
•低级语言
–汇编语言 和机器语言一一对应,不可移植
–机器语言
有两种执行方式:
解释执行:解释器读入高级语言写的指令,然后解释执行 代表:Basic
编译执行:使用编译器把高级语言变成目标代码,后者可执行的程序 代表:C, C++
将上面两种执行方式混合在一起执行就具备了移植性,代表:java虚拟机
要是不混合执行,就需要在不同型号的机器上有不同的版本,例如QQ就分为windows版,手机版,平板版,mac版...
而混合执行后只需要在一开始准备好不同的jvm,java文件编译为统一的.class文件就可以在不同的系统上运行了
静态语言 VS 动态语言
Java:
ArrayList list = new ArrayList();
list = new Vector(); //编译错误,类型不匹配
Ruby:
p = Person.new;
p = Animal.new; //编译正确
静态语言:
优点:具备代码感知能力,在编译器可以检查出很多错误
缺点:类型确定后无法改
动态语言:
优点:很灵活,表达能力强
缺点:编译期无法检查,错误会在运行期报出
编译的具体步骤:
词法分析:
语法分析:形成语法树
如何定义语言
以java为例:
语义分析:
代码生成:
递归:
计算n的阶乘: 迭代vs递归
要想理解递归需要理解进程在内存总的布局:
每一个栈帧代表着一个函数的调用
具体的阶乘函数的栈帧:
具体的运行过程
若栈帧太多可能导致栈空间的溢出
解决方法:将递归改成尾递归
尾递归可以复用同一个栈帧
对于尾递归, 现代的编译器就会发现这个特点,生成优化的代码, 复用栈帧。 第一个算法中因为有个n* factorial(n-1) ,虽然也是递归,但是递归的结果处于一个表达式中, 还要做计算, 所以就没法复用栈帧了。
学自:“码农翻身”微信公共号