• 検索結果がありません。

note.dvi

N/A
N/A
Protected

Academic year: 2021

シェア "note.dvi"

Copied!
201
0
0

読み込み中.... (全文を見る)

全文

(1)
(2)
(3)

5

PASCAL

入門

5.1

ここでの目的

ここでは Pascal の文法を中心に解説をするが, 単に Pascal を用いてプログラムが書けるようになるこ とが目的なのではなく, Pascal の言語構造を理解し, アルゴリズムを正確に記述できるようにすることが重 要である. またプログラムは, 自分自身だけが読むものではなく他人にも読めるように, わかりやすく簡潔に書くべ きである. これらのことを念頭において学ぶことを期待したい.

5.2 Pascal

とは

プログラム言語 Pascal は1968年に N. Wirth によって開発された言語であり, 2つの主要な目標 にそって設計されている. 一つはプログラミングの教育に適した言語にすること. もう一つは, 信頼性が高 く, 効率のよいものを実現することである. 教育用に設計された言語という意味は, プログラムの機能や構 成要素を論理的に説明するための構成を持っていることであり, 可搬性や書きやすさという要素を削ってで も, プログラムの機能の理解が容易になるような言語構造を持っている.

ここでは, Sun Microsystems の SPRACompiler と呼ばれる処理系での拡張を加えながら, 1983年 に承認された ISO による標準 Pascal を元に, 解説を進める. この章の内容の元になったものは, Jensen と Wirthによる [1] と ISO の規格書に解説を加えた [2] である.

5.3 Pascal

をはじめる前に

Pascal によるプログラミングをはじめる前に, 後の混乱を避けるための注意をする. 以下の注意書きは, 無用なトラブルを回避するためできる限り守った方がよい. • 各ファイルには, その中身がわかるような簡潔なファイル名をつけること. • 各プログラム毎にサブディレクトリを作成することが望ましい. • test.p など, 既存のプログラム名に .p を付加したファイル名は避けること. • 不要になったファイルは削除すること. また, プログラムソースを見やすくするために, 「字下げ」をきちんとすること.

5.4 Pascal

のプログラムの書き方・実行の方法

実行コードを作成するには以下の手順で行なう. 1. エディタ (Mule など) を利用して, プログラムソースを書く. 2. コンパイラを起動して, 実行コードを作成する. 3. 作成した実行コードを実行する.

(4)

例えば, Mule を利用して, hello.p というプログラムソースを作成した時, これをコンパイルするには, 以 下のようにする. % pc hello.p -o hello 最後の -o hello という部分は, 実行コードを hello という名前で作成することを指示している. もし, -o helloという部分を省くと, コンパイラは a.out という名前で実行コードを作成する. ここで作成した hello を実行するには % ./hello とする. ここで, ./ をわざわざ指定していることに注意せよ1. Exercise 5.4.1 test.pというプログラムを % pc test.p -o test として, 作成して, % test とすると, どのようなことが起こるか. それは何故か?

5.4.1 Pascal の処理系について

講義で利用する Pascal の処理系は SPARCompiler と呼ばれる SUN MicroSystems が作成したもので ある. この処理系は, ANSI の規格以上の機能を持っているので, ここで作成したプログラムが他の処理系 の上で実行できないこともあり得る. この講義では, Pascal の標準仕様内のものと, SPARCompiler 独自のものを区別して利用する. 本来, 標 準仕様の内部で利用することが望ましいが, 種々の理由により, 独自拡張の部分も利用する.

5.5 Pascal

の基本的な注意

5.5.1 Pascal Program の基本的な構造

Pascal のプログラムは, コメント(注釈), 頭書き, ブロックから構成されている. 頭書きは, プログラ ムに名前を与え, プログラムのパラメタを並べて書く. ブロックは, 次の6つのセクションにわかれ, 最後 のセクション以外は空でも良いが, セクションは必ず次の順序で書かなければならない. 1. 名札宣言 2. 定数定義 3. 型定義 4. 変数定義 5. 手続きと関数の宣言 6. 文 それぞれの文は ; で終るか, begin と end で囲まれた文の集まりである複合文で構成されている. ここ で, C との違いは, ; は文の分離記号であり, 文の終端記号ではない. 1 UNIXではカレントディレクトリはデフォールトではコマンドサーチパスには入っていないし, 入れない方が望ましい.

(5)

5.5.2 Bachus-Naur formula

Pascalの場合, その構文は Bachus-Naur formula (BNF) と呼ばれるメタ言語によって記述できる. BNF においては, ::= | { } は BNF による形式化のメタ記号に属し, Pascal の記号ではない. {} はその記号の中の0回以上の繰り返 しを表す. 上に述べたプログラムの構造を BNF を利用して記述してみると  プログラム  ::=  プログラムの頭書き   ブロック  .  プログラムの頭書き  ::= program  名前  (  ファイル名  { ,  ファイル名  } ) ;  ファイル名  ::=  名前   名前  ::=  英字  {  英数字  }  英数字  ::=  英字  |  数字   ブロック  ::=  名札宣言部   定数定義部   型宣言部   変数宣言部   手続きと関数の宣言部   文の部  のようになる.

5.5.3 Pascal で利用できる文字

Pascal では, 英文字, 数字, 空白文字(スペース, タブなど), 記号文字, 改行文字などが利用できる. 記 号文字には特別な意味があることが多いので, 注意すること. また 標準の Pascal では, 大文字と小文字は 区別されない2. また, Pascal のコンパイラにおいて, 日本語が利用できるといっても, 変数名などに日本 語が利用できるわけではないので注意すること. また, 以下のものは全て空白と見なして無視される. • 空白文字, 改行文字, タブ, 改ページ記号, コメント.

5.5.4 コメント

コメントとは, プログラミングの補助となるようソースプログラム中に書かれた注釈部分のこと. コンパ イラは単にこれを無視するので, プログラムには影響を与えない. コメントは, { ではじまり, } で終る. ま た, { } が利用できないシステムの場合には, (* と *) の対を利用することができる. コメントは入れ子に はできない3 . 即ち, コメントの中にコメントを入れることはできない.

5.5.5 名前

名前とは, 定数, 型, 変数, 手続き, 関数を表すための名称である. 標準的な Pascal においては, 名前の長 さには制限はない. また, 名前はどの予約語とも異なった綴りでなくてはならない. 2 今回利用する SPARCompiler では, default では大文字と小文字を区別しているので注意すること. しかしながら, 標準の範囲 内で全てを小文字で記述するという考え方をしたほうがよい. 3 SPARCompilerにおいては, /* , */ の対, " で囲まれた部分もコメントとみなされる. その際, 異なった記号によるコメントは 入れ子にできる.

(6)

5.5.5.1 特殊記号 特殊記号とは, 英数字以外のうち, Pascal の文法上特殊な意味を持つものである. 標準 Pascal の特殊記 号は以下のものである.  特殊記号  ::= +| - | * | / | = | < | > | [ | ] | . | , | := | ; | ; | ( | ) | <> | <= | >= | .. |^|  予約語  また SPARCompiler においては, 次の特殊記号が定義されている. ~ & | ! # % したがって, SPRACompiler での特殊記号の定義は次のようになる.  特殊記号  ::= +| - | * | / | = | < | > | [ | ] | . | , | := | ; | ; | ( | ) | <> | <= | >= | .. |^|~|&| | | ! |#|%|  予約語  5.5.5.2 予約語 以下の名前は予約語と呼ばれ, 特別な意味を持ち, 他の名前として利用できない. and array begin case const div do downto else

file for forward function goto if in label main mod nil not of or packed procedure program record repeat set then to

type until var while with

また, SPARCompiler においては, 次の名前も予約語となっている. define extern external module otherwise

private public static univ

Pascal においては, 名前の一部に $, _ を使うことできるが, これらの文字を名前の先頭におくことは,

systemが既に利用している名前と衝突することが多いので, できる限り避けることが望ましい.

以下の名前は標準 Pascal において既に使われている名前である.

abs arctan boolean char chr cos dispose eof eoln exp false get input integer ln maxint new odd ord output page pred put read readln real reset rewrite round

sin sqr sqrt succ text true trunc write writeln また, SPARCompiler においては, 次の名前が既に使われている

