文艺复兴

CCF在去年正式把pascal在高中竞赛中踩在脚底,那我就偏要玩玩这个语言。

现在,在网上,关于Pascal的讨论已经可以说只剩断壁残垣了。(我甚至能找到比我还大的帖子(悲)) 不是很容易找到那种系统又准确的教程了。我就到处整理学习了一下,并把他分享出来。

我也是初学这门语言,如果有些点不准确感谢纠正:)

语言细节一篇文章大概是讲不完的,那我就讲讲简单的语法吧。

基本程序结构

1
2
3
4
5
6
7
8
program {程序名字}
.....
{可以有一大堆不重要的声明}

begin
......
{主要执行这里面的代码}
end.

begin前面的声明,如果用不上,都可以省略。

program这一句也行。

注意,Pascal不区分大小写(字符串除外)。

注释

Pascal 中注释使用大括号括起。

1
2
3
4
5
6
7
{单行注释}
{*




*}

输入输出语句

输出:

一般使用 write 输出

1
2
3
write('Hello!');    {输出}
writeln('Hello'); {输出并换行}
write('Hello!',chr(10)); {与上一行作用相同}

Pascal里可以用 chr(10) 作为换行符

chr是将数字转换为ASCII字符的函数

10在ASCII中对应换行符。

write可以输出字符串,变量等,注意字符串用 ‘ 单引号括起

用 ‘,’ 隔开输出内容表示接在后面输出,一般输出不同种类数据时使用

write还可以自定义场宽(如果定义的场宽大于实际输出长度,则留空。如果小于,则按原样输出),
如果输出的是real类型还可以定义保留小数位数(是四舍五入)。用’:’分隔,第一个参数是场宽,第二个是小数位,如下: (当然writeln也可以)

1
2
3
4
5
6
7
8
9
10
11
write(6:7);
{输出:' 6 '}

write(6,0);
{输出:'6'}

write(114.514:0:1);
{输出:'114.5'}

write(114.594:0:1);
{输出:'114.6',四舍五入}

输入:

一般使用 read 输入

1
2
read({变量名列表});
read(a,b); {读入a,b 忽略空格和换行符}

还有readln()函数也可以读入。readln和read的区别是readln读入后切换到下一行,如:
1
2
3
输入数据:
1 2 3 4
5 6

用read读入四个值
1
2
read(a,b);
read(c,d);

之后a = 1,b = 2,c = 3,d = 4;
而用readln读入
1
2
readln(a,b);
read(c,d);

之后a = 1,b = 2,c = 5,d = 6;

注意:

  • 变量类型与输入的数据类型必须一致
  • 输入顺序与变量顺序要一致

变量及数据处理

要在程序var区域定义变量

1
2
3
4
5
6
7
program aPlusB;
{简单的A+B程序}
var a,b:longint;
begin
readln(a,b);
writeln(a+b);
end.

Pascal的标准数据类型

整型

表示整数
|类型|数值范围|占用字节|二进制意义|
|:—-:|:————-:|:———-:|:—-:|
|shortint|-128~127|1|带符号8位|
|integer|-32768~32767|2|带符号16位|
|longint|-2147483648~2147483647|4|带符号32位|
|byte|0~255|1|无符号8位|
|word|0~65535|2|无符号16位|

实型

