FC2ブログ

telealias

 ニワトリだって空を飛べる。

RSS 1.0

2010-03-06(Sat)

オブジェクト指向はそんなに難しくない #1

 カテゴリ名はスーパーハカー坂崎嘉穂の口癖より。

 まぁ、なんていうか、たまにはこういう話もいいかな、と思って、プログラミング関連の話を書いて、今後カテゴリに放り込もうと思います。まる。

 さて、オブジェクト指向。

 なんていうか、今更というか、べつに新しいことじゃないんだけど、たぶんプログラミングの入門がprint("hello, world.");でその後に代入式、四則演算、if文、for文やって関数定義までやったら確かにある程度のことはできてしまう。あ、いや、あと配列くらいはやったほうがいいのか。

 でもここまでやったんだったら、その先はすぐそこにあるんだから、と思ってしまう。
 そんなわけで、オブジェクト指向の話をする。ただし、ご覧の通り関数定義くらいまではわかるというひとが対象。あしからず。

 まず、オブジェクト指向ってなんなの?というところから。
 こういう話になったときに、車だとか、パン屋だとか、そういうたとえが真っ先に出てくるんだけど、このたとえってかえって分かりにくい。プログラミングに直結してないんだもの。
 ひとまずこのたとえのことはおいといて、ちょっと下のプログラムを見てほしい。
 doubleとかintとか型が出てきてるけど、doubleは小数点つき、intは整数の数字が入る容れ物だと思いねい。

double distance(int origin_x, int origin_y, int target_x, int target_y)
  {
  horizon_length = target_x - origin_x;
  vertical_length = target_y - origin_y;
  // 平方根の算出.
  double dis = sqrt(horizon_length * horizon_length + vertical_length * vertical_length);
  return dis;
  }

 見ての通り、平面の二点の距離を求める関数。直角三角形の斜辺の長さを求める要領。sqrtは平方根を求める関数。標準にあるかどうかは知らない。
 まぁ、これくらいのことだったら別に毎度毎度計算してもいいんだけど、それなりに汎用的に使えるから、関数化しておくのはよさそうだ。
 でもこれを実際に使おうと思うと、引数が四つもある!

int a_x = 0;
int a_y = 0;
int b_x = 3;
int b_y = 4;
double from_a_to_b = distance(a_x, a_y, b_x, b_y);

 これくらいならいい。けど、これを頻繁に呼び出すんだったら、ちょっと考えもの。
 構造体を使おう。C++的に書くと、こう。構造体はstructで作る。座標なので名前はcoordってことにしよう。

struct coord
  {
  int x;
  int y;
  };

double distance(coord origin, coord target)
  {
  horizontal_length = target.x - origin.x;
  vertical_length = target.y - origin.y;
  // 平方根の算出.
  double dis = sqrt(horizon_length * horizon_length + vertical_length * vertical_length);
  return dis;
  }

 こうやっておいて、

coord a = {0, 0};
coord b = {3, 4};

double from_a_to_b = distance(a, b);

 この方が、断然いい。何度も繰り返し使うならなおさらそう。
 struct(構造体)っていうのは、つまりはそういう、変数をまとめてひとつのものとして扱うための仕組み。どうしてそうしたいかというと、上の例のように、引数をいくつも書くのは大変だからである。
 もし、aとbの間だけでなく、aとc、bとc、なんてものを同時に計算したかったら、

int a_x = 0;
int a_y = 0;
int b_x = 3;
int b_y = 4;
int c_x = 5;
int c_y = 9;

double from_a_to_b = distance(a_x, a_y, b_x, b_y);
double from_a_to_c = distance(a_x, a_y, c_x, c_y);
double from_b_to_c = distance(b_x, b_y, c_x, c_y);

 めんどくさい。

coord a = {0, 0};
coord b = {3, 4};
coord c = {5, 9};

double from_a_to_b = distance(a, b);
double from_a_to_c = distance(a, c);
double from_b_to_c = distance(b, c);

 まだ幾分ましになったんじゃあるまいか。
 もっとましにする方法がある。