FALSE TRUE addr alfa argc argv arshft asl asr

assert bell card clock close date discard double exit expo firstof flush getenv getfile halt in_range index integer16 integer32 intset land lastof length linelimit lnot longreal lor lshft lsl lsr max maxchar message min minchar minint next null open pack random remove return rshft seed shortreal single sizeof stlimit string substr sysclock tab time trim univ_ptr unpack varying wallclock xor

5.5.5.3

Pascal における数は整数と実数4 のデータ型からなる. それぞれは, 次のように定義される.

4 正確には, 「整数と呼ばれるデータ型と実数と呼ばれるデータ型」.

(7)

 数字列  ::=  数字  {  数字  }  符号なし整数  ::=  数字列   符号なし実数  ::=  符号なし整数  .  数字列  |  符号なし整数  .  数字列  E  桁移動子   符号なし整数  E  桁移動子   符号なし数  ::=  符号なし整数  |  符号なし実数   桁移動子  ::=  符号なし整数  |  符号   符号なし整数   符号  ::= +| -これらの例としては, 1 100 0.1 5E-3 87.35E+8 などがあり, 87.35E+8 とは, 87.35 を 10(+8) 乗したもののことである. また, SPARCompiler においては, 2 から 36 進数の表現をすることができる. それは, 次のように表現 される  拡張された数字列  ::=  英数字  {  英数字  } BASE ::=  英数字   符号なし BASE 進数  ::= BASE #  拡張された数字列   符号なし数  ::=  符号なし整数  |  符号なし BASE 進数  |  符号なし実数  ここで, 11 から 36 を表すには A から Z を用いる. また符号なし整数は [0, maxint] の範囲内になければならない. 5.5.5.4 文字列 文字の列を ’ で囲んだものを文字列と呼ぶ. 2個以上の文字列要素を含んだ文字列は, 同じ個数の成分 を持った文字列型の値を表したものである.  文字列  ::= ’  文字   文字  ’ また, ’ 自身を文字列に入れるには ’’ とする. 5.5.5.5 定数の定義 定数を定義することは, 定数と同義な名前を定義することである.  定数名  ::=  名前   定数  ::=  符号なし数  |  符号   符号なし数  |  定数名  |  符号   定数名  |  文字列   定数定義  ::=  名前  =  定数 

5.5.6 式

Pascal における式とは, 計算規則を表す構成要素である. 式は, その変数が使用時に不定である場合を除 いて, ある値を表す. それを式の値と呼ぶ. 式は演算子と被演算子(変数, 定数, 関数)からなる. また, 演 算子には優先順位が付けられている.

(8)

Example 5.5.1 次のようなものは式である. x 15 sin(x+y) x*y x+y -x x = 1.5 式の構文, 演算子, 優先順位とは後ほど述べることにする.

5.5.7 文

文とは, Pascal プログラムの基本的な単位になるもので, アルゴリズム上の動作単位を表す. また, 記号 ; は文の区切りを表している. Example 5.5.2 次は文である. x := y + z また, 何も書かれていない文(空文)も存在する. また, begin と end によって文の集まりを一つの文(複合文)にすることができる. Example 5.5.3 次は一つの複合文である. begin a := b ; c := d ; end

5.6

最も簡単なプログラム

はじめに最も簡単と思われるプログラムを書いてみよう. プログラムを実行すると画面に何かを表示するものである. Example 5.6.1 もっとも簡単なプログラムの例 { Program 1 Hello World. を出力する. }

program Hello_World (output) ; begin

writeln(’Hello World.’) end.

以下では, このプログラムの内容を説明する. (ただし, コメント, 空白行は行数として数えない.)

1行目

program Hello_World (output) ;

(9)

この行がプログラムの頭書きである. プログラムの名前として, Hello_World とした. また, このプログラ ム中で利用されるパラメタ(関数)として, この場合出力しか行なわないので, output とだけ指定してあ る. もし, 入力も行なう時には, input も指定しなくてはならない.

2行目と4行目

begin end. で囲まれた部分が「文」となっている. Pascal においては, 「文の部」の最後の end の後には . をつける ことに注意しよう. これは, . で文が全て終了することを表している.

3行目

writeln(’Hello World.’) ここで利用された writeln という手続きは, その引数として, 文字列をとり, その文字列を標準出力に出力 する5 . その際, 出力した文字列の最後に改行文字を出力する. ここで, この文には ; をつける必要がない ことに注意. begin と end は文をまとめて複合文にするのであるから, わざわざ文の区切り記号である ; をつける必要はない. もし, つけたとすると, その後に空文があると理解できる. Cで記述されたプログラムなどは, システムに対して戻り値を与えることができる. しかしながら, Pascal の言語仕様にはそのようなものは存在しない. Exercise 5.6.1 Program 1を真似て, 次のような出力を得るプログラムを書け. 各自の学籍番号 (改行) 各自の名前 (改行) 何でも好きなこと (改行)

5.7

プログラムの宣言部

Section 5.5.1で, Pascal のプログラムは「頭書き」と「ブロック」からなることを述べた. ここでは, 「ブ ロック」の宣言部がどのようになっているかを見てみよう. BNF による「プログラム」の定義は, 以下の ようになっていた.  プログラム  ::=  プログラムの頭書き   ブロック  .  プログラムの頭書き  ::= program  名前  (  プログラム引数  { ,  プログラム引数  } ) ;  プログラム引数  ::=  名前   名前  ::=  英字  {  英数字  }  英数字  ::=  英字  |  数字   ブロック  ::=  名札宣言部   定数定義部   型宣言部   変数宣言部   手続きと関数の宣言部   文の部  以下では, 頭書き, 名札宣言部, 定数定義部, 型宣言部, 変数宣言部の文法を述べておく. 5 ここの解説は本当は正しくない. この手続きは「テキスト」ファイルという型の変数に対して動作が定義されているもので, よ り一般的な記述法がある.

(10)

5.7.1 頭書き

頭書きにおける「プログラム名」はプログラム中においては何の意味も持たない. 「プログラム引数」に は, Pascal 内部で定義されたもの以外のもの(例えば関数, ファイル名)とプログラムとの結び付きを要求 するものの名称を書いておく. それぞれが, どんな型もののであるかは, 標準名称 input と output を除い て, プログラムブロックの変数宣言部に記述しておかなくてはならない.

手続き readln, writeln 等を標準入力・標準出力に利用する時には, それぞれ input, output を記述し ておかなくてはならない.

5.7.2 名札宣言部

プログラム中には後から参照ができるように, 名札を書くことができる. プログラム中で利用する名札

は, 使用する前に名札宣言部において宣言6 をしておかなくてはならない.

しかしながら, Pascal においては名札は goto 文の目印として利用されるだけであり, Pascal の構造上, ほとんど goto 文は利用しないので, 事実上名札は利用しない. 名札宣言部は以下のような構文である.  名札定義部  ::=  空  | label  名札  { ,  名札  } ;  名札  ::=  数字列  と定義され, 数字列の値は [0, 9999] の範囲内になければならない. さらに, 名札の数字列は, 数値として 同じものであれば, 同じ名札とみなす. すなわち, 7 と 007 は同じ名札を表している.

5.7.3 定数定義部

定数定義部の構文は以下の通りである.  定数定義部  ::=  空  | const  名前  =  定数  ; {  名前  =  定数  ; } ここで定義した名前(定数名)はその定数と同義語になる. また, 定数とは次のように定義されている.  定数  ::=  符号なし数  |  符号   符号なし数  |  定数名  |  符号   定数名  |  文字列   定数名  ::=  名前  すなわち, 定数としてとり得るものは, (符号がついても良い)数, すでに定義されている定数名, もしく は, 文字列であることがわかる. Example 5.7.1 次は正しい定数定義である. const a = 32 ; b = 1.0 ; c = -2.0 ; d = a ; e = -b ; f = ’---’ ;

5.7.4 型定義部

Pascalのデータ型は, 変数の宣言において直接記述しても良いし, 型名で参照しても良い. Pascal ではす でに定義されている型から新しい型をつくり出す手続きが定義されている. これが型定義である. 6 一般に宣言とは, プログラム中で実際に実行される文ではなく, それ以降に利用する識別子等を定義することをいう.

