next up previous
Next: 5 関数の宣言と「スコープルール」 Up: 計算天文学 II Previous: 3 簡単な実習

Subsections


4 関数と制御構造

4.1 「値を返さない関数」(手続き)

数学では「関数」といえば、指数関数とか三角関数のように、変数に対 応して値が決まるものだが、C++言語の場合は必ずしもそうではない。以下 の例で説明しよう。

// procedure_sample
#include <iostream>
using namespace std;

#define PI  3.14159265358979

void print_volume(double radius)
{
    cout <<"Radius = " << radius << endl;
    cout <<"Volume = " << radius*radius*radius*PI*4.0/3.0<<endl;
}

int main()
{
    double x;
    cerr << "Enter radius : ";
    cin >> x;
    print_volume(x);
    return 0;
}
このプログラムは、単に適当な数字を読み込んで、その値を半径とする球の体 積を表示するプログラムである。このプログラムでは、実際に体積を計算して 答を表示するのを、 print_volume という名前の関数が行なってい る。

この「関数」は英語の function の訳語であるが、数学的な「関数」と いうより、機能とか働きとかいった意味合いに近い。ただし、すぐあと で説明するように、値を返す関数というものもあり、こちらは数学的な 意味での関数に少し似ている。

値を返さない関数は、

void 名前(型 引数1 [,引数2, ...] [, 型[ ... 引数i [,引数i+1,...]])
{
    [変数宣言]
    実行部
}

という形をとる。このような記述がプログラムのなかにあると、 もとのプログラム、つまり int main() で始まっているところの実行部のな かからここで新しく作った関数を「呼び出す」ことができる。Fortran では call とかがついたが、 C/C++ ではいきなり関数名を書くだけである。

4.2 (値を返す)関数

// bisection
#include <iostream>
using namespace std;

double f(double x)
{
    double y ;
    y = x*x*x - 2;
    return y;
}

void bisection(double &  xmin,
               double &  xmax,
               double eps)
{
    
    double x, f_min, f_max;
    f_min = f(xmin);
    f_max = f(xmax);
    if (f_min * f_max  > 0.0){
        cout <<"cannot find solution...\n";
    }else{
        while(xmax - xmin > eps){
            x = (xmin + xmax) *0.5;
            if (f(x) * f_min > 0.0 ){
                xmin = x;
            } else{
                xmax = x;
            }
            cout << "x= " << x << " f(x)= "<< f(x) <<endl;
        }
    }
}

int main()
{
    double x0,x1, eps;
    x0 = 0.0;
    x1 = 2.0;
    eps = 1e-10;
    bisection( x0, x1, eps);
    cout << "Final x = " << x0 << " " << x1 << endl;
    return 0;
}

ここでは、「関数」らしく値を返すものを使ってみている。値を返さな いものとの違いは、

void 名前(引数の宣言);
の代わりに
型 名前(引数の宣言);
となることと、実行部の最後で、
return 式;

の形の戻すべき値を指定することである。このようにして宣言した関数 は、 C++言語の標準のライブラリに入っている sin, cos, pow など の関数と全く同じように使うことができる。

関数では、値を一つしか返せない。したがって、上の例のように、二分 法で方程式を解いて、区間の両端の値を戻したければ、引数の形で返すことに なる。

とはいうものの、最初の例のところで書いたように、C++では普通に宣言 すると関数の引数の値はコピーされる。で、コピーされた方を書き換えても、元の値は 書き換わらない。元の変数の値を書き換えるためには、上の例のように引数の 宣言のところで型と変数名の間に & をつける。

これは、 C の場合とは大きく違うことに(Cを知っている人は)注意。 もちろん、 C と同じように書くこともできる。

C++ の場合、 & をつけた引数は Fortran の場合とおおむね同じよう に使える。

4.3 プログラムの説明

上のプログラムは、「2分法」というやり方で、方程式の(近似的な)解を求 めるものである。方程式は、関数=0 という形になっているものとしよう。

このやりかたでは、まず最初にどの範囲に答があるかは知っているものとする。 そうすると、下図にあるように、その範囲の両端で関数の符号が違っているはず である。

=10cm \epsffile{bisection.eps}

その区間の中点で関数の値を計算する。図のように、中点での値と左端での値 の符号が同じなら、答えは中点と右端の間にある。この時は、中点の値で左端 の値を置き換える。逆に中点での値と左端での値の符号が違えば、もちろん答 えはその間にある。この時は右端の値を置き換える。いずれの場合でも、答が あるとわかっている区間の幅がもとの半分に狭まる。これを繰り返していって、 答をもとめる。

この方法自体は多分計算天文学 I でもやったと思う。以下、関数 bisection の中身を見ていく。

4.4 判断

if (条件)  文1
else 文2
という構造は、 条件が成り立っていれば文1を、そうでなければ文2を実行せよという意味にな る。文2がない(条件が成り立っている時はなにかするがそうでなければ何も しない)ときには

if (条件) 文1

だけでいい。 なお、上の例では if (条件) の後ろが{ 文 文 ... }とつながっている。このまとまりのことを複文といい、一般に文が書けるところには複文も書け る。このため、 if (...) { .... }else { .... } というような風にすれば条件によって違ういくつかの処理をまとめてできる。

文とは何かをちゃんと説明してなかったが、 C/C++では式にセミコロンをつけ たものが文である。で、式はなにかというと、 代入a=b+c といったものも 式、関数呼びだし bisection(xmin,xmax,eps) も式、単なる数式 a+b ももちろん式である。

C/C++言語の特徴として、実行されるものはすべて式であり、 (void であると いうことも含めて)値を持つということがある。代入式の値は代入された値そ れ自体なので、例えば a = b = c+d といったもので a と b の両方に 同じ値を代入できる。

条件は、数値同士の比較式(大小、等 しい)と、複数の比較式からできる論理式などが書ける。

具体的には、

a >  b                 aがbより大きければ真
a >= b                 aがbより小さくなければ真
a <  b                 aがbより小さければ真
a <= b                 aがbより大きくなければ真
a == b                 aがbと等しければ真
!条件                  条件が偽なら真(否定)
(条件1 ) && (条件2)    両方真なら真(論理積、and)
(条件1 ) || (条件2)    どちらかが真なら真(論理和、or)
というくらいがこれから出てくることがあるであろう。

ただし、実際に条件に書かれるものは値が整数になる式ならなんでもいい。さ らにおせっかいに、実数型の式でも勝手に整数に変換して評価してくれたりす る。こ のため、条件のところに a = b と書いても文法的には正しいが、大抵 の場合やってほしいこととはかなり違う意味を持つ。なお、処理系によってはこれに 警告を出してくれるものもある。

4.5 反復

while (条件){
    文
    ......
    文
}
これは標準の Fortran 77 には対応するものがないが、非常に便利なものであ る。なお、 Fortran の DO ループに対応するものは for 文であり、

for (変数 = 最初の値; 変数 < 最後の値 + 1; 変数 ++ ) 文

という形に書くのが普通である。

これは、まず変数に最初の値を入れて文を実行し、次に変数を1増やしてまた文を 実行し、以下同様に繰り返して変数が最後の値になったらおしまいにするとい うことになる。このような繰り返し処理をループ処理という。この場合は Fortran の DO ループとほぼ同じ動作になる。

C 以外の多くのプログラム言語では、例えば do i = 1, 10 (Fortran の場合)というように、「変数を1増やしては同じことを繰り返す」 という上に書いた通りのことをするための特別な書き方(構文)があるが、 C/C++ の for を使った構文はもっとフレキシブルなものである。

for(式1;条件式;式2) 式3; 式4; ... というふうに書いてあると、実際に 起きることは、

ということで、別に「変数に最初の値を入れて文を実行し、次に変数を 1増やしてまた文を実行し、」ということしかできないわけではない。例えば

int i;
for(i=0; i<262144; i *= 2){
   cout <<"i = " << i << endl;
}
と書けば、変数iの値を繰り返しごとに2倍にすることになる。

なお、ここで、i++; とか i *= 2; とかいうものが出 て来たが、これは C/C++ 言語に特有の書き方で、基本的には i++ は i = i + 1と 同じ意味だし、 i*= 2 は i = i * 2 と同じである。また、 i - という表現 も使える。

一般に、あらゆる演算(加減乗除の他に、論理演算なども)について、

i 演算記号=j
と書くのは
i  = i  演算記号 j
と書くのと同じと思っていい。



Jun Makino
平成14年10月20日