电脑程序
计算机程序是编程语言中的序列或一组指令,以供计算机执行。它是软件的组成部分,还包括文档和其他无形组件。
以其人类可读形式的计算机程序称为源代码。源代码需要另一个计算机程序才能执行,因为计算机只能执行其本机机器指令。因此,可以使用语言的编译器将源代码转换为机器说明。 (汇编语言程序使用汇编器翻译。)结果文件称为可执行文件。另外,源代码可以在语言的解释器中执行。
如果要求执行可执行文件,则操作系统将其加载到内存并启动过程。中央处理单元将很快切换到此过程,以获取,解码然后执行每个机器指令。
如果请求源代码执行,则操作系统将相应的解释器加载到内存中并启动一个过程。然后,解释器将源代码加载到内存中以翻译和执行每个语句。运行源代码比运行可执行文件慢。此外,解释器必须安装在计算机上。
示例计算机程序
“你好,世界!”程序用于说明语言的基本语法。语言基本(1964)的语法有意限制,以使该语言易于学习。例如,在使用之前未声明变量。同样,变量会自动初始化为零。这是基本的示例计算机程序,平均数字列表:
10 INPUT "How many numbers to average?", A
20 FOR I = 1 TO A
30 INPUT "Enter number:", B
40 LET C = C + B
50 NEXT I
60 LET D = C/A
70 PRINT "The average is", D
80 END
一旦学习了基本计算机编程的机制,就可以使用更复杂和强大的语言来构建大型计算机系统。
历史
软件开发的改进是计算机硬件改进的结果。在硬件历史记录中的每个阶段,计算机编程的任务发生了巨大变化。
分析引擎
1837年,雅加德(Jacquard)的织布机启发了查尔斯·巴巴奇(Charles Babbage)试图构建分析引擎。计算设备组件的名称是从纺织行业借来的。在纺织业中,纱线是从商店带来的。该设备的“存储”由内存组成,可容纳1,000个数字的50个小数位数。 “商店”的数字被转移到“工厂”进行处理。它是使用两套穿孔卡编程的。一个集合指向操作,另一组将变量输入。但是,即使在Babbage花费了超过17,000英镑的政府资金之后,成千上万的车轮和齿轮也从未完全合作。
Ada Lovelace为Charles Babbage工作,创建了分析引擎的描述(1843)。该描述包含注释G,它完全详细介绍了使用分析引擎计算Bernoulli号码的方法。该注释被一些历史学家认为是世界上第一个计算机程序。
通用图灵机
1936年,艾伦·图灵(Alan Turing)推出了通用图灵机,这是一种可以对每个计算进行建模的理论设备。这是一台有限状态的机器,具有无限长的读/写胶带。机器可以来回移动胶带,在执行算法时更改其内容。机器以初始状态开始,经过一系列步骤,并在遇到停止状态时停止。当今的所有计算机都已完成。
恩
电子数值集成器和计算机(ENIAC)建于1943年7月至1945年秋季之间。这是一台图灵完整的通用计算机,使用17,468个真空管来创建电路。从本质上讲,这是一系列帕斯卡林(Pascalines)连接的。它的40个单位重30吨,占地1,800平方英尺(167 m 2 ),闲置时的电力为每小时650美元( 1940年代的货币)。它有20个基本10蓄能器。编程eNIAC最多需要两个月。车轮上有三个功能表,需要滚动到固定功能面板。通过将重的黑色电缆插入插件,将功能表连接到功能面板。每个功能表都有728个旋转旋钮。编程ENIAC还涉及设置3,000个开关中的一些。调试计划需要一周的时间。它从1947年到1955年在阿伯丁宣告地面上运行,计算氢炸弹参数,预测天气模式,并生产射击桌以瞄准砲兵枪。
存储程序计算机
存储的计算机没有插入电线和转动开关,而是将其指令加载到内存中,就像它将数据加载到内存中一样。结果,可以快速编程计算机并以非常快速的速度执行计算。 Presper Eckert和John Mauchly建造了Eniac。两位工程师在1944年2月的三页备忘录中介绍了存储程序概念。后来,1944年9月,约翰·冯·诺伊曼(John von Neumann)博士开始从事Eniac项目。 1945年6月30日,冯·诺伊曼(von Neumann)发表了有关EDVAC报告的初稿,该报告将计算机的结构等同于人脑的结构。该设计被称为冯·诺伊曼建筑。该体系结构在1949年同时部署在EDVAC和EDSAC计算机的构造中。
IBM System/360 (1964)是一个计算机家族,每个家族都具有相同的指令集体系结构。 20型是最小,最便宜的。客户可以升级并保留相同的应用程序软件。 195型是最优质的。每个系统/360模型都具有多编程- 一次在内存中进行多个过程。当一个过程在等待输入/输出时,另一个过程可能会计算。
IBM计划使用PL/1对每个模型进行编程。成立了一个委员会,其中包括COBOL , FORTRAN和ALGOL程序员。目的是开发一种全面,易于使用,可扩展的语言,并取代Cobol和Fortran。结果是一种大而复杂的语言,花了很长时间才能编译。
直到1970年代制造的计算机都有用于手动编程的前面板开关。计算机程序写在纸上供参考。通过ON/OFF设置的配置表示指令。设置配置后,按下执行按钮。然后重复此过程。计算机程序还通过纸带,打孔卡或磁带自动输入。加载介质后,通过交换机设置起始地址,并按下执行按钮。
非常大的集成
软件开发的主要里程碑是非常大规模集成(VLSI)电路(1964)的发明。第二次世界大战后,基于管的技术被安装在电路板上的点接触晶体管(1947年)和双极连接晶体管(1950年代末)所取代。在1960年代,航空航天行业用集成的电路芯片取代了电路板。
Fairchild Semiconductor (1957)和Intel (1968)的联合创始人罗伯特·诺伊斯(Robert Noyce)取得了技术的改进,以改善现场效应晶体管的产生(1963)。目的是改变半导体连接的电阻率和电导率。首先,使用西门子工艺将天然存在的矽酸盐矿物质转化为多矽棒。然后, Czochralski过程将杆转换为单晶矽Boule晶体。然后将晶体切成薄片以形成晶圆底物。然后,光刻的平面过程将单极晶体管,电容器,二极管和电阻整合到晶片上,以构建金属 - 氧化物 -氧化型晶体管的基质。 MOS晶体管是集成电路芯片中的主要组件。
最初,集成电路芯片在制造过程中具有其功能集。在1960年代,控制电流迁移到编程仅阅读记忆(ROM)的矩阵。矩阵类似于二维保险丝。将指令嵌入到矩阵上的过程是烧毁不需要的连接。有很多连接,固件程序员在另一个芯片上编写了一个计算机程序来监督燃烧。该技术被称为可编程ROM 。 1971年,英特尔将计算机程序安装到芯片上,并将其命名为Intel 4004微处理器。
术语微处理器和中央处理单元(CPU)现在可以互换使用。但是,CPU早于微处理器。例如, IBM System/360 (1964)的CPU由含有离散组件的电路板制成,该电路板上的陶瓷基板上有离散的组件。
SAC州8008
英特尔4004(1971)是一个4位微处理器,旨在运行Busicom计算器。释放五个月后,英特尔发布了Intel 8008 ,这是一个8位微处理器。比尔·彭茨(Bill Pentz)带领萨克拉曼多州立大学的一支团队使用英特尔8008: SAC State 8008 (1972)建立了第一个微型计算机。其目的是存储患者病历。该计算机支持磁盘操作系统运行Memorex ,3兆字节,硬盘驱动器。它具有包装在单个控制台中的颜色显示和键盘。使用IBM的基本装配语言(BAL)对磁盘操作系统进行了编程。使用基本解释器对医疗记录应用程序进行了编程。但是,计算机是一个进化的死胡同,因为它非常昂贵。此外,它是出于特定目的在公立大学实验室建造的。尽管如此,该项目促进了Intel 8080 (1974)指令集的开发。
X86系列
1978年,现代软件开发环境始于英特尔将英特尔8080升级为英特尔8086时。英特尔简化了英特尔8086,以制造便宜的英特尔8088 。当IBM进入个人计算机市场(1981)时,他们接受了英特尔8088。随着消费者对个人计算机的需求增加,英特尔的微处理器开发也随之增加。发展的继任被称为X86系列。 X86组装语言是向后兼容的机器说明的家族。在较早的微处理器中创建的机器说明均保留在整个微处理器升级中。这使消费者无需购买新的应用程序软件即可购买新计算机。说明的主要类别是:
- 内存指令在随机访问内存中设置和访问数字和字符串。
- 整数算术逻辑单元(ALU)指令执行整数上的主要算术操作。
- 浮点ALU指令对实数执行主要算术操作。
- 调用堆栈指令以将内存和与函数互动所需的按下和弹出单词。
- 单个指令,多个数据(SIMD)指令,可以在多个处理器上执行相同的算法时提高速度。
改变编程环境
VLSI电路使编程环境可以从计算机终端(直到1990年代)到图形用户界面(GUI)计算机。计算机终端将程序员限制为在命令行环境中运行的单个外壳。在1970年代,通过基于文本的用户界面,全屏源代码编辑成为可能。无论可用的技术如何,目标都是用编程语言编程。
编程范式和语言
存在编程语言功能,以提供要组合以表达编程理想的构件。理想情况下,编程语言应该:
- 直接在代码中表达思想。
- 独立表达独立思想。
- 直接在代码中表达思想之间的关系。
- 自由地结合想法。
- 仅在组合有意义的地方结合想法。
- 简单地表达简单的想法。
提供这些构建块的编程语言的编程样式可以归类为编程范例。例如,不同的范例可能会区分:
这些编程样式中的每一种都有助于综合不同的编程语言。
编程语言是一组关键字,符号,标识符和规则,程序员可以通过这些关键字,符号,标识符和规则将指令传达给计算机。他们遵循一组称为语法的规则。
编程语言从形式语言中获得基础。根据其形式语言定义解决方案的目的是生成一种算法来解决下划线问题。算法是解决问题的一系列简单指令。
几代编程语言
编程语言的演变始于Edsac (1949)在其von Neumann架构中使用第一个存储的计算机程序。编程EDSAC是第一代编程语言。
- 第二代编程语言是汇编语言。汇编语言允许程序员使用助记符指令,而不是记住指令号。一个汇编器将每个汇编语言助记符转换为其机器语言编号。例如,在PDP-11上,可以将操作24576引用为源代码中的添加。这四个基本算术操作具有诸如Add,sub,mul和div的组装指令。计算机还具有诸如DW(定义单词)之类的说明,以保留存储单元。然后,MOV指令可以在寄存器和内存之间复制整数。
- 第三代编程语言使用编译器和口译员执行计算机程序。第三代语言的显著特征是它远离特定硬件。早期语言包括Fortran (1958), Cobol (1959), Algol (1960)和Basic (1964)。 1973年, C编程语言成为一种高级语言,产生了有效的机器语言说明。尽管第三代语言历史上为每个语句生成了许多机器指令,但C的语句可能会生成单个机器指令。此外,优化编译器可能会覆盖程序员,而产生的机器说明少于语句。如今,整个语言范式填补了当务之急的第三代频谱。
- 第四代编程语言强调了所需的输出结果,而不是应如何构建编程语句。声明语言试图限制副作用,并允许程序员以相对较少的错误编写代码。一种流行的第四代语言称为结构化查询语言(SQL)。数据库开发人员不再需要一次处理每个数据库记录一个。同样,一个简单的语句可以生成输出记录,而不必了解它们的检索方式。
命令性语言
- 声明将变量名引入计算机程序并将其分配给数据类型- 例如:
var x: integer;
- 表达式产生一个值 - 例如:
2 + 2
产量4 - 语句可能会将表达式分配给变量或使用变量的值来更改程序的控制流,例如:
x := 2 + 2; if x = 4 then do_something();
Fortran
Fortran (1958)被公布为“ IBM数学公式翻译系统”。它是为科学计算而设计的,而无需弦乐处理设施。除了声明,表达和陈述外,它还支持:
它成功了:
- 编程和调试成本低于计算机运行成本。
- 它得到了IBM的支持。
- 当时的申请是科学的。
但是,非IBM供应商还撰写了Fortran编译器,但语法可能会使IBM的编译器失败。美国国家标准研究所(ANSI)于1966年开发了第一个Fortran标准。1978年,Fortran 77成为1991年的标准。Fortran90支持:
COBOL
Cobol (1959)代表“以商业为导向的语言”。操纵符号。很快就意识到符号不需要是数字,因此引入了字符串。美国国防部影响了Cobol的发展, Grace Hopper是主要贡献者。这些陈述是英语和冗长的。目标是设计一种语言,以便管理人员可以阅读程序。但是,缺乏结构化陈述阻碍了这一目标。
COBOL的发展受到严格控制,因此方言并没有出现需要ANSI标准。结果,直到1974年才更改15年。1990年代版本确实做出了相应的更改,例如面向对象的编程。
阿尔戈尔
Algol (1960)代表“算法语言”。它对编程语言设计产生了深远的影响。它来自欧美编程专家委员会,它使用了标准的数学符号,并具有可读的结构化设计。 Algol首先使用Backus -Naur形式来定义其语法。这导致了语法指导的编译器。它添加了以下功能:
Algol的直接后代包括一个分支机构的Pascal , Modula-2 , Ada , Delphi和Oberon 。在另一个分支上,后代包括C , C ++和Java 。
基本的
Basic (1964)代表“初学者的通用符号指令代码”。它是在达特茅斯学院(Dartmouth College)开发的,供所有学生学习。如果学生没有继续使用更强大的语言,那么学生仍然会记住基本的语言。在1970年代后期制造的微型计算机中安装了基本口译员。随着微型计算机行业的发展,该语言也是如此。
- “新”命令创建了一个空的板岩。
- 立即评估的陈述。
- 语句可以通过使用行号进行编程。
- “列表”命令显示了程序。
- “运行”命令执行了程序。
但是,对于大型程序而言,基本语法太简单了。最近的方言增加了结构和面向对象的扩展。 Microsoft的Visual Basic仍被广泛使用,并产生图形用户界面。
C
C编程语言(1973)之所以出名,是因为BCPL被B替换为BCPL, AT&T Bell Labs称为下一个版本。其目的是编写UNIX操作系统。 C是一种相对较小的语言,使编写编译器很容易。它的增长反映了1980年代的硬件增长。它的增长也是因为它具有装配语言的设施,但使用了高级语法。它添加了高级功能,例如:
C允许程序员控制要存储哪个内存数据区域。全局变量和静态变量需要存储最少的时钟周期。该堆栈自动用于标准变量声明。堆内存返回到指针变量malloc()
功能。
- 全局和静态数据区域位于程序区域上方。 (从技术上讲,程序区域称为文本区域。这是存储机器指令的地方。)
- 全球和静态区域存储在(外部)上声明的全局变量
main()
功能。全球变量可见main()
以及源代码中的所有其他功能。
- 全球和静态区域存储在(外部)上声明的全局变量
- 使用的本地变量使用
static
前缀也存储在全球和静态数据区域中。与全局变量不同,静态变量仅在函数或块中可见。静态变量始终保留其价值。一个示例用法将是功能int increment_counter(){static int counter = 0; counter++; return counter;}
- 使用的本地变量使用
- 堆栈区域是位于顶部内存地址附近的连续内存块。放置在堆栈中的变量是从上到下填充的。堆栈指针是一种特殊的用途寄存器,可以跟踪填充的最后一个内存地址。通过汇编语言推动说明将变量放入堆栈中。因此,这些变量的地址是在运行时设置的。堆栈变量丢失其范围的方法是通过POP指令。
- 本地变量声明没有
static
前缀(包括正式参数变量)称为自动变量,并存储在堆栈中。它们在函数或块内部可见,并在退出函数或块时失去范围。
- 本地变量声明没有
- C提供
malloc()
库功能以分配堆内存。用数据填充堆是一个附加的复制功能。存储在堆中的变量在经济上使用指针传递给功能。没有指针,整个数据块必须通过堆栈传递到该功能。
- C提供
C ++
在1970年代,软件工程师需要语言支持将大型项目分解为模块。一个明显的功能是将大型项目物理分解为单独的文件。一个不太明显的功能是将大型项目从逻辑上分解为抽像数据类型。当时,语言支持混凝土(标量)数据类型,例如整数编号,浮点数和字符字符串。抽像数据类型是具体数据类型的结构,并分配了新名称。例如,可以调用整数列表integer_list
.
在面向对象的术语中,抽像数据类型称为类。但是,班级只是一个定义。没有内存分配。当内存分配给类并绑定到标识符时,它称为对象。
通过结合对课程的需求和安全功能编程的需求,开发了面向对象的命令式语言。以面向对象的语言的功能分配给类。然后将分配的函数称为方法,成员函数或操作。面向对象的编程是在对象上执行操作。
面向对象的语言支持语法来建模子集/超集关系。在集合理论中,子集的一个元素继承了超集中包含的所有属性。例如,学生是一个人。因此,一组学生是一组人的子集。结果,学生继承了所有人共有的所有属性。此外,学生具有其他人没有的独特属性。面向对象的语言使用继承模型子集/超集关系。到1990年代后期,以对象为导向的编程成为主要的语言范式。
C ++ (1985)最初称为“ C with Classect”。它旨在通过添加语言模拟的面向对象的设施来扩展C的功能。
面向对象的模块由两个文件组成。定义文件称为标题文件。这是一个简单的学校应用程序中的C ++标头文件:
// grade.h
// -------
// Used to allow multiple source files to include
// this header file without duplication errors.
// ----------------------------------------------
#ifndef GRADE_H
#define GRADE_H
class GRADE {
public:
// This is the constructor operation.
// ----------------------------------
GRADE ( const char letter );
// This is a class variable.
// -------------------------
char letter;
// This is a member operation.
// ---------------------------
int grade_numeric( const char letter );
// This is a class variable.
// -------------------------
int numeric;
};
#endif
构造函数操作是具有与班级名称相同名称的函数。当调用操作执行时,它将执行new
陈述。
模块的另一个文件是源文件。这是一个简单的学校应用程序中的C ++源文件:
// grade.cpp
// ---------
#include "grade.h"
GRADE::GRADE( const char letter )
{
// Reference the object using the keyword 'this'.
// ----------------------------------------------
this->letter = letter;
// This is Temporal Cohesion
// -------------------------
this->numeric = grade_numeric( letter );
}
int GRADE::grade_numeric( const char letter )
{
if ( ( letter == 'A' || letter == 'a' ) )
return 4;
else
if ( ( letter == 'B' || letter == 'b' ) )
return 3;
else
if ( ( letter == 'C' || letter == 'c' ) )
return 2;
else
if ( ( letter == 'D' || letter == 'd' ) )
return 1;
else
if ( ( letter == 'F' || letter == 'f' ) )
return 0;
else
return -1;
}
这是一个简单的学校应用程序中的人类课程的C ++标头文件:
// person.h
// --------
#ifndef PERSON_H
#define PERSON_H
class PERSON {
public:
PERSON ( const char *name );
const char *name;
};
#endif
这是一个简单的学校应用程序中的人类班级的C ++源文件:
// person.cpp
// ----------
#include "person.h"
PERSON::PERSON ( const char *name )
{
this->name = name;
}
这是一个简单的学校应用程序中学生课的C ++标头文件:
// student.h
// ---------
#ifndef STUDENT_H
#define STUDENT_H
#include "person.h"
#include "grade.h"
// A STUDENT is a subset of PERSON.
// --------------------------------
class STUDENT : public PERSON{
public:
STUDENT ( const char *name );
GRADE *grade;
};
#endif
这是一个简单的学校应用程序中学生课的C ++源文件:
// student.cpp
// -----------
#include "student.h"
#include "person.h"
STUDENT::STUDENT ( const char *name ):
// Execute the constructor of the PERSON superclass.
// -------------------------------------------------
PERSON( name )
{
// Nothing else to do.
// -------------------
}
这是一个用于演示的驱动程序程序:
// student_dvr.cpp
// ---------------
#include <iostream>
#include "student.h"
int main( void )
{
STUDENT *student = new STUDENT( "The Student" );
student->grade = new GRADE( 'a' );
std::cout
// Notice student inherits PERSON's name
<< student->name
<< ": Numeric grade = "
<< student->grade->numeric
<< "\n";
return 0;
}
这是编译所有内容的制造商:
# makefile
# --------
all: student_dvr
clean:
rm student_dvr *.o
student_dvr: student_dvr.cpp grade.o student.o person.o
c++ student_dvr.cpp grade.o student.o person.o -o student_dvr
grade.o: grade.cpp grade.h
c++ -c grade.cpp
student.o: student.cpp student.h
c++ -c student.cpp
person.o: person.cpp person.h
c++ -c person.cpp
声明性语言
命令式语言有一个主要的批评:将表达式分配给非本地变量可能会产生意外的副作用。声明性语言通常会省略分配声明和控制流。他们描述了应执行哪些计算,而不是如何计算计算。声明性语言的两种类别是功能性语言和逻辑语言。
功能性语言背后的原理是使用lambda cyculus作为定义明确的语义的指南。在数学中,一个函数是将元素从表达式映射到一系列值的规则。考虑该功能:
times_10(x) = 10 * x
表达方式10 * x
由功能映射times_10()
到一系列值。一个值恰好是20。当x为2时,发生这种情况。因此,该函数的应用在数学上写为:
times_10(2) = 20
功能语言编译器不会将此值存储在变量中。相反,在将程序计数器设置回调用函数之前,它将将值推到计算机的堆栈上。然后,调用功能将从堆栈中弹出值。
命令性语言确实支持功能。因此,如果程序员使用纪律,则可以在命令式语言中实现功能编程。但是,功能性语言将通过其语法迫使该学科进入程序员。功能语言具有量身定制的语法,以强调什么。
开发了一个功能程序,具有一组原始功能,然后是单个驱动程序函数。考虑片段:
function max(a,b){/* code omitted */}
function min(a,b){/* code omitted */}
function difference_between_largest_and_smallest(a,b,c) {
return max(a,max(b,c)) - min(a, min(b,c));
}
原语是max()
和min()
。驱动程序功能是difference_between_largest_and_smallest()
。执行:
put(difference_between_largest_and_smallest(10,4,7));
将输出6。
计算机科学研究中使用功能语言来探索新的语言功能。此外,它们缺乏副作用使它们在并行编程和并发编程中流行。但是,应用程序开发人员更喜欢面向对象的语言的特征。
Lisp
LISP (1958)代表“清单处理器”。它是针对过程列表量身定制的。数据的完整结构是由列表的构建列表形成的。在内存中,建立了树数据结构。在内部,树结构非常适合递归功能。构建树的语法是将括号内的空间分离元素包围。以下是三个要素的列表。前两个要素本身是两个要素的列表:
((A B) (HELLO WORLD) 94)
LISP具有提取和重建元素的功能。功能head()
返回包含列表中第一个元素的列表。功能tail()
返回包含第一个元素以外的所有内容的列表。功能cons()
返回其他列表的串联列表。因此,以下表达式将返回列表x
:
cons(head(x), tail(x))
LISP的一个缺点是当许多功能嵌套时,括号看起来可能令人困惑。现代LISP环境有助于确保括号匹配。顺便说一句,LISP确实支持分配声明和Goto循环的命令性语言操作。另外, LISP在编译时不关心元素的数据类型。相反,它在运行时分配(可能会重新分配)数据类型。在运行时分配数据类型称为动态绑定。尽管动态绑定增加了语言的灵活性,但编程错误可能会徘徊,直到软件开发过程后期。
编写大型,可靠且可读的LISP程序需要预见。如果适当计划,该程序可能比同等的命令式语言程序要短得多。 LISP广泛用于人工智能。但是,它的用法仅是因为它具有命令性的语言操作,从而使意想不到的副作用成为可能。
ML
ML (1973)代表“元语言”。 ML检查以确保仅将同一类型的数据彼此进行比较。例如,此函数具有一个输入参数(一个整数),并返回一个整数:
fun times_10(n : int) : int = 10 * n;
ML不是像LISP那样的括号。以下是times_10()
:
times_10 2
它返回“ 20:int”。 (返回结果和数据类型。)
像LISP一样, ML量身定制为流程列表。与LISP不同,每个元素都是相同的数据类型。此外, ML在编译时分配元素的数据类型。在编译时分配数据类型称为静态绑定。静态绑定会提高可靠性,因为编译器在使用变量之前检查了它们的上下文。
序言
Prolog (1972)代表“逻辑编程”。这是一种基于正式逻辑的逻辑编程语言。该语言是由法国马赛的Alain Colmerauer和Philippe Roussel开发的。它是由罗伯特·科瓦尔斯基(Robert Kowalski)和爱丁堡大学(University of Edinburgh)的其他人开创的选择性线性确定条款解决方案的实施。
序幕计划的基础是事实和规则。这是一个简单的例子:
cat(tom). % tom is a cat
mouse(jerry). % jerry is a mouse
animal(X) :- cat(X). % each cat is an animal
animal(X) :- mouse(X). % each mouse is an animal
big(X) :- cat(X). % each cat is big
small(X) :- mouse(X). % each mouse is small
eat(X,Y) :- mouse(X), cheese(Y). % each mouse eats each cheese
eat(X,Y) :- big(X), small(Y). % each big animal eats each small animal
在输入所有事实和规则之后,可以提出一个问题:
- 汤姆会吃杰里吗?
?- eat(tom,jerry).
true
以下示例显示了Prolog将如何将字母等级转换为其数字值:
numeric_grade('A', 4).
numeric_grade('B', 3).
numeric_grade('C', 2).
numeric_grade('D', 1).
numeric_grade('F', 0).
numeric_grade(X, -1) :- not X = 'A', not X = 'B', not X = 'C', not X = 'D', not X = 'F'.
grade('The Student', 'A').
?- grade('The Student', X), numeric_grade(X, Y).
X = 'A',
Y = 4
这是一个全面的例子:
1)所有龙是滚滚而来的,或者等效地,如果这是一条龙,那就是滚滚的事:
billows_fire(X) :-
is_a_dragon(X).
2)一个生物滚滚,如果其中一个父母冲浪:
billows_fire(X) :-
is_a_creature(X),
is_a_parent_of(Y,X),
billows_fire(Y).
3)X是事物的父母,如果X是Y或X的母亲是Y的父亲:
is_a_parent_of(X, Y):- is_the_mother_of(X, Y).
is_a_parent_of(X, Y):- is_the_father_of(X, Y).
4)如果事物是龙,那么一件事就是一个生物:
is_a_creature(X) :-
is_a_dragon(X).
5)诺伯塔(Norberta)是一条龙,泡芙(Puff)是一种生物。诺伯塔(Norberta)是Puff的母亲。
is_a_dragon(norberta).
is_a_creature(puff).
is_the_mother_of(norberta, puff).
规则(2)是递归(归纳)定义。它可以声明地理解,而无需了解其执行方式。
规则(3)显示了如何通过使用关系表示功能。在这里,母亲和父亲的功能确保每个人只有一个母亲,只有一个父亲。
Prolog是一种非易于的语言。但是,可以通过使用谓词来表示继承。规则(4)断言一个生物是龙的超类。
使用向后推理回答问题。考虑到一个问题:
?- billows_fire(X).
Prolog产生了两个答案:
X = norberta
X = puff
面向对象的编程
面向对象的编程是一种编程方法,用于在对象上执行操作(功能)。基本思想是将现象的特征分组到对象容器中,并为容器提供一个名称。关于该现象的操作也分为容器。以对象为导向的编程结合了对容器的需求和安全功能编程的需求。这种编程方法不必局限于面向对象的语言。用面向对象的语言,对象容器称为类。在非对象的语言中,数据结构(也称为记录)可能成为对象容器。要将数据结构变成对象容器,需要专门为结构编写操作。结果结构称为抽像数据类型。但是,将缺少继承。尽管如此,这种缺点可以克服。
这是一个简单的学校应用程序中的C编程语言标头文件:
/* grade.h */
/* ------- */
/* Used to allow multiple source files to include */
/* this header file without duplication errors. */
/* ---------------------------------------------- */
#ifndef GRADE_H
#define GRADE_H
typedef struct
{
char letter;
} GRADE;
/* Constructor */
/* ----------- */
GRADE *grade_new( char letter );
int grade_numeric( char letter );
#endif
这grade_new()
功能执行与C ++构造器操作相同的算法。
这是一个简单的学校应用程序中的c编程语言源文件,用于等级摘要数据类型:
/* grade.c */
/* ------- */
#include "grade.h"
GRADE *grade_new( char letter )
{
GRADE *grade;
/* Allocate heap memory */
/* -------------------- */
if ( ! ( grade = calloc( 1, sizeof ( GRADE ) ) ) )
{
fprintf(stderr,
"ERROR in %s/%s/%d: calloc() returned empty.\n",
__FILE__,
__FUNCTION__,
__LINE__ );
exit( 1 );
}
grade->letter = letter;
return grade;
}
int grade_numeric( char letter )
{
if ( ( letter == 'A' || letter == 'a' ) )
return 4;
else
if ( ( letter == 'B' || letter == 'b' ) )
return 3;
else
if ( ( letter == 'C' || letter == 'c' ) )
return 2;
else
if ( ( letter == 'D' || letter == 'd' ) )
return 1;
else
if ( ( letter == 'F' || letter == 'f' ) )
return 0;
else
return -1;
}
在构造函数中,功能calloc()
使用而不是malloc()
因为每个内存单元将设置为零。
这是一个简单学校应用程序中的人摘要数据类型的C编程语言标头文件:
/* person.h */
/* -------- */
#ifndef PERSON_H
#define PERSON_H
typedef struct
{
char *name;
} PERSON;
/* Constructor */
/* ----------- */
PERSON *person_new( char *name );
#endif
这是一个简单学校应用程序中的人摘要数据类型的C编程语言源文件:
/* person.c */
/* -------- */
#include "person.h"
PERSON *person_new( char *name )
{
PERSON *person;
if ( ! ( person = calloc( 1, sizeof ( PERSON ) ) ) )
{
fprintf(stderr,
"ERROR in %s/%s/%d: calloc() returned empty.\n",
__FILE__,
__FUNCTION__,
__LINE__ );
exit( 1 );
}
person->name = name;
return person;
}
这是一个简单的学校应用程序中的学生摘要数据类型的C编程语言标头文件:
/* student.h */
/* --------- */
#ifndef STUDENT_H
#define STUDENT_H
#include "person.h"
#include "grade.h"
typedef struct
{
/* A STUDENT is a subset of PERSON. */
/* -------------------------------- */
PERSON *person;
GRADE *grade;
} STUDENT;
/* Constructor */
/* ----------- */
STUDENT *student_new( char *name );
#endif
这是一个简单学校应用程序中学生摘要数据类型的C编程语言源文件:
/* student.c */
/* --------- */
#include "student.h"
#include "person.h"
STUDENT *student_new( char *name )
{
STUDENT *student;
if ( ! ( student = calloc( 1, sizeof ( STUDENT ) ) ) )
{
fprintf(stderr,
"ERROR in %s/%s/%d: calloc() returned empty.\n",
__FILE__,
__FUNCTION__,
__LINE__ );
exit( 1 );
}
/* Execute the constructor of the PERSON superclass. */
/* ------------------------------------------------- */
student->person = person_new( name );
return student;
}
这是一个用于演示的驱动程序程序:
/* student_dvr.c */
/* ------------- */
#include <stdio.h>
#include "student.h"
int main( void )
{
STUDENT *student = student_new( "The Student" );
student->grade = grade_new( 'a' );
printf( "%s: Numeric grade = %d\n",
/* Whereas a subset exists, inheritance does not. */
student->person->name,
/* Functional programming is executing functions just-in-time (JIT) */
grade_numeric( student->grade->letter ) );
return 0;
}
这是编译所有内容的制造商:
# makefile
# --------
all: student_dvr
clean:
rm student_dvr *.o
student_dvr: student_dvr.c grade.o student.o person.o
gcc student_dvr.c grade.o student.o person.o -o student_dvr
grade.o: grade.c grade.h
gcc -c grade.c
student.o: student.c student.h
gcc -c student.c
person.o: person.c person.h
gcc -c person.c
建立面向对像对象的正式策略是:
- 标识对象。这些很可能是名词。
- 标识每个对象的属性。是什么有助于描述对象?
- 识别每个对象的操作。这些很可能是动词。
- 确定从对像到对象的关系。这些很可能是动词。
例如:
- 一个人是用名字识别的人。
- 等级是一封信确定的成就。
- 学生是一个赚取成绩的人。
语法和语义
编程语言的语法是管理其形式的生产规则的列表。编程语言的形式是其声明,表达式和语句的正确放置。语言的语法补充是其语义。语义描述了各种句法构造所附的含义。句法结构可能需要语义描述,因为形式可能具有无效的解释。而且,不同的语言可能具有相同的语法;但是,他们的行为可能不同。
语言的语法通过列出生产规则正式描述。尽管自然语言的语法非常复杂,但英语的一部分可以具有此生产规则清单:
- 一个句子由名词词组成,然后是动词词语;
- 名词 -由文章组成,然后是形容词,然后是名词。
- 动词 -由动词和名词词组成。
- 一篇文章是“”;
- 形容词是“大”或
- 形容词是“小”;
- 名词是“猫”或
- 名词是“鼠标”;
- 动词是“吃的”;
大胆的字样被称为“非终端”。 “单引号”中的单词称为“终端”。
从此生产规则清单中,可以使用一系列替代品形成完整的句子。该过程是用有效的非末端或有效终端替换非终端。替换过程重复直到仅终端保留为止。一个有效的句子是:
- 句子
- 名词词语动词词语
- 文章形容词名词动词短语
- 形容词名词动词短语
- 大名词动词词语
- 大猫动词-
- 大猫动词名词词语
- 大猫吃名词-
- 大猫吃文章形容词名词_
- 大猫吃形容词名词_ _
- 大猫吃小名词_ _
- 大猫吃小鼠标_ _
但是,另一种组合导致无效的句子:
- 小鼠吃了大猫_ _
因此,必须使用语义来正确描述饮食活动的含义。
一种生产规则清单方法称为Backus -Naur形式(BNF)。 BNF描述了一种语言的语法,并且本身俱有语法。该递归定义是元语言的一个例子。 BNF的语法包括:
-
::=
当非终端是正确的时,它的转化为由[n]组成。它的转化为何时终端是正确的。 -
|
转换为OR 。 -
<
和>
围绕非末端的哪个。
使用BNF,英语的一部分可以具有此生产规则清单:
<sentence> ::= <noun-phrase><verb-phrase>
<noun-phrase> ::= <article><adjective><noun>
<verb-phrase> ::= <verb><noun-phrase>
<article> ::= the
<adjective> ::= big | small
<noun> ::= cat | mouse
<verb> ::= eats
使用BNF,签名整数具有生产规则清单:
<signed-integer> ::= <sign><integer>
<sign> ::= + | -
<integer> ::= <digit> | <digit><integer>
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
注意递归生产规则:
<integer> ::= <digit> | <digit><integer>
这允许无限的可能性。因此,必须使用语义来描述数字数量的限制。
请注意生产规则中领先的零可能性:
<integer> ::= <digit> | <digit><integer>
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
因此,有必要描述需要忽略领先零的语义。
软件工程和计算机编程
软件工程是生产优质软件的多种技术。计算机编程是编写或编辑源代码的过程。在正式的环境中,系统分析师将收集经理人有关组织的所有流程以自动化的信息。然后,该专业人员为新系统或修改后的系统准备了详细的计划。该计划类似于建筑师的蓝图。
绩效目标
系统分析师的目的是在正确的时间将正确的信息传递给合适的人。实现此目标的关键因素是:
- 输出的质量。输出对决策有用吗?
- 输出的准确性。它反映了真实情况吗?
- 输出的格式。输出容易理解吗?
- 输出的速度。实时与客户沟通时,时间敏感的信息很重要。
成本目标
实现绩效目标应与所有费用保持平衡,包括:
- 发展成本。
- 独特成本。可重复使用的系统可能很昂贵。但是,它可能比有限使用系统更喜欢。
- 硬件成本。
- 运营成本。
应用系统开发过程将减轻公理:在此过程中稍后检测到错误,校正越昂贵。
瀑布模型
瀑布模型是系统开发过程的实施。正如瀑布标签所暗示的那样,基本阶段相互重叠:
- 调查阶段是了解潜在的问题。
- 分析阶段是了解可能的解决方案。
- 设计阶段是计划最佳解决方案。
- 实施阶段是编程最佳解决方案。
- 维护阶段持续整个系统的生命。部署后可能需要更改该系统。可能存在故障,包括规格故障,设计故障或编码故障。可能需要改进。适应可能需要对不断变化的环境做出反应。
计算机程序员
计算机程序员是负责编写或修改源代码以实现详细计划的专家。可能需要一个编程团队,因为大多数系统太大而无法由单个程序员完成。但是,将程序员添加到项目中可能不会缩短完成时间。相反,它可能会降低系统的质量。为了有效,需要将程序模块定义并分配给团队成员。此外,团队成员必须以有意义有效的方式相互互动。
计算机程序员可以在单个模块中的小型:编程中编程。机会是一个模块将执行位于其他源代码文件中的模块。因此,计算机程序员可以在大型编程中进行编程:编程模块,以便他们有效地彼此搭配。整个编程包括为应用程序编程接口(API)做出贡献。
程序模块
模块化编程是一种完善命令式语言程序的技术。精制程序可能会减少软件规模,单独的责任,从而减轻软件老化。程序模块是一系列语句序列,这些语句在一个块内并通过名称识别在一起。模块具有功能,上下文和逻辑:
- 模块的功能就是它的作用。
- 模块的上下文是要执行的元素。
- 模块的逻辑是它如何执行功能。
该模块的名称应首先通过其函数派生,然后是通过其上下文得出的。它的逻辑不应成为名称的一部分。例如,function compute_square_root( x )
或者function compute_square_root_integer( i : integer )
是适当的模块名称。然而,function compute_square_root_by_division( x )
不是。
模块内的相互作用程度是其内聚力水平。凝聚力是对模块名称及其功能之间关系的判断。模块之间的相互作用程度是耦合水平。耦合是对模块上下文与所执行的要素之间关系的判断。
凝聚
从最差到最佳的凝聚力水平是:
- 巧合的凝聚力:如果模块执行多个函数,则具有重合的内聚力,并且功能完全无关。例如,
function read_sales_record_print_next_line_convert_to_float()
。如果管理执行愚蠢的规则,则在实践中会发生巧合的凝聚力。例如,“每个模块都有35至50个可执行语句。” - 逻辑内聚:一个模块具有一系列功能,则具有逻辑内聚力,但仅执行其中一个功能。例如,
function perform_arithmetic( perform_addition, a, b )
. - 时间凝聚力:如果模块执行与时间相关的函数,则具有时间内聚力。一个例子,
function initialize_variables_and_open_files()
。另一个例子,stage_one()
,stage_two()
, ... - 程序内聚力:如果模块执行多个松散相关的功能,则具有程序内聚力。例如,
function read_part_number_update_employee_record()
. - 通信凝聚力:如果模块执行多个密切相关的功能,则具有通信凝聚力。例如,
function read_part_number_update_sales_record()
. - 信息凝聚力:如果执行多个功能,则一个模块具有信息凝聚力,但是每个功能都有自己的进入和出口点。此外,功能共享相同的数据结构。面向对象的类在此级别工作。
- 功能凝聚力:如果模块仅在局部变量上实现单个目标,则具有功能内聚。此外,它可能在其他情况下可以重复使用。
耦合
从最差到最佳的耦合的水平是:
- 内容耦合:如果模块修改了另一个函数的局部变量,则具有内容耦合。 COBOL用于使用Alter动词来执行此操作。
- 通用耦合:如果模块修改全局变量,则具有通用耦合。
- 控制耦合:如果另一个模块可以修改其控制流,则模块具有控制耦合。例如,
perform_arithmetic( perform_addition, a, b )
。相反,控制应放在返回对象的构成上。 - 邮票耦合:如果修改了数据结构的元素,则模块具有邮票耦合。面向对象的类在此级别工作。
- 数据耦合:如果需要所有输入参数,并且没有修改它们的所有输入参数,则模块具有数据耦合。此外,该函数的结果将作为一个对象返回。
数据流分析
数据流分析是一种设计方法,用于实现功能凝聚力和数据耦合模块。该方法的输入是数据流图。数据流图是一组代表模块的椭圆形。每个模块的名称都显示在其椭圆形内。模块可以在可执行级别或功能级别。
该图还具有将模块相互连接的箭头。指向模块的箭头代表一组输入。每个模块只能从中指出一个箭头以表示其单个输出对象。 (可选,另外一个例外箭头指出。)雏菊链的链条将传达整个算法。输入模块应启动图。输入模块应连接到变换模块。变换模块应连接到输出模块。
功能类别
计算机程序可以按功能线进行分类。主要功能类别是应用程序软件和系统软件。系统软件包括操作系统,该操作系统将计算机硬件与应用程序软件相结合。操作系统的目的是提供一个以方便有效的方式执行应用程序软件的环境。应用程序软件和系统软件都执行实用程序程序。在硬件级别上,一个微型编码程序控制着整个中央处理单元的电路。
应用程序软件
应用软件是解锁计算机系统潜力的关键。企业应用程序软件捆绑会计,人员,客户和供应商应用程序。示例包括企业资源计划,客户关系管理和供应链管理软件。
企业应用程序可以在内部开发为一种独一无二的专有软件。另外,它们可以作为现成软件购买。可以修改购买的软件以提供自定义软件。如果应用程序是自定义的,则使用公司的资源,或者将资源外包。外包软件开发可能来自原始软件供应商或第三方开发人员。
内部软件的潜在优势是功能,并且可能会为规范开发报告。管理层也可能参与开发过程并提供一定程度的控制。管理层可以决定抵消竞争对手的新计划或实施客户或供应商的要求。合并或收购可能需要更改企业软件。内部软件的潜在缺点是时间,资源成本可能是广泛的。此外,有关特征和性能的风险可能会迫在眉睫。
现成软件的潜在优势是可识别的,应该满足基本需求,并且其性能和可靠性具有记录。现成软件的潜在缺点是它可能具有使最终用户混淆的不必要功能,它可能缺乏企业需求的功能,并且数据流可能与企业的工作流程不符。
从经济上获得定制企业应用程序的一种方法是通过应用程序服务提供商。专业公司提供硬件,自定义软件和最终用户支持。他们可能会加快新应用程序的开发,因为他们拥有熟练的信息系统人员。最大的优势是它可以从内部资源中释放出人员配备和管理复杂的计算机项目。许多应用程序服务提供商针对具有有限信息系统资源的小型,快速增长的公司。另一方面,拥有主要系统的大型公司可能会建立其技术基础设施。一种风险是必须信任具有敏感信息的外部组织。另一个风险是必须信任提供商的基础架构可靠性。
作业系统
操作系统是支持计算机基本功能的低级软件,例如调度过程和控制外围设备。
在1950年代,也是运营商的程序员将编写程序并运行它。程序完成执行后,可能已经打印了输出,或者可能已将其打到纸带或卡上以供以后处理。该程序经常不起作用。然后,程序员查看了控制台灯,并摆弄了控制台开关。如果不幸的是,将进行记忆打印量以进行进一步研究。在1960年代,程序员通过使操作员的工作自动化减少了浪费时间。一个称为操作系统的程序始终保存在计算机中。
一词操作系统可以指两个软件级别。操作系统可以参考管理过程,内存和设备的内核程序。更广泛地说,操作系统可以涉及中央软件的整个软件包。该软件包包括内核程序,命令行解释,图形用户界面,实用程序程序和编辑器。
内核程序
内核的主要目的是管理计算机的有限资源:
- 内核程序应执行进程计划。当选择程序进行执行时,内核会创建一个过程控制块。但是,执行程序仅在一个时间切片中可以独家访问中央处理单元。为了为每个用户提供连续访问的外观,内核迅速抢占每个过程控制块以执行另一个过程。系统开发人员的目标是最大程度地减少调度延迟。
- 内核程序应执行内存管理。
- 当内核最初将可执行文件加载到内存中时,它将地址空间从逻辑上划分为区域。内核维护一个主区域表和许多每个程序区域(Prepergion)表 - 每个运行过程一个。这些表构成虚拟地址空间。主区域表用于确定其内容在物理内存中的位置。预先表格允许每个过程都有自己的程序(文本)预审前,数据预先和堆栈预审前。
- 该程序预先存储机器指令。由于机器指令没有更改,因此程序预先可以通过同一可执行文件的许多过程共享。
- 为了节省时间和内存,内核可以仅加载磁盘驱动器的执行指令块,而不是整个执行文件。
- 内核负责将虚拟地址转换为物理地址。内核可以从内存控制器请求数据,而是接收页面故障。如果是这样,内核将访问内存管理单元以填充物理数据区域并转换地址。
- 内核应进程要求从堆中分配内存。当过程结束时,该过程可能会请求释放该过程。如果该过程未经要求释放所有分配的内存而退出,则内核执行垃圾收集以释放内存。
- 内核还确保一个过程仅访问其内存,而不是内核或其他过程的内存。
- 内核程序应执行文件系统管理。内核具有创建,检索,更新和删除文件的说明。
- 内核程序应执行设备管理。内核提供了标准化和简化鼠标,键盘,磁盘驱动器,打印机和其他设备的接口。此外,如果两个进程同时请求,则内核应仲裁对设备的访问。
- 内核程序应执行网络管理。内核代表流程传输和接收数据包。一项关键服务是找到通往目标系统的有效途径。
- 内核程序应提供系统级功能,供程序员使用。
- 程序员通过相对简单的接口访问文件,该接口又执行相对复杂的低级I/O接口。低级接口包括文件创建,文件描述符,搜索文件,物理阅读和物理写作。
- 程序员通过相对简单的接口创建进程,该接口又执行相对复杂的低级接口。
- 程序员通过相对简单的界面执行日期/时间算术,该接口又执行相对复杂的低级时间接口。
- 内核程序应在执行过程之间提供通信渠道。对于大型软件系统,可能需要将系统设计为较小的流程。流程可以通过发送和接收信号相互通信。
最初,操作系统是在组装中编程的;但是,现代操作系统通常用C , Objective-C和Swift等高级语言编写。
公用事业计划
公用事业计划旨在帮助系统管理和软件执行。操作系统执行硬件实用程序,以检查磁盘驱动器,内存,扬声器和打印机的状态。实用程序可以优化文件在拥挤的磁盘上的位置。系统公用事业程序监视硬件和网络性能。当指标超出可接受的范围时,会生成触发警报。
实用程序包括压缩程序,以便将数据文件存储在较小的磁盘空间上。压缩程序还节省了通过网络传输数据文件时的时间。实用程序可以对数据集进行整理并合并。实用程序检测计算机病毒。
Microcode程序
MicroCode程序是控制软件驱动计算机的数据路径的底层解释器。 (硬件的进步将这些操作迁移到硬件执行电路。)MicroCode指令允许程序员更轻松地实现数字逻辑级别- 计算机的真实硬件。数字逻辑水平是计算机科学与计算机工程之间的边界。
逻辑门是一个微小的晶体管,可以返回两个信号之一:打开或关闭。
这五个大门构成了二进制代数的基础 - 计算机的数字逻辑功能。
MicroCode指令是助图程序员可能用来执行数字逻辑功能,而不是在二进制代数中形成它们。它们存储在中央处理单元(CPU)控制店中。这些硬件级指令在整个数据路径中移动数据。
微型指导周期开始时,当Microsequencer使用其微序列计数器来从随机访问存储器中获取下一个机器指令。下一步是通过将适当的输出线选择到硬件模块来解码机器指令。最后一步是使用硬件模块的一组门执行指令。
执行算术的说明通过算术逻辑单元(ALU)。 ALU具有执行基本操作的电路,以添加,移动和比较整数。通过通过ALU结合和循环基本操作,CPU执行其复杂的算术。
MicroCode指令在CPU和内存控制器之间移动数据。内存控制器微码指令操纵两个寄存器。内存地址寄存器用于访问每个内存单元的地址。内存数据寄存器用于设置和读取每个单元格的内容。
MicroCode指令在CPU和许多计算机总线之间移动数据。磁盘控制器巴士将硬盘驱动器写入并读取。数据还通过外围组件互连Express BUS在CPU和其他功能单元之间移动。