関数

処理を1つにまとめる関数について説明します。

関数は何らかの処理を1つにまとめたものです。これまで使ったline()やrect()なども関数の1つです。プログラムはline()を処理するときにlineという名前の関数に記述されている何らかの処理を実行しています。

line()を実行して線を描画するにもいろいろな処理を実行していることが想像できます。もし、それらの処理を自身でコードを記述し線を描画することを想像したら、複数の線を描画するときにはかなり煩わしくなることが想像できます。

関数はline()やrect()などのように実行した後、呼び出し元に値を返さないものもあれば、random()のように呼び出した元に値を返すものがあります。

また、rect(100,100,50,50)のように引数として値を渡して実行するものもあります。

関数は値を渡したり(引数)、受け取ったり(戻り値)を自由にできます。

関数は自身で作成することができます。関数を作成することでより効率的にコードを書くことができます。また、関数は部品化することとなるので、他のコードで再利用することが容易にできるようになります。

引数も戻り値もない関数を定義するには以下のように記述します。

void 関数名() {
    関数の処理
}

float型のデータを2つ引数として渡し、int型のデータを戻り値として返す関数は以下のように記述します。

int 関数名(float x, float y) {
    関数の処理
    return int型のデータ
}

関数はその関数名が呼ばれたら、{〜}の範囲内に書かれた処理を実行します。関数もこのブロックが存在します。

ブロック内の処理が終わると呼び出し元のコードに戻ります。

関数名は自由につけることができます。しかし、1連のプログラムの中で同じ名前と引数の関数は2つ以上作れません。

そして、システムですでに使われている名前や、変数名と同じ名前は避けることをおすすめします。

関数名の左には戻り値として返すデータの型を指定します。引数は、複数個定義する場合はカンマ(,)区切りで記述します。

関数作成

関数を作成し効率よくなることを体感したいと思います。

size(500,500);
background(255);
noFill();
strokeWeight(10);
ellipse(250,250,100,100);
strokeWeight(5);
ellipse(250,250,65,65);
strokeWeight(12);
ellipse(250,250,30,30);

このコードはellipse()で3つの円を描いて画面中央に的を描画しています。

この的を複数画面の中で描画したい場合は以下のコードで可能です。

size(500,500);
background(255);
noFill();
strokeWeight(10);
ellipse(100,100,100,100);
strokeWeight(5);
ellipse(100,100,65,65);
strokeWeight(12);
ellipse(100,100,30,30);
strokeWeight(10);
ellipse(350,250,100,100);
strokeWeight(5);
ellipse(350,250,65,65);
strokeWeight(12);
ellipse(350,250,30,30);
strokeWeight(10);
ellipse(200,300,100,100);
strokeWeight(5);
ellipse(200,300,65,65);
strokeWeight(12);
ellipse(200,300,30,30);

このコードは画面に3つの的を描画しています。

しかし、3つの的を描画するだけでこのコード量となります。どこのコードがどの的なのかちょっとわかりにくいです。

このまま10個の的を画面に描画しようとすると、このコードの3倍以上となります。

関数を定義し、すっきりさせたいと思います。

void setup() {
  size(500,500);
  background(255);
  drawMato(100,100);
  drawMato(350,250);
  drawMato(200,300);
}
void drawMato(float x,float y) {
  noFill();
  strokeWeight(10);
  ellipse(x,y,100,100);
  strokeWeight(5);
  ellipse(x,y,65,65);
  strokeWeight(12);
  ellipse(x,y,30,30);
}

このコードは先程と同じく画面に3つの的を描画します。

Processingは関数を定義し使用する場合、必ずsetup()関数を定義する必要があります。

setup()関数はシステムが自動的に呼び出す関数です。プログラム実行時に最初に1度だけシステムが呼び出します。

ここではプログラム実行時にsetup()が呼ばれて、size()により画面の大きさを指定し、background()で背景を白くし、自身の定義したdrawMato()を3回実行しています。

これにより的を描画するコードは部品化されるので、好きな時に好きな所で的を描画することができます。

再帰的呼び出し

定義した関数の中でいろいろな関数を呼び出して処理を実行していますが、関数の中でその関数を呼び出すこともできます。

これを再帰的呼び出しといいます。この再帰を使って表現を豊かにすることもできます。

void setup() {
  size(500,500);
  background(255);
  stroke(0);
  noFill();
  drawCircle(width/2,height/2,width);
}
void drawCircle(float x,float y,float radius) {
  ellipse(x,y,radius,radius);
  if (radius > 2) {
    radius *= 0.75;
    drawCircle(x,y,radius);
  }
}

8行目で定義した関数の中で、12行目で自分自身の関数を呼び出しています。

10行目でradius変数が2より大きければ再帰的な呼び出しをしています。

再帰的に呼び出しをするときは必ず条件を確認し実行します。

そうしなければ永久ループと同じく、ずっと自分自身を実行するコードとなるのでプログラムが終了しなくなります。

このコードを少し改造します。

void setup() {
  size(500,500);
  background(255);
  stroke(0);
  noFill();
  drawCircle(width/2,height/2,width/2);
}
void drawCircle(float x,float y,float radius) {
  ellipse(x,y,radius,radius);
  if (radius > 2) {
    drawCircle(x+radius/2,y,radius/2);
    drawCircle(x-radius/2,y,radius/2);
  }
}

11,12行目で再帰的に呼び出すときにradius/2としているので、呼び出すたびにradius引数の値が小さくなっていきます。

radius引数の値が2以下となったときに呼び出さなくなり処理は終わります。

これをさらに改造すると次のようになります。

void setup() {
  size(500,500);
  background(255);
  stroke(0);
  noFill();
  drawCircle(width/2,height/2,width/2);
}
void drawCircle(float x,float y,float radius) {}
  ellipse(x,y,radius,radius);
  if (radius > 8) {
    drawCircle(x+radius/2,y,radius/2);
    drawCircle(x-radius/2,y,radius/2);
    drawCircle(x,y+radius/2,radius/2);
    drawCircle(x,y-radius/2,radius/2);
  }
}