プログラミング考え方講座「関数」

プログラミング考え方講座

今まで紹介してきたようなサンプルソースを思い返してみよう。

例えば、成績を判定して表示するプログラム

例えば、文字列を連結するプログラム

これらは、非常に単純だった。

しかし、実際に世の中で使用されているものは、もっと複雑だ。

これを、一つの処理として書いてしまうととんでもないことになってしまう

そこで、一定の処理を別の場所で定義しておきたい

これを実現するのが、今回解説する関数だ。

この中では、今まで解説してきた全ての内容が使える

また、できることの幅が一気に広がる内容でもあるので、使いこなせるようにしていきたい。

スポンサーリンク

今回の説明内容

今回は、一定の処理を行う関数を解説する。

今までも関数自体は出てきている

例えば、前回解説したようなC言語の文字列連結を行うstrcatstrncat、Javaの文字列比較を行うequalsなどなど。

もっと言うと、画面出力を行うSystem.out.printlnprintfconsole.logもそうだ。

このような処理を、今度は自分で定義してみよう、というお話だ。

関数

関数とは

関数とは、すでに何度か説明してしまっているが、一定の処理を記述し、呼び出すだけでそれを行ってくれる塊のことだ。

先に、用語だけ説明してしまおう。

関数を構成する要素は、基本的に以下のものがある。

  • 関数名:処理の名前
  • 引数:処理のために受け取る情報
  • 戻り値:処理が終わった後に、呼び出し元に返す情報

これらと、実際に行う処理内容で関数が構成されている。

では、具体的な言語で実装例を見ていこう。

行う処理としては、ある数字を受け取って、1からその数までの和を求めるとしよう。

Javaの使用例

先にソースコードを載せてしまう。以下がJavaのソースコードだ。

class Sample12 {
    public static void main(String args[]) {
        int sum = sumAllFromOne(10);
        System.out.println("結果:" + sum);
    }

    static int sumAllFromOne(int num){
        int res = 0;
        for(int i = 1; i <= num; i++){
            res += i;
        }
        return res;
    }
}

3行目で自作の関数を呼び出し7行目から13行目でその内容を記述している。

では、まずは関数の定義部分から見ていこう。

