缅怀Pascal
文艺复兴
CCF在去年正式把pascal在高中竞赛中踩在脚底,那我就偏要玩玩这个语言。
现在,在网上,关于Pascal的讨论已经可以说只剩断壁残垣了。(我甚至能找到比我还大的帖子(悲)) 不是很容易找到那种系统又准确的教程了。我就到处整理学习了一下,并把他分享出来。
我也是初学这门语言,如果有些点不准确感谢纠正:)
语言细节一篇文章大概是讲不完的,那我就讲讲简单的语法吧。
基本程序结构
1 | program {程序名字} |
begin前面的声明,如果用不上,都可以省略。
program这一句也行。
注意,Pascal不区分大小写(字符串除外)。
注释
Pascal 中注释使用大括号括起。1
2
3
4
5
6
7{单行注释}
{*
多
注
行
释
*}
输入输出语句
输出:
一般使用 write
输出1
2
3write('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
11write(6:7);
{输出:' 6 '}
write(6,0);
{输出:'6'}
write(114.514:0:1);
{输出:'114.5'}
write(114.594:0:1);
{输出:'114.6',四舍五入}
输入:
一般使用 read
输入1
2read({变量名列表});
read(a,b); {读入a,b 忽略空格和换行符}
还有readln()函数也可以读入。readln和read的区别是readln读入后切换到下一行,如:1
2
3输入数据:
1 2 3 4
5 6
用read读入四个值1
2read(a,b);
read(c,d);
之后a = 1,b = 2,c = 3,d = 4;
而用readln读入1
2readln(a,b);
read(c,d);
之后a = 1,b = 2,c = 5,d = 6;
注意:
- 变量类型与输入的数据类型必须一致
- 输入顺序与变量顺序要一致
变量及数据处理
要在程序var区域定义变量1
2
3
4
5
6
7program 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
8type
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
8var 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 | {基本语法} |
以上只适合单行,多行需要复合语句。在pascal中符合语句就相当于C里的大括号。
1 | if ({判断语句}) then |
case
1 | {基本语法} |
程序会计算case后表达式的值,然后看看对应第几个表达式的值,然后执行第几句语句(如果有多句记得用begin-end复合语句)。如果都没有匹配则执行else后的内容。
不像C中switch一样的是她不用在每段后面加上break或continue跳转。当然她也可以用嵌套if语句代替。
例子(懒得写了,网上复制的):1
2
3
4
5
6
7
8
9
10
11
12
13
14var
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 | {基本语法} |
例子: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 | {基本语法} |
例子: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
6function {函数名字}({参数}:{类型}): {返回类型};
{定义区域}
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
5procedure {名字}({参数}:{类型}):;
{定义区域}
begin
{语句}
end;
例子:1
2
3
4
5
6
7
8
9
10procedure main();
begin
write('114');
writeln(' 514');
writeln('1919810');
end;
begin
main();
end.
这里我写了一个main函数,pascal是没有main函数的,我们就可以这样写一个,就可以定义main里的局部变量了。这里没有参数。所以我们写不写括号都可以。
数组
普通一维数组的声明语法一般是先用type定义一下,然后再在var里定义一个数组对象,如下:1
2
3
4
5type
{定义一个下标从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
7var
name:string;
begin
writeln('What''s your name?');
read(name);
writeln('Hello ',name);
end.
用字符数组:1
2
3
4
5
6
7
8program 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
33type
{类定义语法}
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
2I'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 |