大家好,欢迎来到IT知识分享网。
目录
在计算实现路径上——从具体到抽象
-
从纸带读取到集成电路
在学习上或者生活中,选择性题目(eg.选择题、判断题)总是比问答型题目(eg.填空题、问答题)简单。因为边界清晰,答案就藏在已知的选项中,即使不知道如何解算,也可以通过统计学技巧来保证一定的正确率。
沿着“用简单方法去解决复杂问题”的思路,计算机科学家们决定将所有将计算机的所有运算都基于逻辑运算。由于在最小单元的逻辑运算中,参与计算的对象(这里可以看做是数据)只有两个,运算的前提是识别和读取这两个对象。一个对象需要具备不同的状态(对应于数值)才可以囊括各种变化的情况,其中最简单的就是两种状态,非此即彼。
那怎么怎么识别和读取任意对象的两个状态呢?
早期是通过穿孔纸带进行标识的。 穿孔纸带利用一排孔表示一个字符,沿着一定间隔,用穿孔或不穿孔表示两种不同的状态,再进行不同的排序组合,接着由纸带读取器逐格扫描纸带,来将待计算对象的状态导入内存。虽然实现起来比较简单,但是具有速度较慢、容易出错等问题。
随着科学技术的发展,电子管、晶体管(Transistor)等电子元件出现,计算装置(即计算机)沿着微型化、网络化、智能化、集成化的方向迭代。正如1975年以来,集成电路((Integrated Circuit,IC)的晶体管集成度遵循着 摩尔定律(Moore’s Law),即单个微芯片(microchip)上可以容纳的晶体管数目每两年翻一番(增加一倍)。于是嵌入了数以亿计电子元件的超大规模集成电路使计算能力得到显著提升,目前大多数现代计算机的 CPU核心能够以每秒30亿条指令的速度持续运行,其峰值速度甚至还要快许多。不变的是,现代计算机采用最简单的进位制——二进制,即用0和1表示对象的两种状态。因为两个状态的系统具有稳定性,抗干扰能力强,能与物理部件的两种状态相对应(比如高电平与低电平);而且二进制运算法则简单,技术容易实现,从而降低研制成本。在集成电路的硬件加持下,键盘或触屏等界面终端的输入被汇编或编译成计算机可以识别的二进制语言(即机器语言),在操作系统(Operating System,OS)的软件控制下,并发(同一时间间隔内)或并行(同一时刻)地高速计算多个任务。
-
从图灵机到冯·诺依曼机
图灵机(Turing Machine)是图灵在1936年提出的一种可用于计算的数学模型,它描述了一种抽象化机器,这个机器由一个控制器、一个读写头和一条无限长的纸带组成。纸带上有用于计算的数字或符号,控制器控制读写头在纸带上移动并读取或修改纸带上的数字或符号。发出读写命令的控制器内部有一个有限状态转换表, 状态转换表根据当前状态及从纸带读取的内容来确定下一个状态并控制读写头的移动,状态转换循环往复,直至完成计算。控制器内部的有限状态转换表可以理解为编写的程序。
计算机的底层运算都是加法,正如“减法=加负数”,“乘法=多个相同数值的加和”。如果把图灵机看做只会做加法的机器,那么通用图灵机(Universal Turing Machine,UTM)则是可以进行多种运算的图灵机,因为引入了可以控制 控制器的指令,先读取指令对控制器的动作和指令要求的控制器的状态,再读取纸带上的数字或符号。通过升维,拓宽计算的广度。
由于在任一时刻都只能知道当前的状态,图灵机的计算能力是受到限制的,因为它无法记住更多的数据,无法把很多数据保存下来以便在未来继续使用它们或对它们进行处理。后来,冯·诺依曼机解决了这个问题。
冯·诺依曼设计了一种以存储器、运算器、控制器、输入设备和输出设备为五大核心组成部分的计算机,并在1945年提出了一种将程序指令存储器和数据存储器合并在一起的计算机设计概念结构。程序和数据存放到计算机内部的存储器中,计算机在程序的控制下一步一步进行处理。计算机在运行时,先从指令存储器中取出第一条指令,通过控制器的译码,按指令的要求,再从数据存储器中取出数据,以进行指定的运算和逻辑操作等加工,然后再按地址把结果送到内存中去。接下来,再取出第二条指令,在控制器的指挥下完成规定操作。依此进行下去。直至遇到停止指令。
冯·诺依曼机在通用图灵机的基础之上,引入了运算器和输入输出设备,同时封装了存储器,使得计算能更可控、更便捷地在人为控制下机械却高效的完成。
在计算应用方式上——高级语言编程
人类该如何跟计算机沟通,使其计算得出满意的结果呢?
计算机只懂机器语言,即之前提到的由二进制代码表示的一系列机器指令代码集合,如果人们直接使用机器语言进行编程,则很容易沉沦在0和1的指令代码中,直观性差,难以追溯编程中使用单元的状态,而且极易出错,一位变动结果足以大相径庭。于是约翰·巴克斯发明出了最早的高级语言(全称高级编程语言)Fortran,证实了 高级语言以及将高级语言翻译成机器语言的一系列“翻译器”可以衔接起人类与计算机的沟通桥梁。用高级语言编写的程序被称为源代码(source code)。根据翻译方式,高级编程语言被分为编译性语言和解释型语言。
编译型语言与解释性语言
编译性语言通过编译器(compiler)将源代码全部编译成可执行文件,一旦编译完成,之后每次运行可以脱离编译器,直接运行可执行文件,故运行效率更高;但是不能跨平台,难以实现在不同操作系统间(Windows、Linux、MacOS)的随意切换,故可移植性较差。比如C、C++、C#、Pascal、Erlang,Haskell,Rust、Go等。特别地,
解释型语言通过解释器(interpreter)解释完一个指令后马上执行,因此是边解释边执行的,不会把所有指令全部解释完后统一执行,因此比较依赖解释器,每次执行都得重新转换源代码,故运行速度更慢;但是相同的源代码可以在不同的平台上解释成对应的机器码,实现跨平台运行,故更加灵活。比如Python、PHP、JavaScript、VB、Perl、Ruby等。
特别地,
C、C++为了解构多引入了一些环节,即需要经过依次经过预处理(Preprocess)、编译(Compilation)、汇编(Assembly)和链接(Linking)四个步骤后才生成可执行文件。详见从高级语言到机器语言_51CTO博客_机器语言是高级语言吗
Java是一种编译型-解释型语言,同时具备编译和解释特性。作为编译型语言,Java程序要被统一编译成字节码文件——文件后缀是class。此种文件在java中又称为类文件。java类文件不能再计算机上直接执行,它需要被java虚拟机翻译成本地的机器码后才能执行,而java虚拟机的翻译过程则是解释性的。java字节码文件首先被加载到计算机内存中,然后读出一条指令,翻译一条指令,执行一条指令,该过程被称为java语言的解释执行,是由java虚拟机完成的。而在现实中,java开发工具JDK提供了两个很重要的命令来完成上面的编译和解释(翻译)过程。两个命令分别是java.exe和javac.exe,前者加载java类文件,并逐步对字节码文件进行编译,而另一个命令则对应了java语言的解释(javac.exe)过程。在次序上,java语言是要先进行编译的过程,接着解释执行。java所谓的编译与传统的编译不同在于它是将.java文件编程成平台无关的字节码.class文件,并非C一样编译成可执行的机器语言。因此,从某种程度上说,java更像解释型语言。
C#需要进行两次“编译”。C#程序在第一次运行的时候,会依赖其.NET Frameworker平台,编译成中间码(Intermediate Language,IL),然后由即时编译器(JIT compiler)翻译成本地的机器码执行。从第二次在运行相同的程序,则不需要再执行以上编译和翻译过程,而是直接运行第一次翻译成的机器码。所以对于C#来说,通常第一次运行时间会很长,但从第二次开始,程序的执行时间会快很多。那么,C#为什么要进行两次“编译”呢?其实,微软想通过动态编译(由JIT compiler工具实现)来实现其程序运行的最优化。如果代码在运行前进行动态编译运行,那么JIT compiler可以很智能的根据你本地机器的硬件条件来进行优化,比如使用更好的register,机器指令等等,而不是像原来那样,build一份程序针对所有硬件的机器跑,没有充分利用各个机器的条件。
另外,还有我们经常用到的脚本语言,比如JavaScript、Shell等语言都是脚本语言,本质上来说,脚本语言就是解释型语言。
详见解释型语言和编译型语言的区别_jack-zhu的博客-CSDN博客_解释性语言和编译性语言的区别
有了语言过后,如何组织语言成为一个比较重要的问题。
面向过程vs面向对象编程
目前有两种主流的编程范式,分别为面向对象程序设计和面向过程程序设计。面向过程编程早于面向对象编程出现,两者的对比见下表~
面向过程编程 | 面向对象编程 | ||
英语名称 | Procedure-oriented Programming, 简称POP |
Object-oriented programming, 简称OOP |
|
编程语言 | C、Fortran、Pascal、Basic(除VB)等 | C++、C#、Python、Java、JavaScript、VB等 | |
特点 | 围绕数据和过程来组织软件自上而下设计,逐步细化。 程序由一系列要执行的计算步骤组成,将程序分为一个个封装起来的函数模块,使用时再一个一个依次调用。基于过程地考虑整个数据通过软件时的变化,强调对数据的处理,结构化地处理整个流程。 面向过程程序设计也称为结构化程序设计,是基于“冯·诺依曼”模型的。 以过程为中心,适合于那些强调过程,强调对数据的处理的软件。这类软件就像一个流水线,数据进入这个流水线并流出,每一步都是一个结构化的层次。 |
围绕功能或对象来组织软件自下而上设计,遵循封装、继承、多态、抽象的基本原则以及单一职责、开放封闭、里氏替换、依赖倒置、接口隔离等设计原则。 它把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为;将软件功能转变为不同模块的动作和模块间的通信,更专注于对象与对象之间的交互。 对象涉及的属性和方法都在对象内部,函数是对象的一个方法,依赖于类和对象概念——类是相同种类对象的抽象,是它们的公共属性;对象是类的实例,数据不再贯穿整个程序,而是成为各个模块的私有属性,会增加一个类内部的复杂性,但降低了模块之间的耦合性,符合对程序”低耦合,高内聚“的要求。 以对象为中心,强调对对象的操作,适合于以功能为特性的软件。 |
|
优点 | 适用于简单系统,容易理解执行步骤。流程化使得编程任务明确,在开发之前基本考虑了实现方式和最终结果,具体步骤清楚,便于节点分析。效率高,面向过程强调代码的短小精悍,善于结合数据结构来开发高效率的程序。 | 安全性更高,将数据访问隐藏在了类的成员函数中,而且,类的成员变量和成员函数都有不同的访问属性,更容易设计出耦合的系统。 更适用于大型复杂系统,更容易修改程序,更容易添加新功能,方便复用,容易扩展,便于维护。 |
|
缺点 | 代码重用性更低,扩展能力差,后期维护难度大。在整个程序中,数据可能在任何地方被访问,这会导致调试难度的增加,因此也就出现了所谓的一行代码的修改会导致整个程序的变化。 | 开销大,若要修改对象内部,对象的属性不允许外部直接读取,所以需要增加许多读写功能,性能比面向过程低。 | |
以大象放进冰箱为例 | 逻辑顺序 |
(依次定义函数)打开冰箱 → 把大象放进冰箱 → 关闭冰箱 如果编程就是讲故事的话,面向过程编程的【视角】就是: 我(程序员)把数据a怎么样了,又把数据b怎么样了……,类似于”把字句“的陈述 |
冰箱和大象是什么(定义类) → 冰箱和大象怎么样(对象实例化) 如果编程就是讲故事的话,面向对象编程的【视角】就是: 本来对象a、b……是怎么样的,但是对象a被我(程序员)怎么样了,对象b又被我怎么样了……,类似于”被字句“的陈述 |
代码形式 |
//C语言 |
//C++语言 |
如有理解不到位的地方,欢迎指正!!!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/136305.html