モジュール化 (関数)

今回は、かなり強引ですが、モジュール化(関数)からクラスまで一気にやってみたいと思います。
かなり混乱も予想されるのですが、モジュール化とクラスは考え方としては同じものなので、一緒にやった方が理解しやすいと思います。

モジュール化 (関数)

まずはモジュールとは、
電気機器やコンピューター装置などの、構成要素の単位。独立の完成した機能をもち、交換や着脱が可能で、より大きなシステムに構成される。
(大辞泉より引用)

とあります。簡単に言ってしまえば「部品化」ということです。自動車の部品がそれぞれが独立していて異なる車種にも使われるように、プログラムも部品化してやれば他のプログラムに再利用することが可能になります。
また、このモジュール化の最大の利点は「プログラムが見やすくなる」ということです。
そして、その部品化されたものを関数(function)と呼びます。
なぜ日本語の名前として「関数」と付けられたのかはよく分からないのですが、英語名のfunction(機能)から、その性質はよく分かりますよね。
以下がその概念図です。

func_diagram.png

メインのプログラム内で関数(function)がその都度実行されています。
ここで気を付けなければいけないのは、この考え方が適用できるのは、C言語やBasicなどの手続き型言語になります。手続き型言語は、プログラムが上から下に順番に実行されていく形のものです。JavaやC++などのオブジェクト指向言語はまた違った概念になります(下の方で解説してます)。

また、式は以下の形になります。

型 関数名( 型 引数,  型 引数, 型 引数 ・・・)

ここで、引数という関数に値を渡す機能が出てきます(例題として出てます)が、この引数はいくつでも作ることができます。
と、概念的なことだけ説明されても分からないでしょうから、ここで例題をやってみましょう。以下は、直径最大5ピクセルの円を画面上に描くプログラムです。

screen11.PNG

(以上のようなグラフィックスが生成される)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void setup()
{
  size(400, 400);
  background(255);
  noStroke();
  smooth();
  ellipseMode(CENTER);    //中心に描画
}
 
void draw() 
{
  //ここは画面の描画だけをしている
}
 
void mouseDragged() 
{  
  //pmouseとは、現在のマウスの位置の一つ前のマウスの位置を指します。
  //よって、現在の位置から前の位置を引くことによってマウスの移動の早さを検出することができます
 
  float speed = abs(mouseX-pmouseX) + abs(mouseY-pmouseY);  //マウスの早さを検出
  fill(255-speed*10);  // マウスの早さを線の色にする
 
  //speedの値を制限することによって、円の大きさを最大5までに制限
  if(speed > 10) speed = 10;
  ellipse(mouseX, pmouseY, speed, speed);  //マウスの位置で円を描画
  //println(speed);
}

これをモジュール化するとどうなるでしょうか。ちょっと強引ですが、以下のようにまとめてみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void setup()
{
  size(400, 400);
  background(255);
  noStroke();
  smooth();
  ellipseMode(CENTER);    //中心に描画
}
 
void draw() 
{
  //ここは画面の描画だけをしている
}
 
void mouseDragged() 
{  
  //pmouseとは、現在のマウスの位置の一つ前のマウスの位置を指します。
  //よって、現在の位置から前の位置を引くことによってマウスの移動の早さを検出することができます
 
  variableEllipse(mouseX, mouseY, pmouseX, pmouseY);  //様々な大きさ、色の円を描く関数
}
 
/////////円の描画関数////////////////////
void variableEllipse(int x, int y, int px, int py) 
{
  float speed = abs(x-px) + abs(y-py);  //マウスの早さを検出
  fill(255-speed*10);  // マウスの早さを線の色にする
 
  //speedの値を制限することによって、円の大きさを最大5までに制限
  if(speed > 10) speed = 10;
  ellipse(x, y, speed, speed);  //マウスの位置で円を描画
  //println(speed);
}

このように、円の描画の部分だけ抜き出して関数にしています。
実際のところ、この程度のコードなら、わざわざ関数にする必要もないのですが、関数の利点は、コピペで他のプログラムに簡単に移植できるというところですね。

また、関数は値を返すこともできます。以下はBoolean型の値を返す関数が使われている例です。
object_pressed_circle

練習問題

それでは、練習問題をやってみましょう。bounceX()とbounceY()という、折り返しをする機能を持った関数を作ってみて下さい。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int eSize = 5;  //オブジェクトのサイズ
int xSpeed = 5;  //オブジェクトのスピード
int ySpeed = 3;  //オブジェクトのスピード
int x = 0, y = 0;  //オブジェクトのx座標
 
//初期化
void setup() {
  size(400, 400);
  noStroke();      //輪郭を描かない
  frameRate(30);  //再生速度を1秒30フレームに設定(初期値は1秒60フレーム)
  smooth();        //オブジェクトにアンチエイリアスをかける
}
 
//プログラムが終了するまで、draw()の中は繰り返される
void draw() {
  background(255);  //背景は白
  x = x + xSpeed;    //xの値にxSpeedを足す
  y = y + ySpeed;    //xの値にxSpeedを足す
 
  //もし、xの値が画面の右端より大きくなるか、左端より小さくなった場合、
  if(x > width || x <0) {
    xSpeed = -xSpeed;    //xSpeedの正負を入れ替える
  }
  if(y > height || y <0) {
    ySpeed = -ySpeed;    //xSpeedの正負を入れ替える
  }
  fill(0);  //黒で描く
  ellipse(x, y, eSize, eSize);    //円を描く
}

解答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
int eSize = 5;  //オブジェクトのサイズ
int xSpeed = 5;  //オブジェクトのスピード
int ySpeed = 3;  //オブジェクトのスピード
int x = 0, y = 0;  //オブジェクトのx座標
 
//初期化
void setup() {
  size(400, 400);
  noStroke();      //輪郭を描かない
  frameRate(30);  //再生速度を1秒30フレームに設定(初期値は1秒60フレーム)
  smooth();        //オブジェクトにアンチエイリアスをかける
}
 
//プログラムが終了するまで、draw()の中は繰り返される
void draw() {
  background(255);  //背景は白
  x = x + xSpeed;    //xの値にxSpeedを足す
  y = y + ySpeed;    //xの値にxSpeedを足す
 
 xSpeed = bounceX(x, xSpeed);
 ySpeed = bounceY(y, ySpeed);
 /* 
  //もし、xの値が画面の右端より大きくなるか、左端より小さくなった場合、
  if(x > width || x <0) {
    xSpeed = -xSpeed;    //xSpeedの正負を入れ替える
  }
  if(y > height || y <0) {
    ySpeed = -ySpeed;    //xSpeedの正負を入れ替える
  }
*/
  fill(0);  //黒で描く
  ellipse(x, y, eSize, eSize);    //円を描く
}
 
int bounceX(int bx,  int xSp) {
  if(bx > width || bx <0) {
    xSp = -xSp;    //xSpeedの正負を入れ替える
  }
  return xSp;
}
 
int bounceY(int by, int ySp) {
  if(by > height || by <0) {
    ySp = -ySp;    //xSpeedの正負を入れ替える
  }
  return ySp;
}

この関数は非常に強引に作ってしまったので、通常だとこのような関数は書かないでしょうが、勉強ということで。