今まで紹介してきたようなサンプルソースを思い返してみよう。
例えば、成績を判定して表示するプログラム。
例えば、文字列を連結するプログラム。
これらは、非常に単純だった。
しかし、実際に世の中で使用されているものは、もっと複雑だ。
これを、一つの処理として書いてしまうととんでもないことになってしまう。
そこで、一定の処理を別の場所で定義しておきたい。
これを実現するのが、今回解説する関数だ。
この中では、今まで解説してきた全ての内容が使える。
また、できることの幅が一気に広がる内容でもあるので、使いこなせるようにしていきたい。
今回の説明内容
今回は、一定の処理を行う関数を解説する。
今までも関数自体は出てきている。
例えば、前回解説したようなC言語の文字列連結を行うstrcat
やstrncat
、Javaの文字列比較を行うequals
などなど。
もっと言うと、画面出力を行うSystem.out.println
、printf
、console.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行目で宣言したarray
が100番地を指しているとしよう。
これで、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;
}
}
今回までの内容をフルに使っている。
細かい解説はしないので、どんな処理を行っているか、チャレンジしてもらいたい。
コメント