<commit_before>---
layout: post
title: "计算机相关技术瞎扯"
description: ""
keywords: computer
category: Programming
tags: [computer]
---

# 前言

试图用通俗的语言聊一聊计算机的相关知识体系，尽可能把重点脉络和原理讲清楚，当然不可能面面俱到，也可能会用到不太准确但更通俗的说法。

# 计算机程序如何执行
## 高低电平映射到01序列，01序列映射到其他
计算机从硬件的角度来看只懂得0和1（高电平、低电平），
然后通过**与非门**等逻辑电路组成更复杂的支持加法或者乘法的运算单元，进行运算，

例如打个比方八根排线，假设0为低电平，1为高电平，`0000 0001`这个最右边的排线假设赋予其一个数字1的含义，
`0000 0010`这个右二为高电平的排序赋予数字2的含义（排线的高低电平 映射到 01序列 映射到 我们认识的整数），
那么`0000 0001`这个电信号与`0000 0010`这个电信号输入到
由 **与非门电路** 组合而成的加法运算器中后，输出排线的电平为`0000 0011`于是得到数字3，
这里将`0000 0010`与2对应，以及其他对应整型数的方式比较流行的是补码，
补码流行的原因是对正数、负数的累加都一样，就不用为了正数、负数单独设计累加器。

除了与非门构成的运算结构，还需要保存一些中间运算结果，硬件名字不记得了，总之是能贮存电平的单元，
运算和贮存，这几乎就是组成计算机的硬件的最基本的原理构架了。

## 01序列映射的其他例子

因为计算机只懂得高电平，低电平，或者说01序列，实际上，我们需要用01序列去表示所有需要的东西，

首先是整型数字，也就是补码，也就是一种01序列映射到整型的一个函数。