(11)

 型定義部  ::= type  名前  =  型  ; {  名前  =  型  ; }  型  ::=  単純型  |  構造つきの型  |  ポインタ型  この右辺に現れたものは後ほど定義する. また, Example についても, 後ほど説明する.

5.7.5 変数宣言部

Pascal のプログラム中に現れる全ての変数は必ず変数宣言しなくてはならない. また, 変数はプログラ ム中で現れる前に, 変数を利用する前に宣言しなくてはならない.  変数宣言部  ::= var  名前  { ,  名前  } :  型  ; {  名前  { ,  名前  } :  型  ; } Example 5.7.2 以下は正しい変数の宣言である. var a, b : integer ; c, d : real ; e : Boolean ; f : char ; これは, 文法的には, 名前と型を結び付けている(結合)と考えられる. この結合は, その名前を従属する ブロックで再定義しない限り, その宣言を含むブロック全体で有効である. また, 同一のレベルで, 同一の有効範囲内においては, 単一の名前は一度しか宣言できない.

5.8

変数とは

変数とは, 識別子によって区別された初期化, 代入などが許される記憶領域のことである. Pascal の変数 は, 型定義部の定義に述べた通り, 「単純型」, 「構造つきの型」, 「ポインタ型」に分類される. また, 変 数定義部の説明で述べた通り, 変数はプログラム中で現れる前に, 変数を利用する前に宣言しなくてはなら ないし, その名前を従属するブロックで再定義しない限り, その宣言を含むブロック全体で有効である. す なわち, 変数は宣言しなくては利用できなくて, その名前は, より小さなブロックで再定義しない限り有効 である. これは, 変数の「記憶クラス」, 「スコープ」, 「寿命」と呼ばれる考え方であり, これに関しては 後ほど詳しく述べる. ここでは, 「単純型」のみを扱い, その他の型については後ほど述べることにする.

5.8.1 変数の初期値

Pascal では全ての変数は, 宣言されただけでは値は定まらない. しかも, 標準的な Pascal においては, 変 数の宣言と同時に明示的な初期化は行なわれない. したがって, 明示的な初期化の方法としては,

var a,b : integer ; begin a := 1 ; b := 1 end ; といった方法をとらなくてはならない. しかしながら, SPARCompiler においては, 変数の宣言と同時に明示的な初期化を行なうことができる. Id: P3.tex,v 1.5 2001-03-11 17:24:47+09 naito Exp

(12)

var a,b : integer := 1 ; このように, 宣言と同時に初期化することもできる. しかしながら, 下の例の場合, 変数 a, b の初期化は 実行時にただ一度だけ行なわれるが, 上の例の場合は, 文を実行されるたびに値が代入されることに注意.

5.8.2 単純型

Pascal において, 単純型は次のように定義されている.  単純型  ::=  順序型  |  実数型名   順序型  ::=  書下し順序型  |  順序型名   書下し順序型  ::=  列挙型  |  部分範囲型   順序型名  ::=  型名   実数型名  ::=  型名   列挙型  ::= (  名前  { ,  名前  } )  部分範囲型  ::=  定数  ..  定数  5.8.2.1 列挙型 列挙型は名前を列挙することによって, 値の順序集合を定義する. 値はそれらの名前が表し, その名称を 列挙した時の並べ方によって, その値の順序が決まる. Example 5.8.1 次の例は正しい列挙型の定義である.

type color = (white, red, blue, yellow, green, black) ; day = (mon, tue, wed, thr, fri, sat, sun) ;

Example 5.8.2 次の例は間違った列挙型の定義である. type workday = (mon, tue, wed, thr, fri, sat) ;

free = (sat, sun) ;

ここでは, sat の型が曖昧なので, 間違っている. すなわち, sat は workday と free の両方で定義され ている. 列挙型の値の順序数は, 名前の順序にしたがって 0 から始まる連続した非負整数値への写像によって決ま る. したがって, 次に述べる整数型の範囲を越える数の要素を持った列挙型は定義することができない. また, 順序型に対して, 次の関数を適用することができる. succ 列挙中の後続値. pred 列挙中の先行値.

Example 5.8.1での例に従えば, succ(white) = red であり, pred(blue) = red である.

SPARCompilerにおいては, firstof など, 順序型に適用可能な関数もいくつか用意されている.

5.8.2.2 標準の型

標準の Pascal においては, 標準の単純型として, 次のものが定義されている.

(13)

整数 値は処理系が定義した整数の部分集合. その値は整数. 実数 値は処理系が定義した実数の部分集合. その値は実数で表す. 論理数 値は名前 true と false によって表示される真理値である. 文字 値は処理系が定義した文字集合. それらは文字自身を引用符で囲んで表す. 5.8.2.3 部分範囲型 部分範囲型は, すでに定義されている順序型の部分集合として, その最大値と最小値を指定することに よって定義される. 両方の定数の値は同一の順序型でなくてはならない. その順序型のことを, 部分範囲型 の親の型という. 最小値は最大値よりも小さいか等しくなくてはならない. Example 5.8.3 次は正しい部分範囲型の変数の定義である. var a : 1..10 ; b : -20..30 ; これらはいずれも整数の部分範囲型の変数として定義されている. Example 5.8.4 次は正しい部分範囲型の定義である.

type days = (mon,tue,wed,thr,fri,sat,sun) ; workd = mon..fri ; letter = ’a’..’z’ ; はじめの定義は列挙型の定義であるが, 2番めのものは型 days の部分範囲として定義されている. 部分範囲型として定義された変数に代入する際には, それを越えた範囲の代入を行なうと実行不可能にな る可能性がある. 5.8.2.4 整数 整数を表す型は integer であり, それが表す整数の範囲, そのメモリ内での表現方法は処理系ごとに定 義されている. 整数型の値の順序数は, その値そのものである. これを整数型と呼ぶ. 5.8.2.5 実数 実数を表す型は real であり, それが表す実数の部分集合, そのメモリ内での表現方法は処理系ごとに定 義されている. これを実数型と呼ぶ. 5.8.2.6 論理数 論理数を表す型は Boolean であり, これは標準的に type Boolean = (false, true) ;

で定義されている. すなわち, 標準名 false と true が定義され, さらに, false < true となる順序が入っ ている. これを論理型 と呼ぶ.

(14)

5.8.2.7 文字 文字を表す型は char であり, それが表す文字集合, そのメモリ内での表現は処理系に依存する. これを 文字型と呼ぶ. 文字型が表現する文字集合に対しては, 少なくとも, 以下のものを含む. • 大文字の英字. • 10進の数字. • 空白文字. それ以外にも, 特殊記号等を含んでいるのが普通である7. この時, 文字に対する順序数は, 0 から始まる連 続した非負整数値に写像される. この写像は次の性質を満たす. • 数字の 0 から 9 を表現する文字の値は, 数の順に順序付けられ連続. • 大文字の A から Z を表現する文字の値は, (使用可能な時には)アルファベット順であるが, 連続と は限らない. • 小文字の a から z を表現する文字の値は, (使用可能な時には)アルファベット順であるが, 連続と は限らない. 文字型に対しては, 2つの標準関数がある. これらはいずれも文字集合から自然数の部分集合の間の単射 な写像である. ord 処理系に依存した文字の順序集合において, 文字の順序数を表す. chr chr(i)は順序数 i をもつ文字を表す. この2つの関数は互いに逆写像の関係にある. しかも, 文字型は順序型であるので, 標準関数 pred, succ を利用することができるが, pred(c) = chr(ord(c)-1) succ(c) = chr(ord(c)+1) が成り立つ. 5.8.2.8 型の適合性 次の4つのいずれかが成り立つ時, 型 T 1 と T 2 は適合するという. (a) T 1 と T 2 は同じ型である. (b) T 1 が T 2 の部分範囲型であるか, T 2 が T 1 の部分範囲型であるか, T 1 と T 2 の両方が同じ親の型の 部分範囲型. (c) T 1 と T 2 が基底型の適合する集合型であって, 両方とも詰めありか, 両方とも詰めなしである. (d) T 1 と T 2 が同数の成分を持つ文字列型である.

