编程语言-Pascal
编程语言-Pascal
Pascal 是一种命令式和过程式编程语言,由 Niklaus Wirth 设计,是一种小型、高效的语言,旨在鼓励使用结构化编程和数据结构化的良好编程实践。它以法国数学家、哲学家和物理学家布莱斯·帕斯卡 (Blaise Pascal) 的名字命名。
Pascal语言是在ALGOL 60语言的基础上发展起来的。
ALGOL 60
ALGOL (Algorithmic Language)(算法语言的缩写),ALGOL 60(Algorithmic Language 1960 的缩写)。作为继ALGOL 58之后的革新之作,ALGOL 60在结构化编程领域迈出了关键性的一步。前者引入了代码块的概念,并使用begin和end关键字对它们进行界定,这一特性极大地促进了后续编程实践的发展。而在ALGOL 60中,函数定义的概念得到了实现,允许递归调用,这无疑是对编程世界的一大贡献。此外,ALGOL 60还开创性地支持了函数嵌套定义,即一个函数内部可以定义另一个函数,并采用词法作用域规则。这一特性为后来众多编程语言的设计提供了灵感源泉,其中包括CPL、PL/I、Simula、BCPL、B、Pascal以及C语言等。
代码示例
algol 60
procedure Absmax(a) Size:(n, m) Result:(y) Subscripts:(i, k); value n, m; array a; integer n, m, i, k; real y;comment The absolute greatest element of the matrix a, of size n by m, is copied to y, and the subscripts of this element to i and k;begin integer p, q; y := 0; i := k := 1; for p := 1 step 1 until n do for q := 1 step 1 until m do if abs(a[p, q]) > y then begin y := abs(a[p, q]); i := p; k := q endend Absmax
对于许多 Pascal 和 Delphi 的开发者来说,看到 ALGOL 60 的代码无疑会勾起一段段温馨的记忆。
从上面的例子可以看出,ALGOL 60 与 Pascal 及 Delphi 在语法结构上有许多相似之处,如:
- 程序块结构
:
begin...end
的使用。 - 变量声明 :在开始处明确声明所有变量及其类型。
- 控制结构
:如
for
循环、if
语句等,都采用了类似的语法形式。
这种相似性反映了 Pascal 和 Delphi 对 ALGOL 60 继承和发展的重要特征,也让熟悉这两种语言的开发者在阅读 ALGOL 60 的代码时感到亲切。
尽管ALGOL 60在学术研究领域享有盛誉,但在商业应用方面却未能大放异彩。主要由美国及欧洲的研究型计算机科学家们推动,ALGOL 60成为了算法出版的标准语言,并对后续语言的设计产生了深远影响。然而,由于其描述中缺乏标准的输入输出设施,加之大型计算机制造商对其兴趣寥寥,导致其在商业领域的推广受到了限制。
ALGOL X:理想与现实的碰撞
ALGOL X 作为 ALGOL 60 的后继者,承载着解决早期编程语言中语义模糊和语法复杂的许多问题,特别是缺乏标准化的字符串系统。Wirth 和 Tony Hoare 提交了一组保守的修改,以添加字符串并清理一些语法。这些被认为太小了,不值得用作新标准 ALGOL,因此 Wirth 为该语言编写了一个编译器,命名为 ALGOL W。然而,理想与现实之间的差距使得 ALGOL X 项目因各方意见不合而进展缓慢,最终未能成为国际认可的标准。在此背景下,ALGOL N 和 ALGOL W 作为替代方案浮出水面,特别是 ALGOL W。
当 ALGOL X 计划陷入僵局时,沃思及其团队并没有放弃对更优秀编程语言的追求。相反,他们从 ALGOL X 的经验教训中汲取灵感,开始 ALGOL W 的开发工作。ALGOL W 在继承 ALGOL 60 优点的同时,进一步简化了语言结构,增强了功能,并且更加关注用户的实际需求。这些改进不仅提高了代码的可读性和可维护性,也为后来的编程语言设计树立了典范。
Pascal:传承与创新的结晶
Pascal 受到 ALGOL W 工作的影响,其明确目标是以结构化方式教授编程和开发系统软件。
1970 年,沃思团队将 ALGOL W 进行了优化,并赋予了它一个新的名字——Pascal。这种新的语言不仅继承了 ALGOL 60 的诸多优势,还针对 ALGOL W 中的一些不足进行了修正,如增加了数据抽象能力和引入了枚举类型等特性。
在ALGOL的标量和数组之上,Pascal可以定义复杂的数据类型,并构建动态和递归的数据结构,如列表、树和图。帕斯卡具有强烈的数据类型约束,这意味着没有显式的转换,一种类型的数据不能被转换或解释为另一种类型。与C语言(以及大多数C系语言)不同的是,Pascal允许嵌套的过程定义达到任意深度,并且还允许在子程序(过程和函数)内定义和声明大多数类型的对象。因此,一个程序在语法上类似于一个单一的过程或函数。这与ALGOL 60的块结构类似,但限制了除过程和函数外的任意块语句。
Pascal 方言:从经典到多样化的演变
自 Wirth 设计的 Pascal 问世以来,这一优雅的编程语言不断演化,逐渐形成了多个版本,被形象地称为“Pascal 方言”。这些方言是指在原始 Pascal 基础上进行扩展和改进后形成的新版本。
Pascal-P 系统:传播与自我编译
Wirth 设计的 Pascal-P 编译器,旨在作为一种教育工具来推广 Pascal 语言。它采用了一个精简的 Pascal 子集,该子集设计为可以自我编译的最小语言。这种方法的目的是使编译器能够在任何平台上引导自身,随后逐步扩展到完整的 Pascal 语言。尽管许多编译器都采用了这种方法,但 UCSD Pascal 是一个值得注意的例外。
UCSD Pascal:面向微处理器的优化
UCSD Pascal 基于 Pascal-P2,但它保留了语言的子集状态,以便在内存受限的新型微处理器上运行得更为高效。此外,UCSD 团队还将 Pascal-P2 解释器转换为一种“字节机”架构,更好地适应了面向字节的微处理器环境。UCSD Pascal 成为了多种系统的基石,其中包括 Apple Pascal。
Borland Pascal:独立发展的分支
尽管 Borland Pascal 并不是基于 UCSD 的代码库开发的,但它在 UCSD Pascal 流行的时代出现,并且在功能上与后者高度相似。这标志着 Pascal 发展的一个重要阶段,最终演变为 Delphi Pascal 和开源编译器 FPC/Lazarus 的诞生。
ISO 标准与现代 Pascal 方言
1983 年发布的 Pascal 的 ISO 标准(ISO 7185)为 Pascal 在主机、小型计算机以及 IBM-PC 上的广泛应用奠定了基础,支持从 16 位到 32 位的计算环境。进入 21 世纪,最流行的两种 Pascal 方言分别是遵循 ISO 7185 标准的版本和基于 Delphi/Turbo Pascal(两个版本主要由 Borland 开发,且大多兼容)的版本。
Pacal语言结构
Pascal 语言以其清晰简洁的设计风格著称,最初形式下是一种纯粹的过程式语言。它不仅继承了 ALGOL 类似的控制结构,如
if
、
then
、
else
、
while
、
for
和
case
,支持单个语句或由
begin-end
组成的语句块。除了传统的控制结构外,Pascal 还引入了多种数据组织方式,这些特性在 ALGOL 60 中是没有的,例如:
- 记录(Records) :用于组合不同类型的数据项,类似于其他语言中的结构体。
- 变体字段(Variants) :允许记录中包含互斥的字段,根据需要选择其中一个使用。
- 指针(Pointers) :直接操作内存地址的能力,增强了语言的灵活性。
- 枚举(Enumerations) :定义一组命名的常量集合,提高了代码的可读性和可维护性。
- 集合(Sets) :表示一组元素的数据类型,支持高效的集合运算。
- 过程指针(Procedure Pointers) :允许将过程作为参数传递给其他函数,增强了语言的功能性编程能力。
Pascal 程序以关键字 program
开始,并附带一个外部文件描述符列表作为参数(在 Turbo Pascal 等环境中这不是必需的);接着是包含在 begin
和 end
关键字之间的主程序块。分号用来分隔语句,整个程序(或单元)以句点(即.
)结束。Pascal 源代码中不区分字母的大小写。
源代码示例:
program HelloWorld(output);begin WriteLn('Hello, World!') {No ";" is required after the last statement of a block - adding one adds a "null statement" to the program, which is ignored by the compiler.}end.
数据类型
数据类型 | 变量能够存储的值类型 |
---|---|
integer | integer (whole) numbers 整数 |
real | floating-point numbers 浮点数 |
Boolean | the values True or False 值 True 或 False |
char | a single character from an ordered character set 有序字符集中的单个字符 |
set | equivalent to an array of Boolean values 等效于 Boolean 值的数组 |
array | a countable group of any of the preceding data types or records 一个可数的由前文提到的数据类型或记录组成的群体 |
record | A collection of any of the preceding data types 上述任何数据类型的集合 |
string | a sequence or “string” of characters is declared as a “packed array of char” with a starting index of 1. These can be assigned string constants and individual characters can be accessed as elements of the array. 一个字符序列或“字符串”被声明为一个“打包的字符数组”,其起始索引为1。这些字符串可以被赋予字符串常量,并且可以像访问数组元素一样访问单个字符。 |
基本类型(除布尔类型外)的值范围由具体实现定义,并且提供了一些数据转换函数。对于实数转整数的转换,提供了以下几种函数:round(使用银行家舍入法四舍五入到整数)和trunc(向零舍入)。
程序员可以使用Pascal语言的类型声明机制,基于预定义的类型来自由定义其他常用的数据类型(例如字节、字符串等),例如:
type byte = 0..255; signed_byte = -128..127; string = packed array[1..255] of char;
常用的数据类型如字节(byte)和字符串(string)在许多实现中已经被预先定义好了。这意味着开发者可以直接使用这些类型,而不需要关心底层的具体实现细节。这种设计简化了编程过程,使得开发者能够更加专注于逻辑实现而非底层数据管理。
通常情况下,系统会使用一个字来存储数据。例如,字节(byte)类型可能会存储在一个机器整数中——可能是32位——而不是一个8位的值。Pascal本身并不包含允许对基本存储类型进行更细粒度定义的语言元素。这种能力被包含在了多个Pascal的扩展和后续语言中。
packed 关键词编译器在处理结构化数据类型如集合(sets)、数组(arrays)和记录(records)时,使用最有效的存储方法来存储这些数据,而不是为每个元素分配一个完整的字(word)。这种方式可以显著减少内存使用量,从而提高程序的整体效率。
Subrange types 子范围类型
任何序数数据类型(除实数外的简单类型)都可以定义子范围(subranges)
var x : 1..10; y : 'a'..'z';
Set types Set 类型
与当时的其他编程语言相比,Pascal 支持一种集合类型:
var Set1 : set of 1..10; Set2 : set of 'a'..'z';
集合是现代数学的基本概念,并且可以用于许多算法中。这种特性非常有用,在支持集合的语言中往往比没有这种特性的语言实现起来更快更高效。例如,在许多Pascal编译器中就是如此:
if i in [5..10] then ...
执行速度超过:
if (i > 4) and (i < 11) then ...
不连续值的集合在性能和可读性方面尤其有用:
if i in [0..3, 7, 9, 12..15] then ...
Record types 记录类型
Pascal 记录类型的示例:
type car = record length: integer; width: integer end;
变体记录类型的示例:
type Shape = (Circle, Square, Triangle); Dimensions = record case Figure: Shape of Circle: (Diameter: real); Square: (Width: real); Triangle: (Side: real; Angle1, Angle2: 0..360) end;
变体记录允许记录的多个字段相互重叠以节省空间。
Type declarations 类型声明
可以使用类型声明从其他类型定义新类型:
type x = integer; y = x;...
此外,复杂类型可以从简单类型构造:
type a = array[1..10] of integer; b = record x : integer; y : char {extra semicolon not strictly required} end; c = file of a;
File type 文件类型
type a = file of integer; b = record x : integer; y : char end; c = file of b; var fa : a; // 声明一个类型为a的文件fa fb : b; // 声明一个类型为b的记录fb fc : c; // 声明一个类型为c的文件fc i : integer; // 声明一个整数i ch : char; // 声明一个字符ch
procedure Example;begin assign(fa, 'file_of_integers.dat'); // 设置fa指向名为file_of_integers.dat的文件 reset(fa); // 打开文件用于读取 read(fa, i); // 从fa读取一个整数到i close(fa); // 关闭文件fa assign(fc, 'record_file.dat'); // 设置fc指向名为record_file.dat的文件 rewrite(fc); // 创建或打开文件用于写入,如果文件存在则会被覆盖 fb.x := 5; // 给记录fb的x字段赋值 fb.y := 'A'; // 给记录fb的y字段赋值 write(fc, fb); // 将记录fb写入文件fc close(fc); // 关闭文件fcend;
在Pascal程序中,一旦定义了这些文件类型,你就可以声明变量来指向这些文件,并对其进行读写操作。
文件类型
a
:a
是一个文件类型,它的元素(即文件中的每一个条目)都是整数类型。这意味着你可以把这个文件看作是一个整数的序列,当你从这个文件读取数据时,每次读取的是一个整数。
记录类型
b
:- 这是一个用户自定义的数据结构,包含了两个不同的字段:
x
是一个整数类型,而y
是一个字符类型。记录类型允许你定义包含不同类型字段的复杂数据类型。
- 这是一个用户自定义的数据结构,包含了两个不同的字段:
文件类型
c
:c
也是一个文件类型,但是它的元素是记录类型b
。这意味着这个文件中的每一项都是由一个整数和一个字符组成的记录。
Pointer types 指针类型
Pascal 支持使用指针:
type pNode = ^Node; Node = record a : integer; b : char; c : pNode end;var NodePtr : pNode; IntPtr : ^integer;
在这里,变量 NodePtr 是一个指向 Node 类型的指针,而 Node 是一种记录类型。在声明前就可以使用指针,这称为前置声明,是对必须先声明后使用的规则的一个例外。
要创建一个新的记录,并将数值 10 和字符 A 分别赋值给记录中的字段 a 和 b,同时将指针 c 初始化为空指针(在 Pascal 中表示为 “NIL”),相应的语句应该是:
new(NodePtr);...NodePtr^.a := 10;NodePtr^.b := 'A';NodePtr^.c := nil;...
也可以使用
with
语句完成此操作,如下所示:
new(NodePtr);...with NodePtr^ dobegin a := 10; b := 'A'; c := nilend;...
在 with 语句的范围内,a 和 b 引用记录指针 NodePtr 的子字段,而不是记录 Node 或指针类型 pNode。
指针必须具有关联的类型,指向不同类型的指针不兼容(例如,指向 char 的指针与指向整数的指针不兼容)。用 dispose 函数动态释放动态引用空间。
Control structures 控制结构
Pascal 是一种结构化的编程语言,这意味着控制流被结构化为标准语句,通常没有 ‘goto’ 命令。
while a <> b do WriteLn('Waiting');
if a > b then WriteLn('Condition met') {no semicolon allowed before else}else WriteLn('Condition not met');
for i := 1 to 10 do {no semicolon here as it would detach the next statement} WriteLn('Iteration: ', i);
repeat a := a + 1until a = 10;
case i of 0 : Write('zero'); 1 : Write('one'); 2 : Write('two'); 3,4,5,6,7,8,9,10: Write('?')end;
Procedures and functions 过程和功能
Pascal 语言将程序结构划分为过程(procedures)和函数(functions)。通常,过程主要用于其副作用(如修改变量或输出数据),而函数则主要用于返回一个值。
program Printing(output);
var i : integer;
procedure PrintAnInteger(j : integer);begin ...end;
function triple(const x: integer): integer;begin triple := x * 3end;
begin { main program } ... PrintAnInteger(i); PrintAnInteger(triple(i))end.
过程和函数可以嵌套到任何深度,而 ‘program’ 结构是逻辑上最外层的块。默认情况下,参数按 value 传递。如果 ‘var’ 位于参数名称之前,则通过引用传递该参数。
每个过程或函数都可以有自己的 goto 标签、常量、类型、变量以及其他过程和函数的声明,这些声明都必须按此顺序排列。但是,在某些方言(如 Delphi)中,声明部分的严格排序要求已放宽。
Semicolons as statement separators
分号作为语句分隔符
Pascal 语言从 ALGOL 语言中借鉴了许多语法特性,包括使用分号作为语句分隔符。这与 PL/I 和 C 等其他语言形成了对比,后两者是将分号用作语句终止符。在记录类型声明、代码块或 case 语句的结束关键字前;repeat 循环中的 until 关键字前;以及 if 语句中的 else 关键字前,并不需要使用分号。
在早期版本的 Pascal 中,额外的分号是不被允许的。然而,在 1973 年的修订报告中引入了类似 ALGOL 的空语句,并且在 ISO 7185:1983 中对语言进行了后续修改,现在大多数情况下允许使用可选的分号。但在 if 语句中的 else 关键字前仍然不允许直接使用分号,因为 else 后跟的是单一语句而非语句序列。在嵌套 if 语句的情况下,不能通过在嵌套 if 后加上分号来解决悬空 else 问题(即内部 if 没有 else 子句而外部 if 有),因为这样实际上会终止两个 if 条件。相反,必须使用显式的 begin…end 块来实现这一目的。
Pascal在20世纪70年代变得非常成功,尤其是在迅速发展的微型计算机市场上。随着70年代末微型计算机领域的兴起,许多微型计算机也都有了Pascal编译器。在80年代,它被广泛用于大学级别的编程课程教学,并在同一时期也被用来编写商业软件。
资料来源:
Pascal (programming language) - Wikipedia