プログラムは一連の計算のステップによって記述されるが,それらの中では,いくつかの部分的な仕事に 分割し,それぞれをプログラム部分として定義することが可能である.
このようなプログラムの部分的な仕事を手続きもしくは関数としてはっきりとした部分として定義する ことができる.
3.13.1 手続き
手続き宣言はプログラムの一部分を定義するものであり,手続き呼びだし文によって,それらを実行する ことができる.
この宣言は,プログラムと同一の形式であり,プログラムの頭書きの変わりに手続きの頭書きを利用する.
手続き宣言 ::= 手続き見出し ; 指令 | 手続き表示 ; 手続きブロック
| 手続き見出し ; 手続きブロック
手続き見出し ::= procedure 名前 ({ 仮パラメタ節 {; 仮パラメタ節 }}) 手続き表示 ::= procedure 手続き名
手続き名 ::= 名前
手続きブロック ::= ブロック 指令 ::= 英字 { 英字 | 数字 }
仮パラメタ節 ::= パラメタ群 |var パラメタ群 | 関数引数仕様 | 手続き引数仕様 パラメタ群 ::= 名前 {, 名前 }: 型名
手続き引数仕様 ::= 手続き見出し 関数引数仕様 ::= 関数見出し
手続きを呼び出す場合には,その手続きは呼び出す以前に宣言されていなければならない. 従って,2つ の手続きもしくは関数をお互いに呼びあう場合などには問題が生じる. その場合に利用されるのが, 指令 forward である. 指令 forward のついた手続き宣言は,その手続きブロックが後方にあってもよいが, どこかに手続きブロックが存在しなければならない.
3.13.1.1 手続きブロックでの宣言
手続きブロック内で宣言されたラベル,定数,型,変数は,そのブロック内でのみ有効であり,そのブロッ ク外からは参照できない. このようなものを,局所的に定義されているという. 例えば,局所的に定義され た変数は局所変数と呼ぶ. また,外のブロックと同じ名前で宣言されたものがある場合には,より内部で定 義されたものが有効となる. これを(変数の)隠蔽と呼ぶ.
局所変数はプログラムの実行中には, その局所変数を定義するブロックを駆動中の時のみメモリ内に存 在する. それ以外の時には,その局所変数はメモリ内には存在しない.
ただし, SPARCompilerの static をつけて宣言された局所変数は, そのブロックの駆動中以外でもメ モリ内には存在しているが,外部のブロックからは見ることができない. このような,その変数がどこのブ
ロックから参照可能かの概念を,変数のスコープと呼ぶ. また,どのような時に変数がメモリ内にあるかの 概念を変数の寿命と呼ぶ.
3.13.1.2 手続きの仮パラメタ節と呼びだし
手続きの仮パラメタ節中で定義された変数は,局所変数と同様に扱うことができる. 実際に,手続き呼び だし文において書かれた変数を実引数と呼ぶ.
手続き呼びだし文 ::= 手続き名 実引数並び 実引数並び ::= ( 実引数 {, 実引数 }) 実引数 ::= 式 | 変数 | 手続き名 | 関数名
ただし,手続きread,readln,write,writelnの場合には,少々定義が異なる.
仮パラメタとしては,単に変数が書かれた時(値引数), var が指定されている時(変数引数), 手続 き名が指定されている時(手続き引数),関数名が指定されている時(関数引数)によって,呼び出しの方 法が異なる.
3.13.1.2.1 値引数 値引数の場合には,仮パラメタ節に記述された型と同値な型を持つ値であることが
必要である. この場合,手続きにはその値のみが渡される. これを値呼びだし(call by value)と呼ぶ.
3.13.1.2.2 変数引数 変数引数の場合には,仮パラメタ節に記述された型と同値な型を持つ変数である
ことが必要である. この場合, 手続き中ではその変数が参照されている. (具体的には,変数のアド レスが 渡される.)これを参照呼びだし(call by address) と呼ぶ.
3.13.1.2.3 手続き引数 この場合には,実引数の手続きは手続きを呼び出したブロックで定義されてい
なければならない. 手続き中では,その実引数を表している.
3.13.1.2.4 関数引数 この場合には, 実引数の関数は手続きを呼び出したブロックで定義されていなけ
ればならない. 手続き中では,その実引数を表している.
3.13.1.3 手続きの副作用
参照呼び出しで渡した変数は,手続き内ではもとの変数を参照しているので,手続き内で変更をした場合 にも,もとの変数が変更されている. これを手続きの副作用と呼ぶ.
3.13.2 関数
関数は式の評価で利用するために,単一のスカラ値,もしくはポインタ値を計算するプログラムの一部分 である. 関数宣言は関数を定義するものであり,関数呼びだし文によって,それらを実行することができる.
この宣言は,プログラムと同一の形式であり,プログラムの頭書きの変わりに関数の頭書きを利用する.
関数宣言 ::=
関数見出し ; 指令 | 関数表示 ; 関数ブロック | 関数見出し ; 関数ブロック 関数見出し ::= function 名前 ({ 仮パラメタ節 {; 仮パラメタ節 }})
関数表示 ::= function 関数名 関数名 ::= 名前 : 結果型 関数ブロック ::= ブロック
結果型 ::= 単純型名 | ポインタ型名
この定義を見ればわかるように,ほとんど手続きと同じであるが,その値を返すことだけが異なる.
結果の型としては,単純型及びポインタ型以外を返すことはできない.
関数に関することは,値を返すこと以外は全て手続きと同じである.
関数呼びだし文 ::= 関数名 実引数並び
3.13.3 手続きと関数の例
Example 3.13.1 以下の手続きと,それを呼び出しているプログラムは,与えられた2つのinteger型の 変数のうち小さくない方を表示するものである.
program print_max (output) ; var
x,y : integer ;
procedure print_max(x,y : integer) ; var
z : integer ; begin
if (x < y) then writeln(y) else
writeln(x) ; end ;
begin x := 1 ; y := 2 ; print_max(x,y) end.
Example 3.13.2 この例では,2つのinteger型の変数の和を返す関数を記述している.
program print_sum (output) ; var
x,y : integer ;
function return_max(x,y : integer) : integer ; begin
return_max := x + y ; end ;
begin x := 1 ; y := 2 ;
writeln(return_max(x,y)) end.
Example 3.13.3 この例は,配列をベクトルと思い,それの長さを返し,さらに,それに直交するベクトル を一つ求めている11 .
program vector (output) ; type
vector = array [1..2] of real ; var
x,y : vector ; r : real ;
function get_norm_and_normal (a : vector ; var b : vector) : real ; begin
b[1] := a[2] ; b[2] := (-1)*a[1] ;
get_norm_and_normal := sqrt(a[1]*a[1] + a[2]*a[2]) ; end ;
begin
x[1] := 1.0 ; x[2] := 0.0 ;
r := get_norm_and_normal(x,y) ;
writeln(’length of (’,a[1],’,’,a[2],’) = ’,r) ;
writeln(’normal of (’,a[1],’,’,a[2],’) = (’,b[1],’,’,b[2],’)’) ; end.
このように,関数の結果型とはなり得ないものを求めるには,関数もしくは手続きの副作用を利用するこ とができる.
Example 3.13.4 この例は,異なった関数に対して,f(x) +g(y/2)の値を求める関数である.
11本当はちょっと問題のある計算である.
program sum_of_function_values (output) ; var
x,y : real ;
function sin_ft(a : real) : real ; begin
sin_ft := sin(a) ; end ;
function cos_ft(a : real) : real ; begin
cos_ft := cos(a) ; end ;
procedure ch(var a : real) ; begin
a := a/2.0 ; end;
function sum_of_function_values ( a,b : real;
procedure ch (var a : real) ; function f(a : real) : real ;
function g(a : real) : real ) : real ; begin
ch(b) ;
sum_of_function_values := (f(a) + g(b)) ; end ;
begin
x := 0.0 ; y := 1.0 ;
writeln(x,y,sum_of_function_values(x,y,ch,sin_ft,cos_ft)) ; end.
Example 3.13.5 この例は,上の例と同じ結果をもたらすが,forwardを利用している.
program sum_of_function_values (output) ; var
x,y : real ;
procedure ch(var a : real) ; forward ; function sin_ft(a : real) : real ; begin
sin_ft := sin(a) ; end ;
function cos_ft(a : real) : real ; begin
cos_ft := cos(a) ; end ;
function sum_of_function_values ( a,b : real;
function f(a : real) : real ;
function g(a : real) : real ) : real ; begin
ch(b) ;
sum_of_function_values := (f(a) + g(b)) ; end ;
procedure ch ; begin
a := a/2.0 ; end;
begin
x := 0.0 ; y := 1.0 ;
writeln(x,y,sum_of_function_values(x,y,sin_ft,cos_ft)) ; end.