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 を利用します。

  1. 次のサイトからパッケージをダウンロードします。
    "with GCC x.x.x support files" と表示されているものをダウンロードします。
    GNU Pascal Compiler for MinGW
    http://www.gnu-pascal.de/binary/mingw32/
  2. パッケージを C:\MinGW などのディレクトリに展開します。
  3. 環境変数の編集ダイアログを開き,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

論理演算子

演算子説明使い方
andANDa and b
orORa or b
notNOTnot 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 > aa は b の真部分集合[1,2,3] < [1,2,3] → false
a <= b, b >= aa は b の部分集合[1,2,3] <= [1,2,3] → true
e in ae は 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 手続きを呼び出します。

スポンサーリンク