5.8.3 変数と記憶領域

変数は宣言と同時に対応する記憶領域が確保される. しかしながら, それぞれの型に対して, どれだけの 記憶領域が確保されるかは処理系に依存している. 7 通常, よほどのことがない限り, ASCII 文字集合と等価であると思って間違いないだろう.

(15)

5.8.3.1 文字型

文字型を表す記憶領域は, 通常その処理系の扱う文字集合を表すのに必要十分な量が利用される.

SPAR-Compilerの場合, ASCII 文字集合を利用しているので, 文字型の変数の記憶領域は1バイト=8ビットで

ある.

SPARCompilerにおいて, minchar, maxchar, bell, tab と書かれる文字定数が定義されている. それら の順序数は10進数で minchar 0, maxchar 255, bell 7, tab 9 である. 5.8.3.2 整数型 整数型を表す記憶領域は, 文字型の表すそれの整数倍であることが普通である. 特に, 処理系の設計上, 1 バイト, 2バイト, 4バイトなどの 2n バイトを利用することが多い. SPARCompilerの場合, 整数型の変数の記憶領域は4バイト=32バイトである. この値は, コンパイル 時において -xl オプションを利用すると2バイト=16バイトとなる. このようなコンパイル時における 依存性を避けるため, SPARCompiler においては, integer16, integer32 と書かれるそれぞれ16ビット, 32ビット長に固定された整数型が定義されている.

また, maxint, minint と書かれる整数定数が定義され( maxint は標準 Pascal の範囲で定義されてい る.)それぞれ, integer 型の表す最大の整数, 最小の整数を表現する. また, Pascal においては, C で利用される「符号なし整数」の型は定義されていないが, 部分範囲型を利 用することにより, 「符号なし整数」型を定義することも可能である. 5.8.3.2.1 整数の内部表現 整数型の変数の内部での表現は, Section 2.3.2 で述べた表現がとられている ことが多い. 5.8.3.3 実数型 実数型を表す記憶領域は, 文字型の表すそれの整数倍であることが普通である. 特に, 処理系の設計上, 4 バイト, 8バイトなどの 2n バイトを利用することが多い. SPARCompilerの場合, 実数型 real の変数の記憶領域は8バイト=64ビットである. この値は, コ ンパイル時において -xl オプションを利用すると4バイト=32ビットとなる. このようなコンパイル時 における依存性を避けるため, SPARCompiler においては, single, shortreal, double, longreal と書 かれるそれぞれ32ビット, 32ビット, 64ビット, 64ビット長に固定された実数型が定義されている. これらの数を表す表現方法は, 実際には浮動小数点数として表されている. (10を底とする)浮動小数 点数とは, 以下のような型で表現された数のことである. (0でない1桁の数).<仮数部> × 10^<指数部> ここで, 任意の 0 でない実数は, このような表示が可能であることに注意せよ. (もちろん, その表示は有 限小数と仮定すれば一意的である.)

(16)

5.8.3.3.1 実数型の内部表現 実数型の変数の内部での表現は, Section 2.3.2 で述べた表現がとられてい ることが多い. オーバーフロー, アンダーフローをした数の演算, 比較等の結果がどうなるかは規格では定められていな い. しかし, それぞれの処理系によって規定されている. 5.8.3.4 列挙型 列挙型の変数の記憶領域は, 整数のそれに準じている.

5.9

いくつかの用語

5.9.1 誤り

プログラムの規則に対する違反には次の2種類がある. • 宣言のない変数の使用等, コンパイル時に検出できる違反. • 配列の添字の範囲の逸脱等, 実行時でないと検出できない違反. ここでは, 「誤り」という言葉は, 後者の意味で用いる.

5.9.2 不定

変数や駆動結果に値が与えられていないものを「不定」と呼ぶ. 一般に「不定」といった時には, 変数が 値を持っているが, それがどんな値かわからないという意味で利用するが, Pascal における「不定」とは もっと強い意味で, 不定の変数に対して比較 x = x を行なうと誤りとなることを意味する.

5.9.3 処理系依存と処理系定義

Pascal における処理系定義, 処理系依存とは, 言語仕様の中では決めることができず, 各処理系に対して 自由度を与えなくてはならない部分を示している. 処理系定義とは, 各処理系がその処理系の中では定義が与えられているものを指す. 一方, 処理系依存と は, 一つの処理系の中でも, その意味を決められないものを表す. 処理系定義の例には次のようなものがある. • 文字列用の文字の値. • 実数型の値. • 文字値の順序数. この他にも多くの可能性がある. 一方, 処理系依存の例は, • 演算子のオペランドの評価順序. • 関数の実引数の評価順序. • 代入文の左辺と右辺の評価順序. などがある.

(17)

5.10

標準関数と演算子

5.10.1 標準関数

ここまでに述べたもの以外にも, Pascal で標準的に定義されている関数がある. それらについては, Jensen-Wirth の付録参照のこと. また, SPARCompiler 独自の関数も数多く定義されている. これらに関しては, SPARCompilerのマニュアルを参照されたい.

5.10.2 演算子

標準 Pascal における演算子は, その被演算子の型, 結果の型に応じて, 代入演算子, 算術演算子, 関係演 算子, 論理演算子, 集合演算子に分類される. 全ての二項演算において, 被演算子の評価順序は, 処理系依存である. 5.10.2.1 代入文 代入の演算子 := を利用した文を代入文と呼ぶ. この被演算子は, その左辺であり, 右辺の値を左辺に代入 する. この時, 被演算子の型は, ファイル型を除く全ての型で有効である. 代入文の構造は次で定義される.  代入文  ::=  変数  :=  式  |  関数名  :=  式  5.10.2.1.1 代入可能性 次の5つのうちいずれかが成り立つ時, 型 T 2 の値は, 型 T 1 に対して代入可能 であるという. (a) T 1 と T 2 が同じ型で, その型がファイル型の成分として許されている. (b) T 1 が実数型で T 2 が整数型である. (c) T 1 と T 2 が適合する順序型で, T 2 の値が型 T 1 の定める区間内にある. (d) T 1 と T 2 が集合型で, T 2 の値の全ての元が, 型 T 1 の基底型が定める区間内にある. (e) T 1 と T 2 が適合する文字列型である. 型 T 2 が型 T 1 に対して代入可能である時に限り, 型 T 1 を持つ変数に型 T 2 を持つ値を代入すること ができる. 代入とは「値を与える」ことであり, 値が不定なものを代入することは誤りである. 5.10.2.2 否定演算子 否定演算子 not は, 一つの論理型の被演算子を持ち, 被演算子の否定を表す.  否定演算子  ::= not 5.10.2.3 乗法演算子 乗法演算子とは次で定義されている.

 乗法演算子  ::= * | / | div | mod | and

それぞれの演算は次のようなものである.

(18)

演算子 操作 被演算子の型 結果の型 * 乗算 実数型, 整数型 実数型, 整数型 積集合 任意の集合型 被演算子の型 / 除算 実数型, 整数型 実数型 div 小数点以下切捨ての除算 整数型 整数型 mod 余り 整数型 整数型 and 論理積 論理型 論理型 ここで, 除算が / と div の2種類があることに注意せよ. 5.10.2.3.1 除算の規定 除算 / においては, x/y の時, y が 0 であれば, それは誤りであり, y が 0 でな い時, x/y は, x と y で割った結果である. 除算 x div yは, y が 0 であれば誤りであり, そうでなければ, 次の結果を満たす. abs(i) - abs(j) < abs((i div j) * j) ≤ abs(i)

ここで, 標準関数 abs は, 整数型の引数をとり, その絶対値を整数型として返す. ただし, abs(i) < abs(j) ならば i div j は 0 である. 剰余 i mod j は, j が 0または負の時には誤りである. そうでなければ, i mod j は次の2つの式を満 たす. • i mod j = j - (k*j) ただし, k は整数. • 0 ≤ i mod j < j したがって, i≥ 0, j > 0 の時に限って, (i div j) * i + i mod j = i が成り立つ. 5.10.2.4 加法演算子 加法演算子とは次で定義されている.  加法演算子  ::= +| - | or それぞれの演算は次のようなものである. 演算子 操作 被演算子の型 結果の型 + 加算 実数型, 整数型 実数型, 整数型 和集合 任意の集合型 被演算子の型 - 減算 実数型, 整数型 実数型 差集合 任意の集合型 被演算子の型 or 論理和 論理型 論理型 この時, +, - に関しては, 一つの被演算数しか持たない時には, - は符号の反転を表し, + は恒等変換を 表す. 5.10.2.5 関係演算子 関係演算子とは, 2つの被演算子の間の関係を判定するもので, 次で定義されている.