表示实数
|类型|数值范围|占字节数|有效位数|
|:—-:|:—-:|:—-:|:—-:|
|real|2.9e-39~1.7e38|`6|11~12|
|single|1.5e-45~3.4e38|4|7~8|
|double|5.0e-324~1.7e308|8|15~16|
|extended|3.4e-4932~1.1e4932|10|19~20|
|comp|-2^63+1~2^63-1|8|19~20|

布尔值

boolean,也叫逻辑值。只有两个值: true or false.

字符型

char, 必须用单引号括起来。只有一个值,否则就是字符串。

字符串

可以用string定义,也可以使用字符数组。

常量

常量(constant)在pascal中不像C中一样用const修饰变量(或者直接#define),pascal的常量在const下声明,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{求一个圆的面积}
const {在此定义常量}
PI = 3.141592654;
var {在此定义变量}
r, s : real;
begin
writeln('r ?');
readln(r);
s := PI * sqr(r);
{使用常量,sqr()是pascal求平方函数}

writeln('S: ',s:0:2);
end.

枚举类型

刷OJ也没见过多少人用(bushi)

pascal的枚举类型可以用type声明一下,再在var定义一个此类型的对象使用。如下:

1
2
3
4
5
6
7
8
type
sex = {male,female,yyy}; {声明枚举类型sex}
var
val : sex; {定理sex类型的变量}
begin
val := yyy
writeln('yyy's sex is ',yyy);
end.

这个枚举变量和C语言还是有区别的,他是输出字符串而不是输出数字,因为不是很常用,这里也不深究了。

作用域

在程序文件开头的var部分定义的是全局变量

我们当然可以定义各个函数的局部变量,在后面写到函数的时候再讲

运算符

赋值运算符

pascal中的赋值为 ‘ := ‘,而’ = ‘是判断相等的关系运算符。

(应该不止我一个人容易错在这)

算术运算符

进行基本算术运算。

‘+’表示加法

‘-‘表示减法

‘表示乘法

‘/‘表示除法,*但不同于C的是他返回的是实型值
,无论两个操作数是整型还是实型

如: 5 / 2 = 2.50,4 / 2 = 2.00。

‘div’也表示除法,但只能用于整型,这时相当于C中的’/‘。

如: 5 div 2 = 1, 4 div 2 = 2。

‘mod’表示取余运算,相当于C中的’%’。

关系运算符

关系运算符只有两个返回值,true or false.

‘=’ 判断两个操作数是否相等,相当于C中’==’。

‘<>’判断两个操作数是否不等,相当于C中’!=’。

‘>’ 大于号,’>=’ 大于等于号

‘<’ 小于号,’<=’ 小于等于号

布尔运算符

处理布尔操作符并返回布尔结果,像逻辑上的与,或,非

and 表示与运算 相当于C中的 “&&”。

or 表示或运算 相当于C中的 “||”。

not 表示非运算 相当于C中的 “!”。

至于我还在其他地方看到的and then,or else运算符,我在我的linux和win上的fpc编译器上都是过了,不行。我觉得可能是版本更新把这些东西更新掉了,但也没有找到资料。有懂哥的话可以教教

位运算符

就和C中的位运算很像了。

& 按位与

| 按位或

~ 取反

<<,>> 左移,右移

另外有的pascal编译器还允许not and xor shl shr等作位运算符。

in

有点像python中的用法,直接放代码:

1
2
3
4
5
6
7
8
var c:char;
begin
readln(c);
if c in ['a'..'b']
then writeln(' c is letters')
else if c in ['0'..'9']
then writeln('c is digits');
end.

这里就用到了in运算符,if语句的语法可以看看下面的分支结构。

in运算符判断元素在不在集合里并返回一个布尔值,而这里集合的表示也是pascal比较特色的一点,也挺方便的。”..”表示整个ASCII码闭区间。另外想很多语言一样,if后的括号是可以省略的。

运算符优先级

操作符 优先级
~,not 最高
*,/,div,mod,and,&
\ ,!,+,-,or
=,<>,<,>,<=,>=,in
and or 最低

Pascal中and or运算优先级最低,所以在if语句判断的时候就要:

1
if ( (a<3) and (1<a) ) ...

而不是
1
if (a<3 and 1<a){错误用法}

循环结构,分支结构

分支/选择结构

if else

1
2
3
4
5
6
7
8
{基本语法}
if ({逻辑表达式})
then {语句};

{还有}
if ({逻辑表达式})
then {语句} {这里不能加 ';'}
else {语句};

以上只适合单行,多行需要复合语句。在pascal中符合语句就相当于C里的大括号。

1
2
3
4
if ({判断语句}) then
begin
{语句}
end;

case

1
2
3
4
5
6
7
{基本语法}
case({表达式}) of
{表达式1}:{语句1}
{表达式2}:{语句2}
...
else
{语句}

程序会计算case后表达式的值,然后看看对应第几个表达式的值,然后执行第几句语句(如果有多句记得用begin-end复合语句)。如果都没有匹配则执行else后的内容。

不像C中switch一样的是她不用在每段后面加上break或continue跳转。当然她也可以用嵌套if语句代替。

例子(懒得写了,网上复制的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var
grade: char;
begin
read(grade);
case (grade) of
'A' : writeln('Excellent!' );
'B', 'C': writeln('Well done' );
'D' : writeln('You passed' );
else
writeln('You really did not study right!' );
end;
writeln('Your grade is ', grade );
end.

循环结构

for

1
2
{基本语法}
for 初始变量:= 初始值 to/downto 结束值 do 语句

例子:

1
2
3
4
5
6
7
8
9
{求1加到100的值}
var
i,sum:longint;
begin
sum := 0;
for i:= 1 to 100 do
sum := sum + i;
writeln(sum);
end.

while

1
2
{基本语法}
while (逻辑表达式) do 语句

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
{还是1加到100}
var
i,sum:longint;
begin
sum := 0;
i := 1;
while (i <= 100) do
begin
sum := sum + i;
i := i + 1;
end;
writeln(sum);
end.

这个是用while循环写的 $\sum_{i=1}^{100} i$ 。注意因为do后面不止一个语句,所以用到了begin,end复合语句

repeat-until

相当于C里的 do..while.. 循环,不过until和while意思是反的,学过英语应该都懂。

其实就是出口条件循环,while是入口条件循环,repeat-until会执行一遍语句块再判断循环成不成立,所以语句块至少会被循环一次

1
2
3
4
{基本语法}
repeat
...
until (循环不成立条件);

例子
1
2
3
4
5
6
7
8
9
10
11
12
{还是算1加到100}
var i,sum:integer;
begin
i := 1;
sum := 0;
repeat
sum := sum + i;
i := i+1;
until (i = 101);
writeln(sum);
end.


这里有多句,但不用写begin,end。(我也不知道为什么)

但写了也没事。

函数

Pascal有函数function和过程procedure;
其实就是function有返回值,procedure,相当于C中返回void的函数。
为了行文方便我也把它叫做函数,有的地方叫作 “过程”。

Function

基本语法:

1
2
3
4
5
6
function {函数名字}({参数}:{类型}): {返回类型};
{定义区域}
begin
{语句}
{函数名字}:= {返回值};
end;

定义区域可以拿来定义局部变量等一些东西。当然也可以不写东西。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{写个求两者最小值的min吧}
function min(a,b:longint):longint;
var res:longint;{局部变量}
begin
if (a < b) then res:=a
else res:=b;
min:=res;{返回res}
end;

var a,b:longint;{全局变量}
begin
read(a,b);
writeln(min(a,b));
end.

注意,如果函数参数有多个类型,要用 ; 隔开,如
1
fun(ch:char ; n:longint):boolean;

Procedure

就是无返回值的function,语法也就是不写返回类型和返回值的function.

为了方便解释,我在下文里也把他称作”函数”.

基本语法:

1
2
3
4
5
procedure {名字}({参数}:{类型}):;
{定义区域}
begin
{语句}
end;

例子:
1
2
3
4
5
6
7
8
9
10
procedure main();
begin
write('114');
writeln(' 514');
writeln('1919810');
end;

begin
main();
end.

这里我写了一个main函数,pascal是没有main函数的,我们就可以这样写一个,就可以定义main里的局部变量了。这里没有参数。所以我们写不写括号都可以。

数组

普通一维数组的声明语法一般是先用type定义一下,然后再在var里定义一个数组对象,如下:

1
2
3
4
5
type
{定义一个下标从1到20的整型数组}
arr = array[1..20] of integer;
var
a:arr;

也可以直接定义:
1
a:array[1..20] of integer;

pascal的type部分在这里我感觉就和C中的typedef作用差不多,当然后面定理类的时候也要用到它。

pascal的下标是可以自定义的,可以定义下标从负数开始,也可以用字符和枚举作下标。

字符串

在pascal中我们一般有两种方式定义字符串,字符数组和string.

用string:

1
2
3
4
5
6
7
var
name:string;
begin
writeln('What''s your name?');
read(name);
writeln('Hello ',name);
end.

用字符数组:
1
2
3
4
5
6
7
8
program testString;
var
name:array[1..20] of char;
begin
writeln('What''s your name?');
read(name);
writeln('Hello ',name);
end.

两种方式表现区别不大,而且都可以用[]下标访问第几个元素,
不过有一个细节就是,string下标默认从1开始,第一个程序的name[0]没有东西。

而字符数组的第一个字符总是存在第一个下标,下标从1开始name[1]就是第1个字符;从-1开始,name[-1]就是第1个字符

二维数组

定义语法如下:

1
2
{定义一个400个元素的二维char数组}
a:array[1..20,1..20] of char;

使用的时候可以用 a[3,4]访问元素,也可以试用a[3][4]访问元素。

简单介绍一下吧。

在下面这个程序里我写了个sudent类,并定义了a,b两个对象。

并让他们执行一些操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
type
{类定义语法}
Student = object
{public表示公有成员,像其他很多语言一样}
public
{定义两个成员变量}
name:string;
score:longint;
{声明一个成员函数}
procedure Yes;
end;

{定义两个student类的变量}
var a,b:student;

{要在这个地方定义函数体}
procedure Student.Yes;
begin
{字符串中两个''表示一个单引号}
write('I''m ',name,',I got ',score,chr(10))
end;

{执行部分}
begin
{把a,b赋值}
a.name := 'yyy';
a.score := 1919810;
b.name := 'senpai';
b.score := 114514;
{a,b执行函数}
a.Yes;
b.Yes;
end.

输出:
1
2
I'm yyy,I got 1919810
I'm senpai,I got 114514

这就是类的基本使用,简单来说就是在type里声明一个类型,后面要加上 = object.然后定义类的成员,然后成员函数需要在类外定义。

结语

就将这么多吧,还有一点关于类的构造,继承以及指针等没有讲。不过那些不是开发大项目也不是很用得上,这么多应付一些简单程序,刷刷OJ还是够的吧。(2023年不会还有人用Pascal开发项目吧)

算法 + 数据结构 = 程序

这句大名鼎鼎的公式便是Pascal之父Niklaus Wirth提出的。他于1984年获得图灵奖。他还提出了一句话:

软件变慢的速度要快于硬件变快的速度

确实,Pascal诞生于1970年,比C语言的岁数还要大。在那个计算机普遍只有几十kb内存的年代,多出1k的内存占用都是无法接受的。,于是Pascal以及ALGOL系列等语言需要严格的内存分配机制,像什么变量必须定义在程序开头呀(C语言的早期也是这么要求的)等一些语言特性,使他甚至有比C更好的编译速度和空间优势。但后来C++,java因为高级特性而牺牲部分性能,再到如今火遍天下的python已经完全成为了动态语言,我们是在变慢。

不过这换来了互联网行业的兴盛,以及编程普及的“大好形势”,上古时期,冯诺伊曼等传奇元老曾认为,用汇编语言之上的编译器编译的高级语言,拖慢程序运行速度。是傻瓜才会去用的。那也是他们老人家没有看到现在这个时代。

现在虽然各大牙膏厂在争相练习刀法(bushi),但硬件的更新速度还是有目共睹的,新代i3能打旧版i7,电脑的内存也要比几年前手机硬盘还要大了,16G内存也已经是现在笔记本电脑的标配了。(库克你个小可爱)所以我们现在有资本去丢芝麻捡西瓜。

但是,正如Anders Hejlsberg(也是一位大牛,C#, Turbo Pascal, Delphi, Typescript之父)所言,最想从那个时代继承下来的东西,便是“资源限制”。现在的程序总是把很多不必要的东西往里面塞。

国内很多人接触到Pascal都是通过信息学奥赛。Pascal虽已过气,但他实实在在的影响了国内外许多人(包括Anders)。或许未来的某一天,该语言的学习会归入考古学的范畴(乐)。又或者,根本不会有人在乎。有人说,Pascal优美的像诗,但带啊吗终究不是诗,不会被千古传颂。我们程序员写的代码,或许都会在某一天被按下del键,消失在这个世界中。或是被遗弃,或是被更好的代码替代,这是无法避免的。

幸运的是,我们还能留下思想。或许这,才是程序最核心的东西

1
2
#define begin {
#define end }