17.関数を使おう
1.関数とは
関数はいつくかの処理をまとめたもののことです。例えばProcessingでは、size()、rect()、ellipse()、それからsetup()、draw()など…これらそのものが関数になります。
ellipse()やrect()などの関数はすでにProcessing内で定義されているので、そのまま使えますね。
Scratchのブロック機能はほぼ関数である
Scratchを経験している人は、ブロック機能を思い出してみてください。このブロックではオリジナルのブロックが作れますが、プログラミングの関数とほぼ同じ機能を持っています。Scratchを知らない人にとってもわかりやすい機能なので見てみてくださいね。
Scratchでブロックに名前を付けると、同じ名前の定義のブロックと命令のブロックができます。
定義のブロックには、このブロックが実行されたときのプログラムを書いてゆきます。
あいさつをするブロックの定義と、あいさつブロック
あいさつブロックを実際にプログラムに使うことで、実行できます。
Scratchのブロックに引数を追加する
このブロックは「こんにちは」しか言わないので、もう少し汎用的なブロックに作り変えたいと思います。
編集画面で「引数」(ひきすう)を追加して、この引数の言葉を言うようにしてみます。
引数とは、ブロックを実行するときにブロック内の処理に渡す数や文字の情報のことです。
プログラムでは、引数の「あいさつのことば」を、「~と2秒言う」ブロックに直接入れるようにします。
「こんにちは」以外の挨拶もできるようになりました!
お辞儀の時間も引数にしてみました。これで色々なシチュエーションに対応できますね!
Scratchの戻り値とは
関数には「戻り値」(返り値ともいいます。返り血を連想させるのでこの言葉は使わない人もいます。英語ではReturn Valueといいます)という機能もあります。
たとえば、演算カテゴリの「乱数」ブロックは、実行するたびに指定した範囲から適当な数を返してくれます。このブロックが返す情報を「戻り値」「返り値」といいます。
下のフキダシの中が戻り値です
(Scratchのブロック定義では戻り値の設定はできません)
関数を作ってみよう!
関数はプログラムが複雑になってきてコードを整理する時に便利です。みなさんは作品を作っていたらコードが長くなって、どこに何を書いたか分かりにくくなったということはないでしょうか?そういう時に関数を使えると整理がしやすくなります。
また、関数の作り方次第では、上で紹介したあいさつブロックのように使い回す事も出来るので、同じコードを何度も書く必要が無くなる事があります。
では、実際に日本の国旗を表示するプログラムを例にして関数を学んでいきましょう。まずは関数は気にせずコードを作ります。
void setup(){ size(600,400);}
void draw(){ noStroke(); fill(255); rect(0,0,600,400); fill(230,0,82); // ellipseの横幅と縦幅で(3.0/5)としているのは、 // 整数同士の割り算(3/5)では計算結果も整数で0になってしまうため ellipse(300,200,400*(3.0/5),400*(3.0/5));}// 日本国旗の赤丸の直系は、国旗の縦幅の3/5らしい
日本国旗がウィンドウいっぱいに表示されましたか?
このコードを元に関数を作っていきます。
2.関数の作り方(関数定義)
まず、先頭には戻り値の型(booleanとかintとか)を書きます。戻り値は一つしかないので、名前は必要ありません。
そして、引数は型と名前を書きます。引数は複数指定できますので、名前をつけます。この名前はこの関数内だけで使うので、仮引数と呼びます。
返り値の型 関数名(引数1, 引数2, ...){ 処理をここに書く return 返り値; // 返り値がない場合は省略可 }
この作り方に従って、先ほどの日本国旗を表示するコードを関数にしてまとめてみましょう。
void setup(){ size(600,400);}
void draw(){ japan(); // 関数の呼び出し}
// ここから下が関数/////////////////////////////////////////////void japan(){ noStroke(); fill(255); rect(0,0,600,400); fill(230,0,82); ellipse(300,200,400*(3.0/5),400*(3.0/5));}
関数draw()の下に関数japan()を追加します。この関数japan()には、先ほどのコードの日本国旗を表示する部分をそのまま移します。
そして、このjapan()をdraw()の中で呼び出しています。
では、関数の作り方とjapan()を照らし合わせながら説明します。
2-1.返り値
「返り値」というのは関数内で処理して関数の呼び出し元に返す値の事です。「戻り値」、「返却値」とも言います。関数に返り値がない時は、関数の頭は「void」(ヴォイド)とします。
void … 「何もない」という意味
japan()は日本国旗を表示するだけなので、voidになっています。今までsetup()やdraw()を書いてきたと思いますが、その頭につけていたvoidもこれです。 もしも、返り値がある場合には型の指定、それから、関数内で
return 返り値;
を忘れないようにしましょう。この「return」をすることで、関数が計算結果などを返すようになります。
例えば、1+1の計算結果を返してくれる関数sum1()を作るなら次のようにします。
int sum1(){ int sum = 1 + 1; return sum;}
返り値の型がvoidでない時にこのreturnを忘れると、「この関数は〇〇型の値を返すはずなのに何も返していない!」というようにProcessingに怒られます。
2-2.関数名
japan()では「japan」という関数名にしていますが、好きな名前を付けることができます。日本国旗を表示する関数であっても、関数の名前は他の国の名前や自分の名前にしても大丈夫です。
2-3.引数
「 引数 」、は「ひきすう」と読みます。これは関数が受け取る値の事です。例えば、size()ではウィンドウの横幅や高さを指定しましたよね?その数字のことです。 「2-1.返り値」で例として挙げた1+1を計算してくれる関数sum1を、2つの整数を指定してその和を返す関数にするなら、次のようになります。
int sum1(int n1, int n2){ int sum = n1 + n2; return sum;}
japan()は最初のコードをそのまま移しただけなので今は引数がないですが、後ほどjapan()にも引数をつけます。
2-4.関数の呼び出し
関数を作ったら、呼び出すのを忘れないようにしましょう。japan()はdraw()内で呼び出されています。
void draw(){ japan(); // これ!}
「コードがごちゃごちゃしてきたから関数を作ってまとめたけど、なぜか動かないぞ?」と思ったら、関数を作っただけで呼び出し忘れていた、という事がよくあります。
3.関数japan()を改良しよう
ここまでで作ったjapan()には引数がなく、日本国旗を決まった位置、大きさで表示するだけでした。では、これからjapan()に引数をつけて、表示する位置や大きさを変えられるようにしてみましょう。
まずjapan()に引数をつけます。
// xはx座標、yはy座標、wは国旗の横幅void japan(float x, float y, float w){ noStroke(); fill(255); rect(0,0,600,400); fill(230,0,82); ellipse(300,200,400*(3.0/5),400*(3.0/5));}
これによりjapan()を次のようにx座標、y座標、横幅を指定して呼び出すようになりました。
japan(100,200,200);
しかし、まだjapan()内の処理は書き換えていないので、x座標等を指定しても日本国旗の位置や大きさは変わりません。
では次に、受け取った引数を使って日本国旗を表示するようにjapan()内の処理を書き直しましょう。
void japan(float x, float y, float w){ float h = w * (2.0/3); // 縦幅hは横幅wの(2/3)倍 noStroke(); fill(255); rect(x,y,w,h); // 引数のx,y,wを使用 fill(230,0,82); // 引数x,y,wそれからhを使って国旗の中心を指定 // 丸の大きさは前述のとおり縦幅の(3/5)倍 ellipse(x+(w/2),y+(h/2),h*(3.0/5),h*(3.0/5));}
これで指定した位置、大きさで日本国旗が表示されるようになりました。
位置、大きさを配列などで管理すれば、同じ関数を使って複数の日本国旗を表示することも可能です。
int n = 5;float x[]; //これは float[] x; と同じです。配列宣言はこのような書き方でもOKです。float y[];float w[];
void setup(){ size(600,400); x = new float[n]; y = new float[n]; w = new float[n]; for(int i = 0; i < n; i++){ x[i] = random(width); y[i] = random(height); w[i] = random(10,200); }}
void draw(){ background(0); for(int i = 0; i < n; i++){ japan(x[i], y[i], w[i]); }}
void japan(float x, float y, float w){ float h = w * (2.0/3); noStroke(); fill(255); rect(x,y,w,h); fill(230,0,82); ellipse(x+(w/2),y+(h/2),h*(3.0/5),h*(3.0/5));}