(19)

 関係演算子  ::= = | <> | < | > | <= | >= | in それぞれの演算は次のようなものである. 演算子 被演算子の型 結果の型 in 左被演算子:任意の順序型 T 論理型 右被演算子:正規 T 集合型 論理型 =, <> 任意の単純型, ポインタ型, 文字列型, 正規 T 集合型 論理型 <, > 任意の単純型, 文字列型 論理型 <=, >= 任意の単純型, 文字列型, 正規 T 集合型 論理型 演算子 <>, <=, >= はそれぞれ, 等しくない, 等しいか小さい, 等しいか大きいを表す. また, <=, >= が集合 型に適用された時には, 集合の包含関係を表す. さらに, p, q が論理式(論理型の値を持つ式)であれば, p=q は論理式の同値性を表す. また, p<=q は, p ならば q であることを表す. これは, false < true であることから計算される. 論理型の変数に対する <>は排他的論理和と同義である. また, 関係演算子を文字列に適用した場合には, いわゆる辞書式順序によって比較が行なわれる. 5.10.2.6 算術演算子と型変換 算術演算子とは, 乗法演算子, 加法演算子のうち, 被演算子として, 整数型または実数型を持つものであ る. この時, 被演算子として, 整数型もしくは実数型となっているものに対しては, その演算結果は, 両方 の被演算子が整数型であれば, 整数型となり, そうでなければ実数型となる. また, 実数型の変数と整数型 の変数の算術演算を行なう時には, 整数型の変数は, 実数型の値へと自動的に変換を受ける. この型変換は, Pascal における自動型変換の唯一の例である. その他の型の間の変換は, 明示的に変換関数を用いなけれ ばならない. 整数型の処理系定義の値 maxint は, 次の条件を満足する. • 区間 [-maxint, maxint] の中の値は, 全て整数型で表すことができる. • その区間内の2つの整数値に対する2項算術演算は, その結果も区間内に収まるならば, 整数計算に おける正しい数学的演算を行なう. • この区間内の2つの整数値に対する2項関係演算は, 整数計算における正しい数学的演算を行なう. この規則に当てはまらないものに関しては, 正しい演算が行われるか, 誤りとなるかは処理系による. 5.10.2.6.1 変換関数 trunc(x) 式 x の値は実数型. 結果の型は整数型である. trunc(x) の結果は, x が正または0であれば, 0≤ x - trunc(x) < 1 を, そうでなければ, −1 < x + trunc(x) ≤ 0 を満たす. そのような値が存 在しなければ, 誤りである. 例えば, trunc(3.5) = 3, trunc(-3.5) =−3 である. round(x) 式 x の値は実数型. 結果の型は整数型である. round(x) の結果は, x が正または0であれば, round(x)は trunc(x+0.5) と等価. そうでなければ, round(x) は trunc(x-0.5) と等価である. そ のような値が存在しなければ, 誤りである. 例えば, round(3.5) = 4 である.

ここで, round(-3.5) を考えてみよう. 実数型の値の与え方は, 処理系定義であるので, 実際に 3.5 の値が 3.4999· · · となってしまうかも知れない. このようなことがありうるので, 注意しなくてはならない.

(20)

5.10.3 演算子の優先順位と式

ここで定義した演算子に対しては, その優先順位が定められ, それによって「式」が定義される. 演算子 の優先順位は, 否定演算子, 乗法演算子, 加法演算子, 関係演算子の順に低くなり, 優先順位を持つ演算子の 列は, 左から右に向かって実行される. この場合, 被演算子(オペランド)の評価順序は処理系依存なので, 注意しなくてはならない. (一般に, 式の出現ごとにも異なることがありうる.)これを構文として定義す ると, 以下のようになる.  符号なし定数  ::=  符号なし数  |  文字列  |  定数名  | nil  因子  ::=  変数  |  符号なし定数  |  関数呼びだし  |  集合  | (  式  ) | not  因子   集合  ::= [  要素の並び  ]  要素の並び  ::=  要素  { ,  要素  }|  空   要素  ::=  式  |  式  ..  式   項  ::=  因子  |  項   乗法演算子   因子   単純式  ::=  項  |  単純式   加法演算子   項  |  加法演算子   項   式  ::=  単純式  |  単純式   関係演算子   単純式  ここで, 集合の構成要素となる式は, 全てその集合の基底の型と同一でなくてはならない. [] は空集合を 表し, [x..y] は, 区間 x から y の全ての要素からなる集合を表す. Example 5.10.1 因子とは, x 15 (x+y+z) not p [red,black,green] [1..10] などである. また, 項とは x*y p or q

(x<=y) and (y<z) などである. 単純式とは x+y -x i*j-1 などである. 式とは x = 1.5 p<=q (i<j)=(j<k) c in h である. この定義により, もっとも優先順位が高いのが not であり, 優先順位がもっとも低いのが関係演算子であ ることがわかる. もちろん, () を用いることにより, 先に優先順位の低い式を実行することができる. この定義によると, 式を評価する際には全ての単純式が左から右に向かって評価されることになる. Example 5.10.2 次のような場合を考えてみよう.

(21)

a := 1 ; b := 2 ; c := 3 ; d := 4 ; この時, (a > b) and (c < d) を左から右に評価すると, a > b を評価して, その値が false であることがわかる. したがって, and の 第2被演算子が何であっても, この式の値は false であることがわかる. 【重要な注意】 Pascal においては, 二項演算子の評価順序は規定されていない. その意味は, 書かれてい る順序で評価しても良いし, 逆順に評価しても良いし, 並列に同時に評価しても良い. それどころか, 出現 するたびに評価順序も異なることがある. 一方 C においては, 式の評価順序は確定していて, さらに, 式の値が定まった時点で式の評価を終る. こ れを短絡評価と呼ぶ. Pascal においては最後まで式の評価が行なわれるかどうかは不確定である. また, C とは優先順位が異なることに注意せよ. 例えば, 上と同じことを C では, a > b && c < d と書けるが, Pascal では, and の優先順位が >, < よりも高いので, このままでは文法エラーとなる.

5.10.4 非標準の演算子

SPARCompilerでは, 次の演算子が定義されている8 . 演算子 操作 被演算子の型 結果の型 ~ bitwise not 整数型 整数型

& bitwise and 整数型 整数型

| bitwise or 整数型 整数型

! bitwise or 整数型 整数型

ここで, bitwise not は否定演算子, bitwise and は乗法演算子, bitwise or は加法演算子として定義されて いる

5.10.5 非標準の関数など

SPARCompilerでは, 次の関数が定義されている9. 関数名 動作 asl 整数型の変数の左算術シフト asr 整数型の変数の右算術シフト lsl 整数型の変数の左論理シフト lsr 整数型の変数の右論理シフト 8 その他にも2つほどあるのだが. 9 これ以外にもいくつかあるので, それらは SPARCompiler のマニュアルを参照.

(22)

