Pascal 入門
Pascal のエッセンスをコンパクトにまとめました。
Pascal 入門
Pascal について
Pascal は 1970 年に発表された手続き型言語です。
当初,教育用のプログラミング言語として設計されました。
Pascal には多くの方言がありますが,本書は ISO 7185 を参考にしました。
Pascal の各種標準規格は次のサイトから閲覧できます。
- Pascal Standards
- http://pascal-central.com/standards.html
GNU Pascal Compiler
GNU Pascal Compiler は,GNU が開発しているオープンソースの Pascal コンパイラです。
インストール
Windows
Windows では GNU Pascal Compiler for MinGW を利用します。
- 次のサイトからパッケージをダウンロードします。
"with GCC x.x.x support files" と表示されているものをダウンロードします。
- GNU Pascal Compiler for MinGW
- http://www.gnu-pascal.de/binary/mingw32/
- パッケージを C:\MinGW などのディレクトリに展開します。
- 環境変数の編集ダイアログを開き,C:\MinGW\bin にパスを通します。
使い方
GNU Pascal Compiler は gpc というコマンド名で利用できます。
コンパイル
ソースファイル hoge.pas を実行可能ファイル hoge (または hoge.exe) にコンパイルするには,次のコマンドを実行します。
% gpc -o hoge hoge.pas
なお,オプション -o hoge を指定しなければ,デフォルトで a.out (または a.exe) というファイルが出力されます。
ヘルプ
ヘルプを参照するには gpc --help を実行します。
% gpc --help
Hello World
次のプログラムは,Pascal の Hello World プログラムです。
このソースコードを hello.pas 等の名前のファイルに保存し,コンパイルします。
拡張子は .pas や .p が一般的です。
program hello(output);
begin
writeln('hello, world') { 文字列を出力 }
end.
- begin と end で囲まれた範囲は,ブロック (block) と呼ばれます。
- プログラムの終端にはピリオド . を置きます。
- フリーフォーマットであり,インデントの深さに文法的な意味はありません。
- 大文字と小文字は区別されません。
- 実行の最小単位は文 (statement) であり,セミコロン ; で区切ります。
- 文字列はシングルクォート '...' で囲って表します。
- { から } までと,(* から *) までは,コメントとして扱われます。
生成された実行可能ファイルを走らせると,次のように出力されます。
hello, world
プログラム見出し
Pascal のプログラムは,キーワード program で始まる次の見出しから始まります。
program プログラム名(ファイル名1, ファイル名2, ...);
ファイル名は,プログラムで使用するファイルの名前を指定します。
標準入力,標準出力もファイルとして扱われ,それぞれ input, output と書きます。
文の区切り
ブロック中の最後の文は,文末にセミコロン ; を置く必要はありません。
ただし,セミコロンを置いてもエラーにはなりません (セミコロンより後は空文とみなされます)。
手続き/関数
writeln(...) は,文字列を改行付きで出力する手続きの呼び出しです。
手続き (procedure) は,他の言語でいう関数やサブルーチンに相当するもののうち,返り値を持たないものを指します。
返り値を持つものは関数 (function) と呼び,手続きとは区別されます。
変数
次のプログラムは,整数型と実数型の変数を扱うプログラムの例です。
program sample(output);
var
n : integer;
p : real;
begin
n := 5;
p := n;
writeln(n);
writeln(p)
end.
5
5.00000000000000E+000
変数
変数は var から始まる次のブロックで宣言します。
var
変数名1, 変数名2, ... : 型名;
...
変数への値の代入は,変数 := 値 と書きます。
識別子
識別子 (変数名や関数名など) には,次のような名前を使います。
- 1 字目にはアルファベットを用います。
- 2 字目以降にはアルファベットまたは数字を用います。
キーワードには次のようなものがあります。
and array begin case const div do downto else end file for function goto if in label mod nil not of or packed procedure program record repeat set then to type until var while with
定数の宣言
定数は const から始まるブロックで定義します。
program sample(output);
const
pi = 3.14;
var
r, area : real;
begin
r := 2.0;
area := pi * r * r; { 12.56 }
end.
データ
基本型
代表的な基本型には次のようなものがあります (利用可能な型は処理系によって異なります)。
基本型 | 説明 |
---|---|
integer | 整数型 |
real | 実数型 |
char | 文字型 |
boolean | 論理型 |
リテラル
各データ型のリテラルの例は次の通りです。
種別 | リテラルの例 |
---|---|
整数 | 123 |
実数 | 3.14 1.23e-5 |
文字 | 'a' |
文字列 | 'abc' 'it''s' |
論理 | true false |
文字,文字列リテラルはともにシングルクォート '...' で囲って表します。
文字中でシングルクォート ' を表すには,2 個続けて '' と書きます。
単純型/構造型
データ型は大きく分けて,単純型,構造型,ポインタ型の 3 つに分けられます。
単純型 | 整数型,実数型,文字型,論理型,部分範囲型,列挙型 |
---|---|
構造型 | 配列型,レコード型,集合型,ファイル型 |
部分範囲型
部分範囲型 (subrange type) とは,取りうる値の範囲を限定した型です。
program sample(output);
var
n : 0..maxint;
d : '0'..'9';
begin
n := 0; { 可 }
n := -1; { 不可 }
d := '5'; { 可 }
d := 'a' { 不可 }
end.
部分範囲型は,0..maxint, '0'..'9' のように,下限..上限 の形で書きます。
例えば 1..5 と書いた場合,取りうる値は 1, 2, 3, 4, 5 のみです。
定数 maxint は整数の最大値を表す組込みの定数です。
型の定義
あらゆるデータ型は,次のように型名を定義することができます。
program sample(output);
type
natural = 0..maxint;
digit = '0'..'9';
var
n : natural;
d : digit;
begin
...
end.
列挙型
列挙型 (enumerated type) は,取りうる値を定数として列挙した型です。
program sample(output);
type
dayofweek = (mon, tue, wed, thu, fri, sat, sun);
var
d : dayofweek;
begin
d := tue
end.
文字列型
文字列型は string(長さ) と書かれます。文字列型の扱いは処理系依存です。
program sample(output);
var
s : string(256);
begin
s := 'hello';
writeln(s)
end.
配列
1 次元配列
1 次元配列は次のように書いて宣言します。
program sample(output);
var
arr : array [1..3] of char;
begin
arr[1] := 'a';
arr[2] := 'b';
arr[3] := 'c';
end.
配列の添字に用いるデータ型には,部分範囲型,列挙型などが指定できます。
例えば,列挙型を用いた次のような配列の宣言も可能です。
program sample(output);
type
dayofweek = (mon, tue, wed, thu, fri, sat, sun);
var
arr : array [dayofweek] of integer;
begin
arr[tue] := 256;
end.
多次元配列
多次元配列は次のように書いて宣言します。
program sample(output);
var
arr : array [1..2] of array [1..3] of char;
begin
arr[1][1] := 'a';
arr[1][2] := 'b';
arr[1][3] := 'c';
arr[2][1] := 'A';
arr[2][2] := 'B';
arr[2][3] := 'C';
end.
次のような,より簡略な書き方もあります。
program sample(output);
var
arr : array [1..2, 1..3] of char;
begin
arr[1, 1] := 'a';
...
end.
詰めあり型
構造型の型名に packed を付けたものは,詰めあり型 (pcked type) と呼ばれます。
詰めあり型は,詰めなしに比べてメモリ領域を節約して使用します。
type
charray = packed array [1..100] of char;
標準入出力
writeln 手続き
writeln 手続きは文字列出力を行う手続きです。
改行を行わない write 手続きもあります。
program sample(output);
var
a, b : integer;
begin
a := 2;
b := 3;
write('a + b = ', a + b); { 複数引数を指定可能 }
writeln { 改行のみを行う }
end.
a + b = 5
出力の書式を指定するには,次のように書きます。
program sample(output);
begin
writeln(3:8); { 値:幅 }
writeln(3.14159265358979:8:2); { 値:幅:精度 }
end.
3
3.14
readln 手続き
readln 手続きは文字列入力を行う手続きです。
次のプログラムは,readln 手続きを使って整数の入力を受け付ける例です。
program sample(input, output);
var
a, b : integer;
begin
readln(a, b);
writeln('a + b = ', a + b)
end.
<b>[入力]</b>
2 3
<b>[出力]</b>
a + b = 5
式と演算
算術演算子
演算子 | 説明 | 使い方 |
---|---|---|
+ | 単項プラス | +a |
- | 単項マイナス | - a |
+ | 加算 | a + b |
- | 減算 | a - b |
* | 乗算 | a * b |
/ | 除算 (実数) | a / b |
div | 除算 (整数) | a div b |
mod | 剰余 | a mod b |
演算子の優先順位は一般の四則演算と同じです。
優先順位を変えるには括弧 (...) を使えば OK です。
1 + 2 * 3 { 7 }
(1 + 2) * 3 { 9 }
演算子 / の評価結果は常に実数型です。
演算子 div の評価結果は整数型で,小数部分は切り捨てられます。
10 / 3 { 3.333... }
10 div 3 { 3 }
関係演算子
演算子 | 説明 | 使い方 |
---|---|---|
= | 等値 | a = b |
<> | 非等値 | a <> b |
> | 大なり | a > b |
< | 小なり | a < b |
>= | 以上 | a >= b |
<= | 以下 | a <= b |
論理演算子
演算子 | 説明 | 使い方 |
---|---|---|
and | AND | a and b |
or | OR | a or b |
not | NOT | not a |
制御フロー
if 文
if 条件式 then 文
if 条件式 then 文 else 文
else-if 節,else 節はそれぞれ省略できます。
次のプログラムは,整数 a, b の大小を判定する例です。
program sample(output);
var
a, b : integer;
begin
a := 2; b := 3;
if a = b then writeln('a = b')
else if a > b then writeln('a > b')
else writeln('a < b')
end.
a < b
複文
ブロック begin ... end で 1 つ以上の文を囲んだものは,複文またはブロック文と呼ばれ,文法的には文の一種です。
if 文や for 文などで複数の文を記述したいときは,複文を用います。
if a > b then
begin
tmp := a;
a := b;
b := tmp
end
case 文
case 選択式 of
ラベル1: 文1;
...
end
次のプログラムは,month の値によって処理を振り分ける例です。
program sample(output);
var
month, days : integer;
begin
month := 7;
case month of
1, 3, 5, 7, 8, 10, 12: days := 31;
2: days := 28;
4, 6, 9, 11: days := 30;
end;
writeln(days)
end.
31
for 文
for 変数 := 始値 to 終値 do 文
for 変数 := 始値 downto 終値 do 文
次のコードは,for 文を使って 1 から 5 までの数を数え上げる例です。
program sample(output);
var
i : integer;
begin
for i := 1 to 5 do writeln(i)
end.
1
2
3
4
5
a < b
to の代わりに downto を用いると,カウントダウンを行います。
for i := -1 downto -5 do writeln(i)
-1
-2
-3
-4
-5
while 文
while 条件式 do 文
次のコードは,while 文を使って 1 から 5 までの数を数え上げる例です。
i := 1;
while i <= 5 do begin
writeln(i);
i := i + 1
end
repeat 文
repeat 文 until 条件式
repeat 文中の文は repeat ... until で囲まれているので,begin ... end で囲まなくても複数の文を記述可能です。
i := 1;
repeat
writeln(i);
i := i + 1
until i > 5
goto 文
次のコードは,goto 文を使って 1 から 5 までの数を数え上げる例です。
program sample(output);
label
100;
var
i : integer;
begin
i := 1;
100:
writeln(i);
i := i + 1;
if i <= 5 then goto 100
end.
ラベルは 0 から 9999 までの整数で記述します。
プログラムで使用するラベルは,キーワード label から始まるブロックで宣言します。
label
ラベル1, ラベル2, ...;
手続きと関数
手続きは返り値を持たないもの,関数は返り値を持つものです。
手続き
次のプログラムは,文字 c を n 個出力する手続き nchars を定義したものです。
program sample(output);
{ 文字 c を n 個出力する手続き }
procedure nchars(c : char; n : integer);
var
i : integer;
begin
for i := 1 to n do write(c)
end;
begin
nchars('*', 7);
writeln
end.
*******
一般に,手続きの定義は次のように書きます。
procedure 手続き名(仮引数の宣言);
ローカルな定義や宣言
begin
処理
end;
関数
次のプログラムは,整数 m, n の最大公約数を求める関数 gcd を定義したものです。
program sample(output);
{ 整数 m, n の最大公約数を求める関数 }
function gcd(m, n : integer) : integer;
var
r : integer;
begin
while n <> 0 do
begin
r := m mod n;
m := n;
n := r
end;
gcd := m
end;
begin
writeln(gcd(84, 35)) { 出力: 7 }
end.
一般に,関数の定義は次のように書きます。
function 関数名(仮引数の宣言) : 返却型;
ローカルな定義や宣言
begin
処理
end;
関数の返り値は,関数名 := 返り値 と書いて指定します。
値引数/変数引数
仮引数宣言に var を付けたものは変数引数,付けないものは値引数と呼ばれます。
変数引数は変数の参照渡し,値引数は値渡しが行われます。
次のプログラムは,整数変数 m, n の値を交換する手続き swap を定義したものです。
procedure swap(var m, n : integer);
var
t : integer;
begin
t := m; m := n; n := t
end;
整合配列引数
配列型の仮引数は,大きさが可変な整合配列として宣言することができます。
整合配列の型は,array [low..high : integer] of char のように書かれます。
program sample(output);
var
arr : array [1..3] of char;
{ 文字配列 a の各要素を出力する手続き }
procedure printelem(
a : array [low..high : integer] of char); { 整合配列 }
var
i : integer;
begin
for i := low to high do writeln(a[i])
end;
begin
arr[1] := 'a';
arr[2] := 'b';
arr[3] := 'c';
printelem(arr)
end.
a
b
c
なお,整合配列以外では,一般に書き下した型定義を仮引数の宣言に書くことはできません。
type charray = array [1..10] of char;
procedure hoge(a : charray); { 可 }
procedure piyo(a : array [1..10] of char); { 不可 }
手続き引数/関数引数
手続きまたは関数を引数に取る手続きや関数を定義することもできます。
次のプログラムは,関数引数を持つ手続き sort を定義したものです。
program sample(output);
var
arr : array [1..5] of char;
{ x < y であれば true を返す }
function less(x, y : char) : boolean;
begin
less := x < y
end;
{ 文字変数 a, b の値を交換する }
procedure swap(var a, b : char);
var
t : char;
begin
t := a; a := b; b := t
end;
{ 関数 f に基づいてソートを行う }
procedure sort(
var a : array [low..high : integer] of char;
function f(x, y : char) : boolean); { 関数引数 }
var
i, j : integer;
begin
for i := high downto low + 1 do
for j := low to i - 1 do
if not f(a[j], a[j + 1]) then swap(a[j], a[j + 1])
end;
begin
arr[1] := 'd'; arr[2] := 'a'; arr[3] := 'e';
arr[4] := 'b'; arr[5] := 'c';
sort(arr, less);
writeln(arr) { 出力: abcde }
end.
集合
集合型は set of 基底型 と書き,基底型の要素を持つ集合を表します。
次のプログラムは,集合型を用いたプログラムの例です。
program sample(output);
var
a, b : set of 1..10;
begin
a := [ 1, 3, 5..8 ]; { a の要素は 1, 3, 5, 6, 7, 8 }
b := [ 1, 3, 6, 8 ]; { b の要素は 1, 3, 6, 8 }
writeln(a = b); { false }
writeln(a > b); { true }
writeln(5 in a); { true }
writeln(5 in b); { false }
end.
集合型のリテラルは,[ 2, 5, 5..8 ] のように角括弧で囲って書きます。
集合演算
集合型には次の演算子を用いることができます。
演算子 | 説明 | 例 |
---|---|---|
+ | 和集合 | [1,2,3] + [4] → [1,2,3,4] |
- | 差集合 | [1,2,3] - [2] → [1,3] |
* | 積集合 | [1,2,3] * [2,3,4] → [2,3] |
= | 等しい集合 | [1,2,3] = [1,2,3] → true |
<> | 異なる集合 | [1,2,3] <> [1,2,3] → false |
a < b, b > a | a は b の真部分集合 | [1,2,3] < [1,2,3] → false |
a <= b, b >= a | a は b の部分集合 | [1,2,3] <= [1,2,3] → true |
e in a | e は a の要素 | 1 in [1,2,3] → true |
レコード
レコード型は,1 つの変数で複数のデータを扱えるようにしたデータ型です。
次のプログラムは,人間を表す person レコード型を定義したものです。
program sample(output);
type
person = record
name : string(256);
age : integer
end;
var
p : person;
begin
p.name := 'Nagao';
p.age := 20;
writeln(p.name);
writeln(p.age)
end.
レコード型の定義は,record ... end の中にフィールドの宣言を書いたものです。
レコードの各フィールドにアクセスするには,p.name のように書きます。
with 文
with 文を用いると,レコード名を書かずにレコード要素にアクセスできます。
program sample(output);
type
person = record
name : string(256);
age : integer
end;
var
p : person;
begin
with p do { with 文中では p. を省略できる }
begin
name := 'Nagao';
age := 20;
writeln(name);
writeln(age)
end
end.
固定部/可変部
レコード型は,メンバが独立したメモリ領域を持つ固定部と,メンバがメモリ領域を共有する可変部を持つことができます。
固定部は C の構造体,可変部は C の共用体に相当します。
先の person レコード型は,固定部のみを持つレコード型です。
次に示すプログラムでは,可変部のみを持つレコード型を定義しています。
program sample(output);
type
shape = record
case flag : (circ, rect) of
circ : (radius : real);
rect : (width, height : real);
end;
var
s : shape;
begin
s.flag := circ;
s.radius := 2.0;
writeln(s.radius:0:2); { 2.00 }
writeln(s.width:0:2); { 2.00 }
writeln(s.height:0:2); { 意味のない値 }
end.
可変部は次の形式で書きます。
case タグフィールド : タグ型 of
定数 : (フィールドの宣言);
...
タグフィールドは,可変部のどのフィールド群が有効かを示すフラグとして用いられます。
ポインタ
ポインタ型は,他の変数への参照を保持するデータ型です。
次のプログラムは,整数変数を指すポインタ p, q を用いた例です。
program sample(output);
var
p, q : ^integer;
begin
new(p); { 動的に領域を確保 }
q := p; { p, q は同じ領域を指す }
p^ := 5;
writeln(p^); { 5 }
writeln(q^); { 5 }
dispose(p); { 領域を解放 }
end.
ポインタ型は ^被指示型 と書きます (書き下した型定義は不可)。
例えば,整数変数を指すポインタの型は ^integer と書きます。
ポインタの指す変数にアクセスするには,ポインタ変数^ と書きます。
Pascal のポインタは,手続き new によって動的に (実行時に) 生成された変数のみを指示できます。
ポインタがどの変数も指していない状態は,定数 nil で表されます。
ptr := nil
ファイル
テキストファイル
テキストファイルを扱うには,定義済のファイル型 text を用います。
次のプログラムは,テキストファイル hoge.txt の内容をコンソールに出力する例です。
program sample(f, output);
var
f : text;
buf : string(256);
begin
assign(f, 'hoge.txt');
reset(f); { ファイルを読み込み用に開く }
while not eof(f) do
begin
readln(f, buf);
writeln(buf)
end;
close(f)
end.
ファイルを読み込み用に開くには,assign 手続きでファイル名を指定した後,reset 手続きでファイルを開きます。
ファイルを閉じるには close 手続きを呼び出します。
手続き readln は,第 1 引数にファイルを指定すると,そのファイルからの読み込みを行います。
次のプログラムは,テキストファイル hoge.txt にサンプルテキストを書き込むプログラムです。
program sample(f);
var
f : text;
begin
assign(f, 'hoge.txt');
rewrite(f); { ファイルを書き込み用に開く }
writeln(f, 'This is some sample text.');
close(f)
end.
ファイルを書き込み用に開くには,reset 手続きの代わりに rewrite 手続きを呼び出します。
writeln 手続きも readln と同様,第 1 引数にファイルを指定すると,そのファイルへの書き込みを行います。
一般のファイル型
一般のファイル型は file of 成分型 と書きます (書き下した型定義は不可)。
例えば,実数型のファイルを扱うには file of real と書きます。
次のプログラムは,実数型のファイルに実数 1/1, 1/2, ..., 1/5 を書き込むする例です。
program sample(f);
var
f : file of real;
i : integer;
begin
assign(f, 'hoge.dat');
rewrite(f);
for i := 1 to 5 do
begin
f^ := 1 / i;
put(f);
end;
close(f)
end.
ファイルのバッファ (バッファ変数) にアクセスするには f^ と書きます。
書き込み中のファイルのバッファ変数を更新 (書き込み位置を移動) するには put 手続きを呼び出します。
次のプログラムは,実数型のファイル hoge.dat の内容を読み込んで,コンソールに出力するものです。
program sample(f, output);
var
f : file of real;
n : real;
begin
assign(f, 'hoge.dat');
reset(f);
while not eof(f) do
begin
n := f^;
writeln(n:0:2);
get(f);
end;
close(f)
end.
1.00
0.50
0.33
0.25
0.20
読み込み中のファイルのバッファ変数を更新 (読み込み位置を移動) するには,put 手続きの代わりに get 手続きを呼び出します。