7行目、ここではstatic int sumAllFromOne(int num){と書かれている。

先頭から順番に見ていこう。

先頭のstaticというやつ、これは…具体的に何かというものは、今は説明できない

詳しくは次回のオブジェクトで説明するが、いったんstaticというやつがついているものから関数を呼び出す時は、staticをつけなければいけないと思っておいて欲しい。

実際、2行目の2つ目にstaticというものがついている。だから、呼び出し先にもつけている。

次に、二つ目のintというやつ。これは以前に説明しただ。

関数に型を設定しているのだが…これは、元の場所に戻す情報の型を設定している。

つまり、この関数を呼び出すと、元の場所にはint型の数字が得られるということだ。

ここにはどんな型も書けるし、何なら元の場所に何も返さないこともできる

ただし、何も返さない時はvoidと書く決まりだ。これは覚えておこう。

三つ目のsumAllFromOneは関数名だ。呼び出す時はこの名前を指定してあげる

ちなみに、ここでは一目でその関数が何をしているか分かるような名前をつけておく習慣がある。

今回で言えば、1からの合計を求めるので、そのまま英語っぽくしてみた。

その次の小括弧は、引数呼び出し元から受け取る情報だ。

今回はint型の情報1個をnumという変数に入れていることになる。

これは、0個でもいいし、2個以上でももちろん問題ない。

ただ、0個の場合でも小括弧は必要になる。忘れないようにしよう。

ここまできて、ようやく8行目から処理開始だ。

この中には、今まで解説してきた変数宣言条件分岐繰り返し処理など何でも書ける。

更に言えば、そこから別の関数を呼び出すことももちろん可能だ。

最後に、12行目。return res;という部分だ。

まず、returnというのは、戻るという意味通り、関数の処理を終えて元の処理に戻ることを表す。

そのため、一度でも関数内でreturnが実行されると、その関数の処理を終える

後ろにくっついているresが、戻り値になる。

今回、先にint型の戻り値と決めているので、ここでint型の内容を返している

こうすることで、呼び出し元でその数を使用することができるのだ。

ちなみに、戻り値がない(先頭でvoidと指定している)場合は、returnだけ書けば問題ない

条件分岐等せず、必ず最後まで通す場合は、return自体無くても大丈夫だ。

というわけで、見直してみよう。この関数は…

  • sumAllFromOneという関数名で定義されている
  • 引数として呼び出し元からint型のデータを一つ受け取り、関数内ではnumとして使用する
  • 1からnumまでの数の和を、int型の戻り値として呼び出し元に戻す

ということになる。

では、その呼び出し元を見よう。サンプルの3行目だ。

ここでは、sumAllFromOne(10)を変数sumに代入するような形で書かれている。

やっていることとして、まずはsumAllFromOne関数を、10という情報を渡して実行し、戻ってきた値を変数sumに代入している。

つまり、最終的にsumには1から10までの合計、55が入るということになる。

なお、複数の引数を渡す場合はコンマで区切り一つも渡さない場合は小括弧だけ書く

これが、関数の仕組みと使い方だ。

一つ補足だが、今までさらっと書いていたpublic static void main(String args[]){も関数だ。

これは特別なもので、プログラム実行時に呼び出されるメイン関数というもの。

先頭二つがまだ説明できないが、その後ろを見ると意味が分かると思う。

まずvoidとあるので、戻り値は持たない。そして、関数名はmainだ。

引数として、文字列の配列argsという変数に入れている。

この引数はコマンドライン引数といって、実行時にプログラムの外から受け取ることのできる情報になる。

プログラム実行時のコマンドの後ろに、半角スペースで挟んで文字列を入れると、それをプログラム内で使用することができる。

今回のオマケで、これも少し使ってみよう。

C言語の使用例

C言語はJavaと比べて、以下の内容が異なっている。

  • staticは不要
  • 呼び出し元より、定義を上に書かなければならない

その他はJavaと同じだ。というわけで、同じ処理をCで書いたものがこちら。

#include<stdio.h>

int sumAllFromOne(int num){
    int res = 0;
    for(int i = 1; i <= num; i++){
        res += i;
    }
    return res;
}

int main(){
    int sum = sumAllFromOne(10);
    printf("合計:%d\n", sum);
    return 0;
}

上の違い以外は、全てJavaと同じになる。特に追加で説明することもないだろう。

JavaScriptの使用例

ちょっとお久しぶりのJavaScriptだ。

さて、JavaScriptでは型の宣言が不要だったのを覚えているだろうか。

それはこの関数でも同じで、型を書く代わりにfunctionという決まった文言を書く。

その他、引数にも型がいらず、そのまま変数名を書くだけになる。

戻り値については、いきなりreturn ○○;と書けるのだ。

var sum = sumAllFromOne(10);
console.log("合計:" + sum);

function sumAllFromOne(num){
    var res = 0;
    for(var i = 1; i <= num; i++){
        res += i;
    }
    return res;
}

その他は、JavaやC言語と同じだ。

関数のちょっと応用

さて、ここで一つ気になることがある。

前回まで解説していた、ポインタだ。

引数としてこのポインタを設定した場合、中でポインタ先の内容を書き換えるとどうなるか

例えば、Javaで言うと以下のような感じだ。

class Sample13 {
    public static void main(String args[]) {
        int array[] = {1, 2, 3};
        test(array);
        for(int i = 0; i < array.length; i++){
            System.out.println((i + 1) + "番目:" + array[i]);
        }
    }

    static void test(int numArray[]){
        numArray[0] = 10;
    }
}

ちょっと別で解説するものもあるが、先にこの答えを考えてみて欲しい。

どうだろうか。

正解は、書き換わる。これを表示すると、10, 2, 3という結果が表示されるのだ。

なぜそんなことになるのかだが、まず配列の変数に入っているのは、先頭へのポインタだった。

つまり、関数へはそのポインタを渡していることになる。

例えば、今回の3行目で宣言したarray100番地を指しているとしよう。

これで、arrayを関数に渡すと、その100番地という情報が渡される

関数内でその中身を書き換えると、当然だが100番地の内容が書き換わる

これで処理が終わって元に戻り、その配列を表示すると…書き換えた個所と同じものを指しているので、結局書き換えられた後の内容が出てくるのだ。

これを応用すると、結果格納用の配列をあえて渡しておき、関数内でその配列に戻したいデータを入れておくことで、元の処理に戻ってからそれを使う、なんてこともできる。

前回、C言語で解説したstrcatなんかはその具体例だ。

二つの文字列を渡して、一つ目に渡した文字列の後ろに、二つ目の内容がくっついていたと思う。

C言語の文字列はchar型の配列だった。つまり、ポインタを渡しているからこそできることなのだ。

この内容は、解説に使ったJavaはもちろん、C言語、JavaScriptでも共通なので覚えておこう。

知らないと不具合の種だが、知っていれば強い武器だ。

さて、ここまできてJava特化の補足を一つ。

上のサンプルの中でarray.lengthという記述がある。

このように配列の変数名.lengthと書くと、そこがその配列の要素数に置き換わる

このように、配列の要素数を調べる方法もどの言語にも大体ある。

よくfor文と組み合わせて使ったり想定された配列の要素数かどうかを確認するときに使ったりするので覚えておくといい。

まとめ:関数

今回は、関数というものをご紹介した。

関数は、一連の処理をまとめておき、別の場所から呼び出せるようにしたものだ。

引数として呼び出し元から関数へ情報を渡したり、逆に戻り値として関数の結果を呼び出し元に返したりできる。

また、関数の内部ではこれまで解説したようなものはほとんど使える

そして、後半ではちょっとした応用例ということで、ポインタを渡した場合の動作について少し解説した。

ポインタを渡して関数内でそのポインタ先の情報を書き換えると、呼び出し元にその内容を反映させることができる。

どうなっているか、しっかり把握しておこう。

さて、次回はオブジェクトだ。

ここまで来れば、かなりのことができるようになる。

同時に、オブジェクトはかなり奥が深いし、言語ごとの仕様も変化が大きくなってくる

考え方講座としてはこのオブジェクトの基本あたりまでを想定しているので、あと少しだ。

更新情報はTwitterでも呟いている。よかったら、ページ下部のTwitterアイコンから覗いていってほしい。

それでは。

オマケ:Javaのコマンドライン引数

最後にオマケだ。ちょっと名前を出したコマンドライン引数を見ていく。

そもそも、Javaを実行するときは、java 〇〇というコマンドで実行している。

eclipseなどの統合開発環境を使用している場合は、実行ボタンをポチっと押すだけだ。

で、この実行時に、プログラムの外から情報を渡せるのが、このコマンドライン引数というものだ。

コマンド実行の場合には、java ○○の後ろに、半角スペース区切りで文字列を書いていく

統合開発環境の場合には実行の設定からこれを付け加えることができる。

使用しているツールにもよるので、「ツール名 コマンドライン引数」などで調べて欲しい。

コマンドで実行することを想定して、実際に使ってみよう。

まずはコマンドライン引数の内容をただ単に表示してみる。

class Sample14 {
    public static void main(String args[]) {
        for(int i = 0; i < args.length; i++){
            System.out.println(args[i]);
        }
    }
}

コンパイル時はそのままコンパイルすればいい。

で、実行するときに、例えば以下のようなコマンドで実行してみよう。

java Sample14 abc def ghi

こうすると、abc, def, ghiが改行されつつ表示されると思う。

このように、スペース区切り1個ずつmainという関数の引数に文字列が入ってくれる

これを使うと、通常のコマンドのようにオプションを設定したりできる。

ただ、注意点もある。

実行時は、いくつの引数が渡されるか分からないので、その場合の条件分岐をする必要がある。

流れとしては大雑把に以下のような感じだろうか。

  • コマンドライン引数の数をカウントし、不正なら使い方を表示して終了
  • コマンドライン引数の中身をチェックし、不正なら使い方を表示して終了
  • コマンドライン引数の中身まで問題なければ、それに応じた処理を実行

この流れに従って、上の関数の例のように1からある数字までの合計を求めるプログラムで、-testとつけたら何を渡したか表示するようにしてみよう。

class Sample15 {
    public static void main(String args[]) {
        if(args.length > 2){
            System.out.println("Usage: java Sample15 [-test]");
            System.out.println("If you write -test, this program shows the number which use for sum.");
            return;
        }
        boolean showFlugs = false;
        if(args.length == 0){
            showFlugs = false;
        }else if(args.length == 1 && args[0].equals("-test")){
            showFlugs = true;
        }else{
            System.out.println("Usage: java Sample15 [-test]");
            System.out.println("If you write -test, this program shows the number which use for sum.");
            return;
        }

        int num = 10;
        int sum = sumAllFromOne(num);

        if(showFlugs){
            System.out.println("Num : " + num);
        }
        System.out.println("Sum : " + sum);
    }

    static int sumAllFromOne(int num){
        int res = 0;
        for(int i = 1; i <= num; i++){
            res += i;
        }
        return res;
    }
}

今回までの内容をフルに使っている

細かい解説はしないので、どんな処理を行っているか、チャレンジしてもらいたい。

コメント

タイトルとURLをコピーしました