然后是字符串，最著名的是[ASCII](https://baike.baidu.com/item/ASCII/309296?fr=aladdin)编码，
例如将65(实际上对计算机而言是 `0100 0001`，65只是人比较好理解的十进制)映射为字母`A`，
所以当计算机看到`0100 0001`时，并且告诉计算机将其理解为ASCII字符时，计算机将其解读为字符`A`并执行接下来的操作，例如打印到屏幕上
（实际上是存储到某个特定的用于显示的内存区间）。
ASCII缺点在于位数太低，不能涵盖中日韩等字符，于是出现了unicode、UTF-8编码等，实际上就是对01序列不同的映射到字符的方式。

然后是浮点数来表示小数，通常采用IEEE-754标准。

而计算机的执行指令，也就是控制计算机对某个数字进行加法、乘法等运算的机器命令，**也是01序列**，
其映射规则称为指令集，
例如[x86指令集](http://blog.csdn.net/liuzhen917/article/details/30465891)，也比较流行的是arm构架指令集。
这些指令集都有对应的，最原始的人可以读懂的方式，称为汇编语言。
注意到不同的机器，如果其指令集不一样，其汇编语言也不一样。

## 计算机如何运算

1. **取指**: 从指令的排线中，读取某一段指令序列。
2. **译码**: 判断要执行的操作，如果需要对数据进行运算还要从数据排线中读取数据（称为操作数）
3. **运算**: 执行运算，将输入数据输入到运算单元
4. **回写**: 保存运算后的数据，将其存到例如内存或者寄存器等地方

然后读取下一段指令序列，不断循环以上四个步骤。
至于下一段指令在哪里，是将指令的位置(地址，对应于C语言的指针)存放到曾为PC寄存器的位置，
每次通过读取PC寄存器，来找到下一段指令的门牌号（地址），执行取指、译码、运算、回写这四个步骤。
通常情况下PC寄存器是按01指令序列的顺序进行读写，但是也有跳转，例如对于C语言的循环或者函数调用就是跳转，
也就是设定PC寄存器内的值，使得下一个执行序列跳转到预期的地方。

这些操作需要保证一定的时序性，因为都共用了同一个数据线，否则后面的信号可能跟前面的信号弄混了，
所以为了保证其时序性，加入一个 *...高低高低高低...* 这样脉动变化的信号作为同步的时钟，
通常说的CPU I5有3.5 GHz 也即是说的这个时钟的频率。

# 计算机语言
计算机只懂得0和1，控制计算机，也就是构成计算机能够执行的正确的01序列，如上所说的x86、ARM等指令集，
这些指令集的文本表示称为汇编语言，也就是最接近计算机指令，同时又映射成人可以读懂的语言。
**汇编语言即使类似与x86这样的机器码的文本表示**，对应人可以读懂的，机器的执行命令，
这些命令绝大多数都是赋值、运算、根据条件跳转这样的机器**可以做**的事情。

但不同机器设计的汇编语言不一样，这样在一台机器上编写的代码，就不能在另外一台机器上跑，
尤其对于早期各类机器而言。

这时出现了一个对汇编语言的执行的某种通用的归纳（抽象），称为C语言，
这样机器虽然不同，但通过C语言的编译器编译成针对这台机器的汇编代码，再编译成机器执行指令的01序列就可以控制机器了。
C语言的最大贡献是对汇编语言执行结构的合理的抽象，同时又方便更高层次地思考问题，而不用太细化到机器指令这一层面，
例如循环结构，对应机器指令而言就是条件跳转，符合某个条件（例如某个数大于0），
就通过改变PC寄存器的值跳到当前执行序列前的某个地方，重复执行这一段01序列。

这样不同的机器都可以共用同一份代码，只要该机器上有C语言编译器就能执行，
通过C语言对汇编语言的抽象，这样就从需要考虑机器编码这一硬件执行过程的苦力中解脱出来，
只需要考虑逻辑问题。
所以总的来说C语言是对汇编的一层抽象，最接近汇编语言，所以执行效率很高且常用于系统编程。

稍微总结一下，所谓编程语言，就是人与机器之间交流的中间语言，我们看到的各种语言都是文本表示，
机器看得懂的是01序列，编译器（例如vc）就是将我们看得懂的文本语言翻译成机器看得懂的01序列。

编译器就是一个翻译官，读某种语言的源代码，理解其内容，映射到目标语言中。
目标语言不仅仅是01序列，也可以是其他，例如将C语言翻译成Fortran语言也可以。
例如本文的撰写格式采用的是markdown语法，通过某个翻译器（不专业的说法），
最终会转化成html格式，然后浏览器理解这段html格式，将其按照内容中的要求将内容显示在显示器上。
这里浏览器实际上是一个**解释器**，不同于**编译器**这样将一段程序翻译成01执行序列或者另一种程序的方式。
浏览器这样的解释器是理解html格式，然后按照其要求执行浏览器程序自己设定的01序列，
html并没有被翻译成01序列，而是通过浏览器代理执行，或者说浏览器成了一个可以理解更高级内容的**虚拟机**。

稍微跑一下题，进一步说，很多程序就是一个理解某个规则的片段并执行的过程，例如excel可以理解的csv文本格式如下:

```csv
Time,H2,CO,CH4,case_example,stages
0.05556,23.35235,3.10185,8.68601,case1,C1S1
0.11111,23.62906,3.55692,7.60854,case1,C1S1
0.16667,24.64287,3.62913,7.69976,case1,C1S1
0.22222,29.64828,5.03771,7.87253,case1,C1S1
0.27778,31.87434,4.77112,7.90599,case1,C1S1
0.33333,32.39345,4.02943,7.54947,case1,C1S1
0.38889,31.42271,3.50293,6.47658,case1,C1S1
0.44444,30.96054,4.52849,6.69443,case1,C1S1
```

就是解析表格内容，由逗号分割不同列，由回车符分割不同行。
这就是csv定义的读取数据的规则或者说语法。
假设Fluent生成的文件不符合tecplot的语法规则，那么tecplot的程序就读不懂Fluent生成的内容。

回到正题，不同的语言有的更接近人，有的更接近机器，例如汇编语言最接近机器，人看汇编语言就会很繁琐，
，例如一个函数调用，在C语言里看很容易一眼看懂，对应到机器码就会看着有些累，编写一个函数调用的机器码更累。
这就带来了编写效率的问题，
早期的语言例如Fortran语言是为了解决机器的执行效率问题，其语法特点方便优化，方便数值计算，
但从现在的角度来说，对人不太友好，例如其函数通常有非常多个参数，且命名很难读懂，
变量作用范围没有加以限制，某个地方出错了可能要找很远才发现问题，很容易出错。
现在的硬件条件已经很丰富了，绝大多数现代语言都是解决人的编写效率问题，

这里有一个问题，有没有执行效率高，同时人编写代码效率高的编程语言呢？
我个人认为，由于机器执行逻辑与人遇到的真实现实问题的差异导致很难实现。
执行效率高意味着对机器的精确控制，而代码编写效率高的一个方面就是尽可能减少考虑细节问题的负担，
而将精力放在现实问题上。

C++想要成为a better C，提供C没有的一些能力，例如对象、模板等，同时尽可能考虑不降低性能损失，
（当然，面向对象的设计风格，C也可以通过函数指针变相地实现，只是会麻烦一点）
可以简单的理解C++是C的超集，目的是让人更有效率地的进行机器底层的开发。
因此现代操作系统、游戏引擎、机器学习后端引擎、数值模拟软件等需要与具体机器打交道、需要考虑性能调优的场景
多采用C++开发。

需要说明的是C++注重于开发效率与优化，添加了模板元编程、面向对象等多个范式，因此不可避免地其语法复杂度较高，
也有各种语法的坑，容易出现一些其他高级语言不会出现的一些意想不到的错误，
所以需要了解其各种坑和细节，我差不多死磕C++一年半，也只能说稍微理解一点其面向对象的内容，
很多编程十年的也不敢说精通C++，其创始人也不敢说完全懂C++，
这些细节和坑对于初步学习编程并没有什么用，所以并不是一个初学编程的好选择。

另外，C++注重效率意味着其编译器设计也很复杂，对于较小的单片机之类的程序而言，其生成的代码可能会有些臃肿，
因此类似单片机之类的小型机器多采用C语言。
<commit_msg>add some content<commit_after>---
layout: post
title: "计算机相关技术瞎扯"
description: ""
keywords: computer
category: Programming
tags: [computer]
---

# 前言

试图用通俗的语言聊一聊计算机的相关知识体系，尽可能把重点脉络和原理讲清楚，当然不可能面面俱到，也可能会用到不太准确但更通俗的说法。

# 计算机程序如何执行
## 高低电平映射到01序列，01序列映射到其他
计算机从硬件的角度来看只懂得0和1（高电平、低电平），
然后通过**与非门**等逻辑电路组成更复杂的支持加法或者乘法的运算单元，进行运算。

例如打个比方八根排线，假设0为低电平，1为高电平，`0000 0001`这个最右边的排线假设赋予其一个数字1的含义，
`0000 0010`这个右二为高电平的排序赋予数字2的含义（排线的高低电平 映射到 01序列 映射到 我们认识的整数），
那么`0000 0001`这个电信号与`0000 0010`这个电信号输入到
由 **与非门电路** 组合而成的加法运算器中后，输出排线的电平为`0000 0011`于是得到数字3，
这里将`0000 0010`与2对应，以及其他对应整型数的方式比较流行的是补码，
补码流行的原因是对正数、负数的累加都一样，就不用为了正数、负数单独设计累加器。

除了与非门构成的运算结构，还需要保存一些中间运算结果，硬件名字不记得了，总之是能贮存电平的单元，
运算和贮存，这几乎就是组成计算机的硬件的最基本的原理构架了。

## 01序列映射的其他例子

因为计算机只懂得高电平，低电平，或者说01序列，实际上，我们需要用01序列去表示所有需要的东西，

首先是整型数字，也就是补码，也就是一种01序列映射到整型的一个函数。

然后是字符串，最著名的是[ASCII](https://baike.baidu.com/item/ASCII/309296?fr=aladdin)编码，
例如将65(实际上对计算机而言是 `0100 0001`，65只是人比较好理解的十进制)映射为字母`A`，
所以当计算机看到`0100 0001`时，并且告诉计算机将其理解为ASCII字符时，计算机将其解读为字符`A`并执行接下来的操作，例如打印到屏幕上
（实际上是存储到某个特定的用于显示的内存区间）。
ASCII缺点在于位数太低，不能涵盖中日韩等字符，于是出现了unicode、UTF-8编码等，实际上就是对01序列不同的映射到字符的方式。

然后是浮点数来表示小数，通常采用IEEE-754标准。

而计算机的执行指令，也就是控制计算机对某个数字进行加法、乘法等运算的机器命令，**也是01序列**，
其映射规则称为指令集，
例如[x86指令集](http://blog.csdn.net/liuzhen917/article/details/30465891)，也比较流行的是arm构架指令集。
这些指令集都有对应的，最原始的人可以读懂的方式，称为汇编语言。
注意到不同的机器，如果其指令集不一样，其汇编语言也不一样。而且是机器直接执行的指令，
所以其规则是直接在硬件上写死的。

## 计算机如何运算

1. **取指**: 从指令的排线中，读取某一段指令序列。
2. **译码**: 判断要执行的操作，如果需要对数据进行运算还要从数据排线中读取数据（称为操作数）
3. **运算**: 执行运算，将输入数据输入到运算单元
4. **回写**: 保存运算后的数据，将其存到例如内存或者寄存器等地方

然后读取下一段指令序列，不断循环以上四个步骤。
至于下一段指令在哪里，是将指令的位置(地址，对应于C语言的指针)存放到曾为PC寄存器的位置，
每次通过读取PC寄存器，来找到下一段指令的门牌号（地址），执行取指、译码、运算、回写这四个步骤。
通常情况下PC寄存器是按01指令序列的顺序进行读写，但是也有跳转，例如对于C语言的循环或者函数调用就是跳转，
也就是设定PC寄存器内的值，使得下一个执行序列跳转到预期的地方。

这些操作需要保证一定的时序性，因为都共用了同一个数据线，否则后面的信号可能跟前面的信号弄混了，
所以为了保证其时序性，加入一个 *...高低高低高低...* 这样脉动变化的信号作为同步的时钟，
通常说的CPU I5有3.5 GHz 也即是说的这个时钟的频率。

# 计算机语言
计算机只懂得0和1，控制计算机，也就是构成计算机能够执行的正确的01序列，如上所说的x86、ARM等指令集，
这些指令集的文本表示称为汇编语言，也就是最接近计算机指令，同时又映射成人可以读懂的语言。
**汇编语言即使类似与x86这样的机器码的文本表示**，对应人可以读懂的，机器的执行命令，
这些命令绝大多数都是赋值、运算、根据条件跳转这样的机器**可以做**的事情。

但不同机器设计的汇编语言不一样，这样在一台机器上编写的代码，就不能在另外一台机器上跑，
尤其对于早期各类机器而言。

这时出现了一个对汇编语言的执行的某种通用的归纳（抽象），称为C语言，
这样机器虽然不同，但通过C语言的编译器编译成针对这台机器的汇编代码，再编译成机器执行指令的01序列就可以控制机器了。
C语言的最大贡献是对汇编语言执行结构的合理的抽象，同时又方便更高层次地思考问题，而不用太细化到机器指令这一层面，
例如循环结构，对应机器指令而言就是条件跳转，符合某个条件（例如某个数大于0），
就通过改变PC寄存器的值跳到当前执行序列前的某个地方，重复执行这一段01序列。

这样不同的机器都可以共用同一份代码，只要该机器上有C语言编译器就能执行，
通过C语言对汇编语言的抽象，这样就从需要考虑机器编码这一硬件执行过程的苦力中解脱出来，
只需要考虑逻辑问题。
所以总的来说C语言是对汇编的一层抽象，最接近汇编语言，所以执行效率很高且常用于系统编程。

稍微总结一下，所谓编程语言，就是人与机器之间交流的中间语言，我们看到的各种语言都是文本表示，
机器看得懂的是01序列，编译器（例如vc）就是将我们看得懂的文本语言翻译成机器看得懂的01序列。

编译器就是一个翻译官，读某种语言的源代码，理解其内容，映射到目标语言中。
目标语言不仅仅是01序列，也可以是其他，例如将C语言翻译成Fortran语言也可以。
例如本文的撰写格式采用的是markdown语法，通过某个翻译器（不专业的说法），
最终会转化成html格式，然后浏览器理解这段html格式，将其按照内容中的要求将内容显示在显示器上。
这里浏览器实际上是一个**解释器**，不同于**编译器**这样将一段程序翻译成01执行序列或者另一种程序的方式。
浏览器这样的解释器是理解html格式，然后按照其要求执行浏览器程序自己设定的01序列，
html并没有被翻译成01序列，而是通过浏览器代理执行，或者说浏览器成了一个可以理解更高级内容的**虚拟机**。

稍微跑一下题，进一步说，很多程序就是一个理解某个规则的片段并执行的过程，例如excel可以理解的csv文本格式如下:

```csv
Time,H2,CO,CH4,case_example,stages
0.05556,23.35235,3.10185,8.68601,case1,C1S1
0.11111,23.62906,3.55692,7.60854,case1,C1S1
0.16667,24.64287,3.62913,7.69976,case1,C1S1
0.22222,29.64828,5.03771,7.87253,case1,C1S1
0.27778,31.87434,4.77112,7.90599,case1,C1S1
0.33333,32.39345,4.02943,7.54947,case1,C1S1
0.38889,31.42271,3.50293,6.47658,case1,C1S1
0.44444,30.96054,4.52849,6.69443,case1,C1S1
```

就是解析表格内容，由逗号分割不同列，由回车符分割不同行。
这就是csv定义的读取数据的规则或者说语法。
假设Fluent生成的文件不符合tecplot的语法规则，那么tecplot的程序就读不懂Fluent生成的内容。

回到正题，不同的语言有的更接近人，有的更接近机器，例如汇编语言最接近机器，人看汇编语言就会很繁琐，
，例如一个函数调用，在C语言里看很容易一眼看懂，对应到机器码就会看着有些累，编写一个函数调用的机器码更累。
这就带来了编写效率的问题，
早期的语言例如Fortran语言是为了解决机器的执行效率问题，其语法特点方便优化，方便数值计算，
但从现在的角度来说，对人不太友好，例如其函数通常有非常多个参数，且命名很难读懂，
变量作用范围没有加以限制，某个地方出错了可能要找很远才发现问题，很容易出错。
现在的硬件条件已经很丰富了，绝大多数现代语言都是解决人的编写效率问题，

这里有一个问题，有没有执行效率高，同时人编写代码效率高的编程语言呢？
我个人认为，由于机器执行逻辑与人遇到的真实现实问题的差异导致很难实现。
执行效率高意味着对机器的精确控制，而代码编写效率高的一个方面就是尽可能减少考虑细节问题的负担，
而将精力放在现实问题上。

C++想要成为a better C，提供C没有的一些能力，例如对象、模板等，同时尽可能考虑不降低性能损失，
（当然，面向对象的设计风格，C也可以通过函数指针变相地实现，只是会麻烦一点）
可以简单的理解C++是C的超集，目的是让人更有效率地的进行机器底层的开发。
因此现代操作系统、游戏引擎、机器学习后端引擎、数值模拟软件等需要与具体机器打交道、需要考虑性能调优的场景
多采用C++开发。

需要说明的是C++注重于开发效率与优化，添加了模板元编程、面向对象等多个范式，因此不可避免地其语法复杂度较高，
也有各种语法的坑，容易出现一些其他高级语言不会出现的一些意想不到的错误，
所以需要了解其各种坑和细节，我差不多死磕C++一年半，也只能说稍微理解一点其面向对象的内容，
很多编程十年的也不敢说精通C++，其创始人也不敢说完全懂C++，
这些细节和坑对于初步学习编程并没有什么用，所以并不是一个初学编程的好选择。

另外，C++注重效率意味着其编译器设计也很复杂，对于较小的单片机之类的程序而言，其生成的代码可能会有些臃肿，
因此类似单片机之类的小型机器多采用C语言。

c为了适用于不同的硬件条件，对于有些地方，例如int类型是32为还是16为并没有严格的限制，这样适配了高中低端不同机器，
但是这可能依赖于某种条件的编程带来一定的问题，于是很多跨机器的C/C++程序都是首先用某种方式（例如宏）检测编译器和硬件平台是什么，
以此实现一份代码适用于所有机型。

接着说一下Java，总体而言，Java的设计初衷很可能是为了让程序员更好地写**应用级**程序，
而不是写跟机器直接打交道的**系统级**程序，因此相对于C/C++做了诸多改进或者限制。

例如Java选择将Integer类型采用固定的32位长度，
同时它不直接生成具体针对某个机器的机器码01序列，而是设计了一个称为JVM的虚拟机，并设计一个通用的汇编规则，
运行的程序先生成符合这个通用的类似汇编语言的01序列（称为字节码），
然后JVM读取这一段字节码，代理机器执行任务。
这样只要某个机器上有针对该机器的JVM就能执行相应程序，
而优化之类的事情就交给JVM处理，甚至可以边运行边优化。

另外Java是纯粹的面向对象语言，而且不让程序员直接使用指针这一危险的操作，并用GC代替程序员`free(pointer)`手动释放不需要的内存。
（因为指针可以理解为地址，指向函数的地址、指向内存的地址等，就好比收拾东西，
由Java自动帮你收拾整理，而不用自己收拾整理，因为人工的就容易出错）

总之，Java为了让程序员更好地写应用级的程序，进一步代替程序员与具体的机器打交道，并限制了一些程序员容易出错的地方，同时也进一步贯彻面向对象的思想。
相同的语言还包括微软的C#。

Java的优势在于写应用程序，并提供了良好的生态，很多都有现成的框架或者库，所以程序员开发Java的应用程序快很多。

再跑个题，所谓框架，就好比建一栋房子，把架子已经搭好了，也预设了一些门窗，你不喜欢可以换掉，房间可以自己按需求布置，
但基本结构已经帮你处理好了，只用填对应的东西。优点是开发速度快，缺点是可定制性被限制了。
就好比造火箭，已经把各个组件设计好了，只需要组装一下，或者少量地方更改一下，这样普通人也可以"造火箭"，但不可能用这个造船。
可以把Fluent视为一个流体力学的数值计算框架。
而库，或者说函数库，通常是提供工具，例如找房子提供水泥，砖头，打桩机等等，但不会预设一些房子的骨架给你，因为库不知道你用这些工具干什么，
所以也可以用来造桥、修路。例如Lapack，或者C语言中的`#include <stdio>`，stdio这些应该都算作库。
总之纯粹的只有语言自身是没有用的，必须有好用的框架、库，有很多同行经验才行。

回到正题，Java优势在于代理程序员与机器打交道，让程序员专注于应用开发，并有良好的生态，包括框架、库和诸多从业者，使得开发程序更为方便、快速。
但Java语言本身设计上实际上不如C#，只不过Java生态比C#发展地好，所以流行度比C#更高。

Java的一个问题是语言设计上的不灵活，导致有些功能需要绕一大圈实现，为了代码良好的复用性，也衍生出各种设计模式，所以Java代码通常冗长啰嗦。
而后起之秀Python则以其语法简洁著称，`Life is short, so I use Python`，开发更为简洁方便。
我对Python了解不够深入，故不能深入分析其优劣，
Python的生态非常好，无论Web开发、网络爬虫、大数据、人工智能、科学计算（有取代matlab的趋势）等各个领域都有良好的生态。

## 学习编程语言的选择

如果需要跟硬件打交道，需要学习C，并了解汇编，可以看王爽的《汇编语言》，如果想进一步了解一段程序是如何运行的可以看《程序员的自我修养》

如果想学习面向对象，从更高层次上思考软件构架，可以学习Java或C#，建议学C#，语言是想通的，学一门再学另一门很快，且能更好地理解该语言设计的优劣。

不推荐学习C++，除非要开发计算图形学引擎，数值算法库，深度学习算法库等高深内容时，
而且应该是在通过C#或Java掌握了面向对象思想，以及通过汇编了解了底层后再学习C++，
因为C++的书籍绝大多数都涉及的太深入的细节上。
如果把编程语言比作棋，C++的书都是在讲下棋的规则，而且很厚很复杂，而少涉及下棋的策略。

如果做科研，Python应该是非常值得一学的语言。

如果想进一步深入到函数式编程领域，顺便熟悉下递归，可以学习Scheme，推荐看《SICP》

当然，编程的基础，算法与数据结构，作为基础还是要掌握的。
如果把编程比作下棋，学习语言是学习的是规则，算法与数据结构学习的是局部策略，而面向对象，设计模式，测试驱动开发等是通盘考虑的大局观。

## 工作中编程语言的选择

应该视情况而定，例如不能因为Fortran已经落伍了就全盘否定，
假设开发一个程序源码就是Fortran，改成C或者C++固然好，但工作量太大，就没必要。

原则应该依据情况怎么方便，怎么好维护怎么来。

而有些程序有自己设计的领域专用语言（Domain Special Language），例如matlab的语言，Aspen的语言，这些都是模仿一下现学现用。
总之，语言是工具，只是当下那个用着趁手用哪个，只要熟悉了常规的编程思路（基本功是算法与数据结构）、几种编程范式（例如面向对象），学其他相似的语言就非常快。

# 操作系统
从前述内容中了解到，让机器执行任务只需要通过编程，让编译器翻译生成机器能够读得懂的01序列，由机器执行即可，
有些认为极为简单，几乎是单一的流程化的任务，例如单片机，这就可以直接写好程序后烧录进单片机就能按照要求运行。

但更复杂的机器，例如个人电脑，需要执行通用的任务，而且有时候是多个任务，需要调度任务。
另一方面，多个任务都用的同一块内容，如果一个任务用了某个内存地址用来做某件事情，另一个任务在不知道的情况下也用了同一块地址，改写了其中的内容，那么就会造成故障，
所以需要一个代理来**调度任务**，**分配存储空间**（包括内存和硬盘上存储的文件）,以及跟各种硬件，如鼠标键盘打交道，驱动这些硬件，

这个代理就是操作系统，其他的程序不直接与硬件打交道，而是跟操作系统打交道，操作系统提供与这些硬件打交道的接口，实际与硬件打交道的是操作系统，由操作系统代理完成。

这样做的一个好处是，硬件只需要跟操作系统之间有一个通信规则，或者说标准、契约、规范，那么无论是A厂家生产的键盘还是B厂家生产的键盘，程序不需要管，
只需要调用操作系统提供的接口即可，这样就可以适配不同的硬件。

另一个好处是内存交由操作系统管理避免了一些危险的操作，例如早期的程序对内存访问没有权限控制，那么就很容易通过改写某个内存，将其中的内容改成自己的01序列，
就实现的病毒攻击之类，
现代的操作系统对于内存管理有严格的访问权限控制，除了操作系统自身的疏漏之外，病毒很难入侵。

# 编程思想
## 面向对象设计思想
## 层次结构的思想
## 设计原则

## 纯软件开发与传统开发的区别与启示
### 测试驱动开发
待续
{'fraction_non_alphanumeric': 0.11028532376559029, 'fraction_numerical': 0.06560738083034341, 'mean_word_length': 18.066775244299674, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 4, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6197735', 'n_tokens_mistral': 11128, 'n_tokens_neox': 12791, 'n_words': 368}