class coord
  {
public:
  int x;
  int y;

  coord(arg_x, arg_y)
    {
    x = arg_x;
    y = arg_y;
    }

  double distance(coord target)
    {
    horizontal_length = target.x - x;
    vertical_length = target.y - y;
    // 平方根の算出.
    double dis = sqrt(horizon_length * horizon_length + vertical_length * vertical_length);
    return dis;
    }
  };

coord a(0, 0);
coord b(3, 4);
coord c(5, 9);

double from_a_to_b = a.distance(b);
double from_a_to_c = a.distance(c);
double from_b_to_c = b.distance(c);

 クラスの使い方っていうのは、つまりこういうことである。
 たとえば、いまの距離の計算なんていうのは、ほかの数値を入れる予定がない。引数に座標しか取り得ない。汎用的とは書いたけど、座標に特化した計算だ。だったら、それは座標それ自体に計算方法がくっついててもいいわけだ。
 プログラミングは処理主体で書かれてきた。オブジェクト指向では、データが主体にある。データに対してどう計算したいのか? オブジェクトは「もの」を指す言葉だけど、同時に「対象」「目的」を指す言葉でもある。
 上で作ったクラスをもう一歩踏み込んでみよう。たとえばこんなこともできるはずだ。

double angle = acos((a.x - b.x) / a.distance(b));

 これは角度を求めるプログラム。なるほど確かに楽に書けるっぽい。けどacos()だとか、a.x-b.xを距離で割ったりとか、ぱっと見、あんまり分かりやすくはない。
 もっとクラスを活用してみよう。

class coord
  {
public:
  int x;
  int y;

  coord(arg_x, arg_y)
    {
    x = arg_x;
    y = arg_y;
    }

  double distance(coord target)
    {
    horizontal_length = target.x - x;
    vertical_length = target.y - y;
    // 平方根の算出.
    double dis = sqrt(horizon_length * horizon_length + vertical_length * vertical_length);
    return dis;
    }
  double angle(coord target)
    {
    horizontal_length = target.x - x;
    return acos(horizontal_length / distance(target));
    }
  };

 ここまで来たらもうベクトルだって取れるんだから、

class vector
  {
public:
  int direction;
  int size;
  vector(int arg_dir, int arg_size)
    {
    direction = arg_dir;
    size = arg_size;
    }
  };

class coord
  {
  //  :
  //  :
  //  :
  // 中略
  //  :
  //  :
  //  :
  vector vector(coord target)
    {
    horizontal_length = target.x - x;
    double dis = distance(target);
    vector vec(acos(horizontal_length / dis), dis);
    return vec;
    }
  };

 を追加したっていいだろう。
 実際的にどれくらいこういう計算が必要になるかというと、それはプログラムの種類によるからなんともいえないけれど、たとえば、シューティングゲームやアクションゲームなんかだと距離や角度の計算は頻繁にしなきゃいけない。シミュレーションのAIの思考ルーチンなんかでも、自身と相手との距離の計算が必要になってくるだろう。
 それをその都度いちいちインラインに書いていたら、はっきりいってしんどい。しんどいからこそ関数化するんだけど、関数化だけだと、たぶん、関数の数が膨大になって、いずれメンテナンスしきれなくなるだろう。
 で、処理単位で分けていってもいいんだけど、データ単位で分けていくと便利なことがいっぱいある、というのが、クラス化の話で、つまりはこれがオブジェクト指向ですよ、という話。もちろん、クラスを細かく分けるほど処理を追いにくくはなるし、そのあたりは適切な加減があるだろうけど。

 オブジェクト指向に詳しい人は、「あれ?」と思ったかもしれない。まだ継承の話をしてない。けど、ものごとには順番というのがある。まずは、データ+処理=オブジェクトっていう考え方を知ってもらえたら、ということから。

 ところで、上のプログラムはあくまで例なので、動作は保障しない。クラスを参照渡ししないで実体渡ししているので非常にメモリ効率が悪いし、そもそも距離計算は絶対値を求めるべきな気がする(abs()とかも使うべきである)。雰囲気だけ掴んでもらえればさいわいです、ってことで。

related: わからないならわからないでいい

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。