5.10.5.1 シフト 整数型の変数に対するシフトとは, 次のようなものである. 前に述べた通り, 整数型の変数は2進数で表 現され, 先頭のビットは符号を表している. その変数を左(右)に 1 シフトするとは, ビット列として各 ビットを左(右)に 1 ずらすことをいう. したがって, 符号ビットを無視すれば, 左に 1 シフトするとは, その整数に 2 を掛けることに等しく, 右に 1 シフトするとは, 2 で割った商を求めることに等しい. しかしながら, 先頭に符号ビットがついているので, それらの扱いが問題となるため, 算術シフト, 論理シ フトの2種類がある. 算術シフトとは, 符号ビットを保存したシフトである. Example 5.10.3 以下は, それぞれの16ビット整数を右に 1 算術シフトした結果である. シフトする前 シフトした後 1111 1111 1111 1000 1111 1111 1111 1100 0000 0000 0000 1000 0000 0000 0000 0100 同様に算術左シフトとは, 符号を保存して左に 1 シフトする. Example 5.10.4 以下は, それぞれの16ビット整数を左に 1 算術シフトした結果である. シフトする前 シフトした後 1111 1111 1111 1000 1111 1111 1111 0000 0000 0000 0000 1000 0000 0000 0001 0000 一方, 論理シフトとは, 符号ビットも含めてシフトする方法である. Example 5.10.5 以下は, それぞれの16ビット整数を右に 1 論理シフトした結果である. シフトする前 シフトした後 1111 1111 1111 1000 0111 1111 1111 1100 1000 0000 0000 1000 0100 0000 0000 0100 Example 5.10.6 以下は, それぞれの16ビット整数を左に 1 論理シフトした結果である. シフトする前 シフトした後 1111 1111 1111 1000 1111 1111 1111 0000 0100 0000 0000 1000 1000 0000 0001 0000

5.11

いくつかのプログラム

ここまででは, 変数の値を表示する方法は述べていなかった. それをするためには, writeln 手続きを 使う. Example 5.11.1 その利用法は, 次の通りである.

(23)

program print_example (output) ; var a : char ; b,c : integer ; x : real ; begin a := ’a’ ; b := 1 ; c := 2 ; x := 1.0 ; writeln(’a = ’,a) ; writeln(’b = ’,b,’ c = ’,c) ; writeln(’b div c = ’,b div c) ; writeln(’b / c = ’,b/c) ; writeln(’x = ’,x)

end.

writeln手続きの出力の形式については, SPARCompiler の writeln の項を参照せよ. 特に実数の出力 の形式には注意しなくてはならない. Exercise 5.11.1 色々な型の演算の値を出力するプログラムを書け. Exercise 5.11.2 integer型で表現される最大の数に 1 を加えたらどうなるかを考察せよ. Exercise 5.11.3 正の浮動小数点数の小数点以下を四捨五入した値を求めるプログラムを書け. Exercise 5.11.4 正の浮動小数点数の小数点以下第2桁めを四捨五入した値を求めるプログラムを書け. この際, writeln が浮動小数点数をどのように表示するかを確かめよ. 一般に, 出力関数がどのような仕様 になっているかは処理系依存なので, 浮動小数点数を利用する前に, その辺を確かめておく必要がある. Exercise 5.11.5 AND, OR, NOTから XOR を作れ.

5.12

文とはアルゴリズム上の動作を表す. 文は実行されるものである.  文  ::= {  ラベル  : }  単純文  |  構造文 

5.12.1 単純文

単純文とは, 他の文を含まないものであり, 空文とは字句を含まない, すなわち, 何もしない文である.  単純文  ::=  空文  |  代入文  |  手続き呼びだし文  | goto 文   空文  ::= 代入文は前に解説した. 手続き呼びだし文に関しては, 後で説明する. 5.12.1.1 goto 文 goto 文の構文は以下の通りである.

(24)

goto 文  ::= goto  ラベル 

goto 文は, プログラム中にラベルで指定された他の部分への制御の移動を行う. ラベルとそれを参照す

る goto 文との間にはある条件が成り立たなければならない. しかし, Pascal において, goto 文はプログ ラムの自然な流れを壊さなければならないような, 異常な状態の処理にのみ用い, それ以外で goto 文を利 用しなければならないようなことはあり得ない. もし, そのようなことがおこれば, アルゴリズムをもう一 度見直した方がよい. このような理由で, ここでは goto 文に関する解説を行わない. 詳しくは [1, 解説書 Section 4]を参照.

5.12.2 構造文

構造文は, 以下の構文を持つ.  構造文  ::=  複合文  |  条件文  |  繰り返し文  | with 文   文の列  ::=  文  { ;  文  } ここで, 文の列は, そこに含まれる文を文面上の順序にしたがって実行する. ただし, goto 文の実行によっ て変更された場合を除く. 5.12.2.1 複合文 複合文とは, 文の列の実行を指定する.  複合文  ::= begin  文の列  end Example 5.12.1 次は複合文である. begin z := x ; x := y end 5.12.2.2 条件文 条件文は以下の構文を持つ.  条件文  ::= if 文  | case 文  5.12.2.3 if文 if文とは, 論理式の値によって制御を分岐させるものである.

if 文  ::= if  論理式  then  文  { else 部  } else 部  ::= else  文 

ここで, if 文の「論理式」の値が true であれば, if 文の「文」が実行される. 「論理式」の値が false の 場合には, if 文の「文」は実行されず, else 部があればその「文」が実行される.

else 部を持たない if 文の直後には字句 else は現れてはならない. これは,

(25)

if e1 then if e2 then s1 else s2 を

if e1 then

begin if e2 then s2 else s2 end と解釈するという意味である. Example 5.12.2 以下は正しい if 文の例である. if x <> 1.5 then z := x + y else z := 1.5 ; if x <> 1.5 then z:= x + y if x <> 1.5 then if y <> 2.0 then z:= x + y else z := y else z := x 5.12.2.4 case文 case文とは, そこに現れた「選択定数」の値によって, 選択肢を実行する分岐文である.

case 文  ::= case  選択式  of  選択肢  { ;  選択肢  } end  選択肢  ::=  選択定数並び  :  文   選択式  ::=  式   選択定数並び  ::=  選択定数  { ,  選択定数  }  選択定数  ::=  定数  Example 5.12.3 次が case 文の例である. case x of 1: y := x ; 2: z := x ; 3,4: w := x end ここで, case 文に入る時には, 「選択式」の値が「選択定数」のいずれかに一致しなくてはならない. そう でなければ誤りである.

5.12.3 繰り返し文

繰り返し文とは, 指定の条件の下に, 文を繰り返し実行することを指定する.

 繰り返し文  ::= repeat 文  | while 文  | for 文 

(26)

5.12.3.1 repeat

repeat 文  ::= repeat  文の列  until  論理式 

repeat文の文の列は, 文の列の実行の後に論理式が true となるまで繰り返し実行される. 論理式が文の 列の実行の後に評価されるので, 少なくとも1回は文の列は実行される.

Example 5.12.4 次のプログラムは, 1 から 10 までの和をとる文である. program sum ;

var i,j : integer ; begin i := 0 ; j := 1 ; repeat i := i + j ; j := j+1 ; until j = 11 ; end. 5.12.3.2 whilewhile 文  ::= while  論理式  do  文  while文 while b do body は begin if b then repeat body until not(b) end と等価である. すなわち, 論理式が最初に評価され, true であれば, 文が実行され, false ならば実行はさ れない. したがって, 「文の列」は一度も実行されないことがある. Example 5.12.5 次のプログラムは, 1 から 10 までの和をとる文である. program sum ;

var i,j : integer ; begin i := 0 ; j := 1 ; while j <= 10 do begin i := i + j ; j := j+1 ; end end. 5.12.3.3 for文 for文とは, 制御変数が表す変数に一連の値を与えながら文を繰り返し実行することを指定する.

(27)

for 文  ::= for  制御変数  :=  初期値  { to | downto }  終値  do  文   制御変数  ::=  純変数   初期値  ::=  式   終値  ::=  式   純変数  ::=  変数名  for文は, 以下の制限を持つ. • 制御変数はその for 文を直接に含むブロック内の変数宣言部で宣言されたものを名称とする変数でな ければならない. • 制御変数の型は順序型でなければならない. 初期値, 終値はその型に適合しなければならない. • for 文の文が1回でも実行されるなら, 初期値と終値は制御変数の型に対して代入可能でなければなら ない. また, for 文の実行後, その制御変数の値は不定となる. ただし, goto 文で抜け出した場合は除く. for文の内部の文 S で制御変数 V に対して, つぎに挙げることをしてはならない. • S が代入文であり, V に値を代入する. • S が for 文であり, V は S の制御変数である. • S が関数の実引数である. • S が手続き read または readln の実引数である. これらの制約を除けば, for v := e1 to e2 do body は, 順序型の変数 v を初期値 e1 から, 終値 e2 まで, 関数 succ を利用して一つずつ動かしながら文 body を実行する. 特に, 上の制約を除き, 次の文と等価である. begin t1 := e1 ; t2 := e2 ; if t1 <= t2 then begin v := t1 ; repeat body ; v := succ(v) ; until v > t2 end ; また,

