next up previous contents index
: プログラム例 : 関数 : リストとベクトル(配列)   目次   索引

関数と局所変数

関数を用いる最大の利点は, 関数を一旦書いてしまえば, 中身をブラックボックスとして扱えることである. 大規模なプログラムを書くときは複雑な処理をいくつかの関数に分割して まず各関数を十分テストし仕上げる. それからそれらの関数を組み合わせていくことにより, 複雑な機能を実現する. このようなアプローチをとることにより, ``困難が分割'' される.

簡単な関数の例をとり関数の書き方を説明しよう.

def nsum(N) {
  S=0;
  for (I=1; I<=N; I++) {
    S= S+ I;
  }
  return(S);
}

    
関数 nsum(N) $\sum_{i=1}^{\tt N} i$ の値を計算して戻す.
N を関数の引数 (argument) とよぶ.
例:
[445] nsum(10);
55
[446] nsum(100);
5050

関数の戻り値 (return value) は return 文で 与える. いまの場合は変数 S の値である. なお, printreturn は違う. print は画面に値を印刷するのに対して, return は関数の値を戻す働きを持つ. print 文では, 関数の値を戻すことはできない.

関数のなかで利用されている変数と引数は, その関数の実行中のみ生成される変数であり, さらにその関数外部の同名の変数の値を変えない. このように一時的に生成される変数を局所変数 (local variable) とよぶ. 関数を用いて処理を分割したとしても, 関数の中で変数の値を変更したら, その関数の外の同じ名前の変数の 値もかわってしまうとしたら, 分割した利点がすくない. そこででてきた概念がこの ``局所変数'' の概念である. 上のプログラム例では, N, S, I が局所変数である. 局所変数はその関数のなかだけで有効な変数である. これを, ``局所変数のスコープはその関数のなかだけ'' という 言いかたをする. 局所変数の考え方は, 計算機言語の歴史では大発明の一つである.

例:

[447] S=3;     
3
[448] N=4;
4
[449] nsum(10);
55
[450] S;
3
[451] N;
4

    
[447]S と関数 nsum のなかの変数 S は別物 である. したがって, nsum の終了時点で関数 nsum のなかの変数 S の値は $55$ であるが, [450]S の値を表示させてみても やはり $3$ のままである. 引数の N についても同様である. 図 8.1 も参照.

図 8.1: メモリの図解
nsum(10) のよびだし直前
場所 内容
S 3
N 4
   
   
   
    
nsum(10) の終了直前
場所 内容
S 3
N 4
S(nsumS) 55
N(nsumN) 10
   

Asir では関数内部の変数は自動的に局所変数とみなされる. ただしこのような計算機言語はむしろ例外に属し, 多くの 計算機言語では局所変数は宣言しないといけない. たとえば C 言語で, nsum を書くとつぎのようになる.

#include <stdio.h>
int nsum(int N) {
  int S;
  int I;
  S=0;
  for (I=1; I<=N; I++) {
    S= S+ I;
  }
  return(S);
}
main() {
  printf("%d\n",nsum(10));
  printf("%d\n",nsum(100));
}

    
このプログラムは C 言語で 1 から 10 までの和, 1 から 100 までの和を計算して印刷するプログラムの例である. int S;, int I が局所変数の宣言である. この文は実行はされない. unix 上ではこのプログラムをたとえば, local.c なる 名前で save し,
bash$ cc local.c
bash$ ./a.out
でコンパイル, 実行できる.

関数の中から外で定義された変数を変更したいときもあるかも しれない. そのようなときは, 関数の先頭で, extern 宣言すればよい.

def nsum(N) {
  extern S;
  S=0;
  for (I=1; I<=N; I++) {
    S= S+ I;
  }
  return(S);
}

    
[444] S=10;
[445] nsum(10);
55
[446] S;
55
関数 nsum の中の変数 S は, 関数 nsum の 外の変数 S と同一の変数なので, [446]S の値が $55$ になってる.

ことなる関数同士の局所変数は互いに無関係である.

def nsum(N) {
  S=0;
  for (I=1; I<=N; I++) {
    S= S+ I;
  }
  return(S);
}
def make_table(N) {
  for (I=1; I<=N; I++) {
     print(I,0); print(" :  ",0);
     print(nsum(I));
  }
}

    
[347] make_table(4);
1 :  1
2 :  3
3 :  6
4 :  10
0
関数 make_table(N) $\sum_{i=1}^p i$ の表を $p=1, \ldots, {\tt N}$ に対して 作成する. make_tableN, InsumN, I は別ものである. 図 8.2 を見よ.

図 8.2: メモリの図解
make_table(4) の実行中 で nsum(1) を呼ぶまえ.
場所 内容
N(make_tableN) 4
I(make_tableN) 1
   
   
   
    
make_table(4) の実行中 で nsum(3) の終了直前.
場所 内容
N(make_tableN) 4
I(make_tableN) 3
S(nsumS) 6
N(nsumN) 3
   

関数はかならずしも値を戻す必要はない.

def hello(N) {
  for (I=0; I<N; I++) {
     print("Hello!");
  }
}

    
関数 hello(N)N 回 Hello を画面に表示する 関数である.
[346] hello(3);
Hello!
Hello!
Hello!
0
[347] hello(3)$
Hello!
Hello!
Hello!
[348]


next up previous contents index
: プログラム例 : 関数 : リストとベクトル(配列)   目次   索引
Nobuki Takayama 平成15年9月12日