関数

2019-06-18

本章のあらすじ

  • 処理のかたまり「関数」
  • 便利な関数を作る
  • 関数で他人と協業する

似たような機能、繰り返すコピペ

ここまでたくさんスケッチを書いてきましたが、とある機能がいろいろな場所で必要になって、コピペを何度も使った経験はないでしょうか。

コピペを多用する例

具体例に触れながら進めていきましょう。ここに、図形を並べて描くスケッチがあります。リンク先からダウンロードして実行してみてください。 (→shapes_template

これらの図形に、「色彩」の回で作ったオリジナルの配色を適用してみてください。

かぼちゃパレットを適用した例

以前作ったものをなくしてしまった場合は、復習がてら新たに作るか、あるいはこんな感じの配色はいかがでしょうか。

// 必ず赤っぽくなる配色

// 赤は 0 ~ 255 の範囲でランダム
float r = random(255);
// 緑は赤を超えない範囲でランダム
float g = random(r);
// 青は緑を超えない範囲でランダム
float b = random(g);

fill(r, g, b);

図形の色を変えるには、その図形を描く前に fill() を実行して塗り色を決める必要がありますね。このスケッチでは図形が8個あるので、それぞれの手前に同じような fill() を書いていきます。

// 塗り色を設定
float r = random(255);
float g = random(r);
float b = random(g);
fill(r, g, b);
// 上段左の図形
ellipse(
  width / 4,
  height / 4,
  100, 100
);

// 塗り色を設定
r = random(255);
g = random(r);
b = random(g);
fill(r, g, b);
// 上段中央の図形
rect(
  width / 2,
  height / 4,
  100, 100
);

コピペと微修正を繰り返すことになります。ちょっと面倒ですね…。他のパレットに差し替えてみようと思い立ったら、また図形の数ぶん修正するわけです。やってられませんね。

この問題を解決してくれるのが「関数」です。

関数

先の色と図形のスケッチを会社の仕事に例えると、「お抱えのデザイナーに都度お願いして、色を設定してもらう」感じ。

融通は効きますが、都度の指示(コピペ・修正)が面倒で非効率的です。

「関数」は処理のかたまりです。先の例を続けるなら、いい感じに色を設定してくれる「スゴ塗りプラン」を用意している会社のようなもの。

この会社さえあれば、お抱えのデザイナーに都度お願いする手間なく、このプランを利用すればよくなります。非効率な業務フローが解消しますね!

関数の宣言

そろそろ実際のコードを見ていきましょう。

関数は、名前とどんな処理をするかのブロックを持っています。

void 関数名 () {
  
}

具体的なコードはこんな感じ。

// いい感じに塗り色を設定するスゴ塗り関数
void fillNicely() {
  // 塗り色を設定
  float r = random(255);
  float g = random(r);
  float b = random(g);
  fill(r, g, b);
}

関数の実行

これで「いい感じに塗り色を設定する」という処理のかたまりができますが、作っただけでは実行されず、明示的に実行してあげる必要があります。

関数を実行するには、関数名に丸括弧 () をつけて 関数名() と書きます。

// スゴ塗り関数を実行
fillNicely();

簡単なコードで処理の流れをみてみましょう。

// スゴ塗り関数
void fillNicely() {
  // 塗り色を設定
  float r = random(255);
  float g = random(r);
  float b = random(g);
  fill(r, g, b);
}

void draw() {
  backgroud(0);

  // スゴ塗り関数を実行
  fillNicely();
  ellipse(width / 2, height / 2, 100, 100);
}

関数の動きを理解できたら、はじめのスケッチを書き直して、効率的に図形の塗り色を決められるように改善してみましょう。 (→shapes_function

// 塗り色を設定
fillNicely();
// 上段左の図形
ellipse(
  width / 4,
  height / 4,
  100, 100
);

// 塗り色を設定
fillNicely();
// 上段中央の図形
rect(
  width / 2,
  height / 4,
  100, 100
);

よく使う機能を関数として切り出すことで、コードを簡潔に書くことができるようになりましたね。

パラメーターを渡す:引数

スゴ塗りプランの導入によって業務フローはだいぶ簡潔になりましたが、このプラン1本だけではユーザーの要望に応えきれません。明るめの色がほしい人もいるし、暗めの色がほしい人もいる。プランにも幅を持たせたくなってきます。

選べるオプションでプランに幅を持たせる

関数の処理を変えるためのオプションが「引数」です。

これまで、関数の丸括弧 () は空っぽでおまじないのように付属していましたが、いよいよ出番です。この括弧の中にオプションを書いていきます。複数ある場合は、カンマ , で並べます。

void 関数名 ( 型1 オプション名1 , 型2 オプション名2 , ...) {
}

オプションの描きかたは変数宣言とよく似ていますね。型に続けてオプションの名前を書きます。コードをもう少し具体的にするとこんな感じ。

// いい感じに塗り色を設定する関数(明るさ指定オプションつき)
// 設定したい色の明るさを引数 br として指定できる
void fillNicely(float br) {
  
}

引数は、関数のブロック内を有効範囲として、普通の変数と同じように使えます。数値と計算したり、if文で判定したり…自由自在です。

簡単な例で処理の流れをみてみましょう。

// 明るさ指定オプションつきのスゴ塗り関数
// 設定したい色の明るさを引数 br (0 ~ 255) として指定できる
void fillNicely(float br) {
  fill(random(255), random(255), br);
}

void draw() {
  backgroud(0);

  // スゴ塗り関数を引数つきで実行
  fillNicely(200);
  ellipse(width / 2, height / 2, 200, 200);

  // スゴ塗り関数を引数つきで実行
  fillNicely(100);
  ellipse(width / 2, height / 2, 100, 100);
}

練習:引数を使ってみる

先の図形を描くスケッチに引数つきの関数を組み込んで、色合いや明るさなどの色のオプションを指定できるようにしてみましょう。 (→shapes_function_args

引数を使って、右下の図形ほど暗い色を生成するようにしてみた例

練習:線を描く関数を作ってみる

マウスをドラッグして線を引くスケッチがあります。 (→my_line_template

このスケッチには、始点のXY座標・終点のXY座標を引数として受け取り、自作の線を描く関数が組み込まれています。

void drawMyLine(int x1, int y1, int x2, int y2) {
  noStroke();
  fill(0);

  // 始点と終点の区間を100分割するループを回す
  for (int i = 0; i <= 100; i++) {
    float x = map(i, 0, 100, x1, x2);
    float y = map(i, 0, 100, y1, y2);

    // 小さな円を描く
    ellipse(x, y, 2, 2);
  }
}

この関数を改造して、おもしろい線が描けないか試行錯誤してみましょう。

円をだんだん濃く大きくした例
サイズと位置をランダムに散らした例

何かできたら、これまで作ってきたスケッチの中で line() を使っているものにこの関数を移植して、差し替えてみましょう。

「始点と終点で線を引く」という機能を関数として切り出したので、同じ機能を持つものとの差し替えが容易になりました。このような汎用的な関数を作りためておくと、表現の幅を広げる資産が増えていくのです。コツコツ貯めていけばいつか最強になれるかもしれません。

処理の結果を戻す:戻り値

いままでの関数は、処理をお願いしたら任せきりのスタイルでした。それだけでもじゅうぶん便利ですが、成果物を作って届けるタイプの仕事があってもいいですよね。例えば、いい感じの色の絵の具を調合して届けてくれる「スゴ色調合プラン」なんてどうでしょうか。

調合した絵の具を成果物としてもらえれば、これまでの例のとおり塗りに使ってもよいですし、背景色に使ったり枠線の色に使ったりと、もっと自由度が増しそうです。

そんなときのための機能が関数にはちゃんと用意されていまして、実行結果を関数の実行元に戻せるのが「戻り値」です。関数宣言の先頭で戻り値の型を書き、具体的な内容は関数の実行を終える際に return 文にて渡します。

戻り値の型 関数名 ( 引数 ) {
  

  return 戻り値 ;
}

スゴ色調合プランの具体的なコードを見てみましょう。

// いい感じの色を調合して、 color 型の戻り値で返すスゴ色調合関数
color createNiceColor() {
  // 色を生成
  float r = random(255);
  float g = random(r);
  float b = random(g);
  color col = color(r, g, b);

  // 生成した色を return で戻す
  return col;
}

void draw() {
  backgroud(0);

  // スゴ色調合関数を実行して色を取得
  color col1 = createNiceColor();
  // 戻ってきた結果を塗り色に設定
  fill(col1);
  ellipse(width / 2, height / 2, 200, 200);

  // スゴ色調合関数を実行して色を取得
  color col2 = createNiceColor();
  // 戻ってきた結果を枠線の色に設定
  stroke(col2);
  ellipse(width / 2, height / 2, 100, 100);
}

しれっと新しい color() が出てきましたが、これは数値を指定して color 型の色を作れるものです。

練習:戻り値を使ってみる

先の図形を描くスケッチに戻り値で色を返す関数を組み込んで、生成した色を戻せるようにしてみましょう。 (→shapes_function_retval

生成した色を塗りや線に適用してみた例

生成した色が戻り値として返ってくるので、 fill() で塗りにもできますし、 stroke() で線の色にも適用できていますね。

戻り値なし: void

戻り値のない関数では、おまじないのように void というキーワードを使ってきました。

void fillNicely() {
  // 関数中で fill() を実行するので、戻り値は不要
  
}

void は「空っぽ」、つまり戻り値なしという意味だったのです。

実は関数だったものたち:組み込み関数

ところで、関数の書きかたって…どこかで見たことないでしょうか?

// 自作の関数の宣言
void fillNicely() {
  
}

// クリックしたときに実行されるブロック
void mousePressed() {
  
}
// 自作の関数を実行
drawMyLine(pmouseX, pmouseY, mouseX, mouseY);

// 線を描く
line(pmouseX, pmouseY, mouseX, mouseY);

fill(), random(), rect() , mousePressed(), …これまでおまじないのように使ってきた機能のうち () のついているものたち、実はすべて関数だったのです。塗りを設定したり、図形を描いたり…特定の目的に沿った機能を簡単に使えるよう、Processingが関数としてまとめてくれています。

プログラミング言語(この講義ではProcessing)が標準で用意してくれている関数たちを「組み込み関数」と呼びます。

まとめ:関数のいいところ

最後に、関数のいいところをまとめておきます。

1. 名前がついて見通しがよくなる

関数には名前が必須です。関数によって処理のかたまりに名前がつくので、コードから意図が読み取りやすくなります。

// 関数なし:

// (どんな塗り色を設定してるんだっけ…?)
float r = random(255);
float g = random(r);
float b = random(g);
fill(r, g, b);

ellipse(width / 4, height / 4, 100, 100);
// 関数あり:

// (関数名からして、赤っぽい色を塗り色にしてるんだろうな)
setReddishFillColor();

ellipse(width / 4, height / 4, 100, 100);

また、長々と書いていた処理がまとまるので、見通しもよくなりますね。

// 関数なし:長くて見通しがよくない

r = random(255);
g = random(r);
b = random(g);
fill(r, g, b);
ellipse(width / 4, height / 4, 200, 200);

r = random(255);
g = random(r);
b = random(g);
fill(r, g, b);
ellipse(width / 4, height / 4, 200, 200);
// 関数あり:短く簡潔で見通しがよい

setReddishFillColor();
ellipse(width / 4, height / 4, 200, 200);

setReddishFillColor();
ellipse(width / 4, height / 4, 200, 200);

2. 他に転用しやすくなる

汎用的な機能をうまく関数として切り出せれば、他のスケッチにも転用できます。

例えば「いい感じの配色を作ってくれる機能」「いい感じの線を描いてくれる機能」みたいなものは、幅広い種類のスケッチに活用できそうですね。

練習:汎用的な関数を利用してみよう

縦横にランダムなサイズの円を描くスケッチがあります。 (→circles

さまざまな表情の円を描く関数を用意して、円の描画処理を差し替えてみました。 (→circles_gradient) (→circles_dots) (→circles_curve

皆さんがこれまで作ってきたスケッチの中にもきっと、 ellipse() を使ったものがありますよね。上記の中から好きな関数を選び、そのスケッチに移植し、 ellipse() を差し替えてみてください。きっとがらっと表情が変わるはずです。

課題:汎用的な関数を作ってみよう

先の線や円の例のように、汎用的な関数を作っておくことで、スケッチの描き込みを細かくするなど複雑な表情が手軽に出せるようになります。汎用的な関数を自分でも作ってみましょう。

  • おもしろい塗り色を設定してくれる関数
  • 変な線を描く関数
  • 変な四角を描く関数

本章のまとめ

  • 関数は処理のかたまり
  • 似たような処理をコピペしていた箇所を共通化できる
  • 関数を効果的に使えれば、コードの見やすさやメンテのしやすさが向上する