for v := e1 downto e2 do body

は, 順序型の変数 v を初期値 e1 から, 終値 e2 まで, 関数 pred を利用して一つずつ動かしながら文 body を実行する.

Example 5.12.6 次のプログラムは, 1 から 10 までの和をとる文である. program sum ;

var i,j : integer ; begin

i := 0 ;

for j:=1 to 10 do i := i + j ; end.

(28)

Example 5.12.7 次のプログラムは, 1 から 10 までの和をとる文である. program sum ;

var i,j : integer ; begin i := 0 ; for j:=10 downto 1 do i := i + j ; end. このように, for 文では, 制御変数を 1 刻みにしか動かせないので, それ以外の時には while 文, または repeat文を利用することとなる. 5.12.3.4 with 文 with文に関しては, 後ほど述べる.

5.12.4 演習問題

ここの演習問題は, ここまでに学んだ繰り返し文や条件文を使って書くことができる. はじめに演習問題をやるために必要な関数について注意しておく. 5.12.4.1 数値を入力する 標準入力から整数数値を入力するためには, readln を使う. readln(v) 利用法は, 以下の通り. Example 5.12.8 標準入力から整数を読んで, それを出力する. var n : integer ; begin readln(n) ; writeln(n) end. 5.12.4.2 乱数の発生

randomという(SPARCompiler に付属の)関数は real 値の乱数を発生させる. 通常, 次のようにして 使う. Example 5.12.9 乱数を2つ発生させる. var n : integer ; r, s : real ; begin n := seed(wallclock) ; r := random(n) ; s := random(n) ; end.

(29)

(SPARCompilerに付属の)関数 seed の部分は, 乱数を初期化する部分で, 現在の時刻から決まる数を 使って初期化をしている. また, random の引数 n は特に意味はないが, 文法上必要なものである. ここで, 発生する乱数は 0 から 1 の間の数が出てくる.

Exercise 5.12.1 repeat文, while 文, for 文を利用して, 2 から N 迄の偶数の和を計算して出力するプロ グラムを書け. Exercise 5.12.2 摂氏と華氏の温度対応は, ◦C = (5/9)(◦F − 32) である. 華氏の温度(整数)を入力し て, 摂氏の温度を印字するプログラムを書け. その際, 摂氏の温度として, 小数点以下切捨て, 小数点以下四 捨五入, 小数点以下第2桁めを四捨五入の3種類を書け. Exercise 5.12.3 非負の integer 型の数値を入力して, それを2進数で表示するプログラムを書け. Exercise 5.12.4 0から 1 までの乱数を十分沢山発生させ, その値が 0.5 未満の確率を表示するプログラ ムを書け. Exercise 5.12.5 for

for v := e1 downto e2 do body と等価な repeat 文を作れ.

5.13

構造つきの型

構造つきの型は, 構造型, レコード型, 集合型, ファイル型に分類される.  構造型  ::=  書き下し構造型  |  構造型名   構造型名  ::=  型名   書き下し構造型  ::= { packed }  詰めなし構造型   詰めなし構造型  ::=  配列型  |  レコード型  |  集合型  |  ファイル型  書き下し構造型に packed がある場合には, その型は「詰めあり」と呼ばれる. 構造型に対する詰めあ りの指定は, 値のデータ領域を節約するように処理系に指示する働きがあり, その型の変数に対する演算な どが高速に行なわれる可能性がある10 .

5.13.1 配列型

配列 (array) 型は, 添字型で示される値から個々の成分への写像としての構造を持つ. 各成分は配列型の 成分型の型表記で示される型を持つ.  配列型  ::= array [  添字型  { ,  添字型  } ] of  成分型   添字型  ::=  順序型   成分型  ::=  型 

Example 5.13.1 例えば, integer 型の添字 0 から 9 までを持つ配列型の変数 digit は次のように定義 される.

var digit : array [0..9] of integer ;

10SPARCompilerでは, packed には何も効果がない.

(30)

この時, 添字型 0..9 は整数型の部分範囲型として決めている.

digit[0] digit[1] digit[2] digit[3] digit[4] digit[5] digit[6] digit[7] digit[8] digit[9]

その他にも, 次のようなものも定義できる. array [Boolean] of color

ここで, 型名 color は, 正当に定義された型であるとしている.

Pascal においては, 配列が定義されている添字以外への参照は誤りとなる. すなわち,

var digit : array [0..9] of integer ;

と定義されている配列 digit に対して, digit[10] を参照することは出来ない. 二つ以上の「添字型」の列を指定する配列型は, 次のような配列型の省略記法である. • 「添字型」はもとの配列型の最初の「添字型」である. • 「成分型」はもとの指定の2番め以降の「添字型」の列を持ち, もとの指定と同じ「成分型」を持つ 配列型である. この「成分型」は, もとの配列型が詰めありの時に限り, 詰めありとなる. 次の2つの例は, それぞれ同じ配列型の異なる表現法を表す. Example 5.13.2

array [Boolean] of array [1..10] of array [size] of real array [Boolean] of array [1..10,size] of real

array [Boolean,1..10] of array [size] of real array [Boolean,1..10,size] of real

Example 5.13.3

packed array [1..10,1..8] of Boolean ;

packed array [1..10] of packed array [1..8] of Boolean ; Example 5.13.4

var str : array [0..1][0..9] of integer ; str[i]は, 10 個の要素を持つ配列となる. str[0] [0] str[0] [1] str[0] [2] str[0] [3] str[0] [4] str[0] [5] str[0] [6] str[0] [7] str[1] [0] str[1] [1] str[1] [2] str[1] [3] str[1] [4] str[1] [5] str[1] [6] str[1] [7] str[0] [8] str[0] [9] str[1] [8] str[1] [9] 配列型の変数の値は次のようにして定義される. 配列型の添字型の値の最小値を m, 最大値を n とした時, k = ord(n)−ord(m)+1 とおき, 配列型の値は, 成分型の値の k 個の組である. それを, v = (v1, · · · , vk) と書いた時, 配列型の変数 v の値は v であり, v[i] は vord(i)−ord(m)+1 を表す. このような, 配列型の

成分を表す変数を添字つき変数と呼ぶ.  成分変数  ::=  添字つき変数  |  フィールド表記   添字つき変数  ::=  配列変数  [  添字式  { ,  添字式  } ]  添字式  ::=  式   配列変数  ::=  変数  配列型の変数の値は, その成分が全て定義されている時に限り, 決まっている. これらの配列の定義は, コンパイル時に配列の大きさが決定できることが必要であり, 実行時に配列の大 きさを変更したりすることはできない. 2つ以上の添字を持つ配列(多次元配列)の添字の評価順序は処理系依存である.

(31)

5.13.1.1 配列型の例

ここでは, real 型の配列をベクトルと思い, その和を計算してみよう. program sum_of_vector ;

var

a,b,c : array[1..3] of real ; i : integer ;

begin

a[1] := 1.0 ; a[2] := 1.0 ; a[3] := 2.0 ; b[1] := 2.0 ; b[2] := 0.0 ; b[3] := 3.0 ; for i:=1 to 3 do

c[i] := a[i] + b[i] ; end.

5.13.2 文字列型

文字列型とは, 次のような詰めありの配列型である. • 「添字型」は最小値が 1 で, 最大値が 1 より大きいような部分範囲型. • 「成分型」は文字型. 文字列型は, その値をテキストファイルに書くことができる, 関係演算子が利用できるという意味で, 通 常の配列型とは異なった性格を持つ. 5.13.2.1 文字列の例 ここでは, 文字列型の変数に文字を代入して, 出力をしてみる. program output_of_string (output) ;

var

s : packed array [1..30] of char ; i : integer ;

begin

for i := 1 to 30 do

s[i] := chr(ord(’A’) + (i-1)) ; writeln(s) ; end. この例で, 30 まで値を与えなかった時, SPARCompiler は値が与えられた範囲のみを出力する. また, SPARCompilerでは, 次のような特別な型が用意されている. 型名 内容 alfa 長さ 10 の文字列型 string 長さ 80 の文字列型 varying 可変長の文字列型, varying[u]と定義した時, 長さ u の文字列型を表す. uは 0 から 65535 まで. ただし, これらの型で指定した文字列を writeln で出力する時には, それぞれの型で結果が異なることが あるので注意すること.

(32)

var

name1 : packed array [1..25] of char ; name2 : packed array [76..100] of char ; name3 : alfa ;

name4 : string ;

name5 : varying [25] of char ; name6 : varying [25] of char ; begin name1 := ’a’ ; name2 := ’a’ ; name3 := ’a’ ; name4 := ’a’ ; name5 := ’a’ ; name6 := ’a’ ;

writeln(name1, ’ and ’, name2) ; writeln(name3, ’ and ’, name4) ; writeln(name4, ’ and ’, name6) end.

5.13.2.2 配列と文字列の初期化

SPRACompiler では, 配列, 文字列を宣言と同時に初期化できる. var

int : array [1..10] of integer = [maxint,1,-375,5,20] ; ch : packed array [1..10] of char = ’ABCDEF’ ;

int2 : array [1..*] of integer = [maxint,1,-375,5,20] ; ch2 : packed array [1..*] of char = ’ABCDEF’ ;

ここで, * が利用されている例では, 初期化が同時に行なわれているので, 必要最小限の量の配列が自動的 に確保される.

var

int : array [1..100] of integer = [50 of 1,50 of 2] ; この例では, 最初の 50 個に 1 が, 次の 50 個に 2 が入る.

var

int : array [1..100] of integer = [* of 1] ;

int2 : array [1..10, 1..10] of integer = [[* of 1], [3 of 8], [10 of 88],] ; 二つめの例では, 第1列の全てに 1 が, 第2列の最初の3つに 8 が, 第3列の全てに 88 が入る. このように, SPARCompiler では, 配列を宣言と同時に初期化する場合には, 配列の大きさをわざわざ書 かなくても済むのだが, やはりコンパイル時に配列の大きさが決定されていることに注意. 決して, 実行時 に配列の大きさを変更できているわけではない.

5.13.3 集合型

集合型は, ある基底の型のベキ集合としての構造を持つ. 集合型の各々の値は, 基底型の値を元とする集 合である.  集合型  ::= set of  基底型   基底型  ::=  順序型  次は, 集合型の例である.

(33)

set of char ;

set of (club, diamond, heart, spade) ;

各順序型 S に対して, 正規 T 集合型と呼ばれる集合型が存在する. もし, S が部分範囲型であれば, T は その親の型であり, そうでなければ, T は S である. 正規 T 集合型とは, A : set of 1..5 ; B : set of 2..9 ; に対して, A and B が定義できるようにした仮想的な型のことである. 5.13.3.1 集合型の例 program set_operation ; type days = (mon,tue,wed,thr,fri,sat,sun) ; week = set of days ;

var wk,work,free : week ; d : days ; begin work := [] ; free := [] ; wk := [mon..sun] ; d := sat ;

free := [d] + free + [sun] end.

この例では, 曜日の列挙型 days と, その集合型 week を定義している. さらに, week 型の変数 wk, work, freeを定め, 最終的に, free は要素 sat と sun を持つ集合型となっている.

5.13.3.2 集合型の内部表現 集合型の変数は, 内部では次のように表現されている. 例えば, T : set of T0 となっているとしよう. この時, T0 が N (T0) 個の要素を持つとすると, T の型の変数のメモリ領域は, 少なくとも N (T0) ビット必要である. 実際には, 中途半端なビット数のメモリ領域は利用できないので, SPARCompilerでは, 少なくとも16ビット, さらに, 16ビットの倍数のメモリ領域が利用され, その最 大は256ビットである.

5.13.4 レコード型

レコード型とは一つの型の中に必ずしも同一の型ではない決まった数の成分を持つような型のことであ る. それぞれの成分をフィールド (field) と呼ぶ.

(34)

 レコード型  ::= record  フィールド並び  end  フィールド並び  ::= {  固定部  { ;  可変部  }|  可変部  }{ ; }  固定部  ::=  レコード要素  { ;  レコード要素  }  レコード要素  ::=  名称並び  :  型表記   可変部  ::= case  可変要素選択子  of  可変要素  { ;  可変要素  }  可変要素選択子  ::= {  タグフィールド  : }  タグ型   タグフィールド  ::=  名称   可変要素  ::=  選択定数並び  : (  フィールド並び  )  タグ型  ::=  順序型名   選択定数並び  ::=  選択定数  { ,  選択定数  }  選択定数  ::=  定数  固定部も可変部も持たないフィールド並びは, 成分を持たず, 値は空値ただ1種類を持つ. このような フィールド並びは空であるという. Example 5.13.5 例えば, 固定部のみからなるレコード型は次のようなものである. record year : 0..3000 ; month : 1..12 ; day : 1..31 end

これは, フィールドとして, year, month, day を持ち, それぞれが integer 型の部分範囲型として定義さ れている. m 個の成分を持つ空でないフィールド並びの値は, 次の m 組である. ただし, Vi はそのフィールド並び の i 番めの成分である. (V1, · · · , Vm) 可変部を直接に含むフィールド並びは, その可変部に対応した一つの成分を持つ. この成分の値と構造は, その可変部によって定義される. Example 5.13.6 可変部を含むレコード型は次のようなものである. type month_type = 1..12 ; days_type = record year : 0..3000 ;

case days : month_type of

1,3,5,7,8,10,12 : (days_1 : 1..31) ; 4,6,9,11 : (days_2 : 1..30) ; 2 : (days_3 : 1..28) end 可変部の値は, 可変部の選択子の値 k と可変部の有効な可変要素のフィールド並びの値 Xk によって決ま る (k, Xk)である. Example 5.13.7 固定部と可変部を持つレコード型の例は次のようなものである.

(35)

record

x, y : real ; area : real ; case shape of

rectangle : (side1, side2 : real ; skew : angle) ; circle : (diamiter : real)

end

この例では, 「可変要素選択子」で「タグフィールド」を利用していない. この時, x をこの型の変数と すると, x.side1 を利用すると, それは 可変部が rectangle であるとみなされる. 可変部が rectangle の時, diamiter は有効ではない. レコード型の変数の各フィールドの値は, そのフィールド名を変数名のあとに . を使ってつなぐことによ り得ることができる.  フィールド表記  ::=  レコード変数  .  フィールド指定部  |  フィールド表記名   レコード変数  ::=  変数   フィールド指定部  ::=  フィールド名   フィールド名  ::=  名称  ここで, 可変部の選択子が不定であれば, 可変部のどの可変要素も有効ではない. また, フィールド名は 変数名とは別の名前空間に属するので, 次のような名前の与え方は間違いではない. a : record a : real ; b : real ; end ; b : integer ; Example 5.13.8 この例は, 固定部と可変部を持つレコード型を定義し, そのフィールドに値を代入して いる.

参照

関連したドキュメント

Thus, in order to achieve results on fixed moments, it is crucial to extend the idea of pullback attraction to impulsive systems for non- autonomous differential equations.. Although

Corollary 24 In a P Q-tree which represents a given hypergraph, a cluster that has an ancestor which is an ancestor-P -node and spans all its vertices, has at most C vertices for

Turmetov; On solvability of a boundary value problem for a nonhomogeneous biharmonic equation with a boundary operator of a fractional order, Acta Mathematica Scientia.. Bjorstad;

Since we are interested in bounds that incorporate only the phase individual properties and their volume fractions, there are mainly four different approaches: the variational method

It is evident from the results that all the measures of association considered in this study and their test procedures provide almost similar results, but the generalized linear

It is easy to prove that B X (D) is a semigroup with respect to the operation of multiplication of binary relations, which is called a complete semigroup of

called a Hecke polygon, which is an admissible fundamental domain for the group generated by the side pairings of it.. There is a correspondence between Hecke polygons and subgroups

, n is called a recursive tree if the vertex labelled 1 is the root and, for all 2 ≤ k ≤ n, the sequence of vertex labels in the path from the root to k is increasing (Stanley