FC2ブログ

telealias

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

RSS 1.0

スポンサーサイト

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

related: スポンサー広告

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: わからないならわからないでいい

2010-03-04(Thu)

言葉のいづるは

 以前に、breakfastを「断食を破る最初の食事」と説明する方がいたので、breakfastには最初という意味は含まれないということを言ったら、「事実上最初なのだから、間違いではない」と返されたことがある。
 間違いではない、というような内容は積極的には人に伝えるべきではないと考えているが、しかし個人の考えレベルでは論理的な説得力に欠けるので、それ以上はなにも言わなかった。

 そんなことをふと思い出して考えてみたが。

・breakfastは断食を破る、の意
・かつて宵の断食という習慣があり、それを破って摂食することから、こう呼ばれるという説
・字義的には一日の最初の食事を表す

 ということは、遅い朝食は、breakfastだということになると思われるのだけど、さて、その前にdinnerとsupperの違いに触れておく。
 これまたbreakfastと同じくらいには有名だとは思うけど、dinnerは豪勢な食事、晩餐、supperは簡素な夕食、というのが辞書的な意味だそうだ。が、これは正確ではない。
 dinnerというのは、一日で一番重要な食事を指す。だから、晩餐は正確ではない。昼でも朝でもかまわないからだ。事実、かつては昼によく食べ、夜は軽く済ませるという習慣があった。
 さて、社交界では、夜も活動時間になるから、軽い夕食では夜が更ける頃には空腹を覚えるのではないか。と思ったら、やはり夜食を摂ることもあったようだ。
 これを卑しい、慎まれるべきだと律したのが教会だ。節制をもって身を清めるを尊し、であり、夜食は暴食の類として禁じていた。fastはfasten(しっかりする)に通じている。断食は節制のためだったわけだ。
 さて、昼のdinnerに戻ってみる。
 なぜ昼によく食べる必要があったのだろう。
 夕には軽く、夜は食べない。もし朝起きて軽食を摂るなら、昼はそれなりの食事でも構わないと考えられる。
 逆に朝を摂らなければ?
 なるほど、昼は自然とよく食べることになる。推測ながら、つじつまは合う。
 で、推測どおり、かつては朝を抜いて昼と夕の二食という習慣があった。これは教会の教えによるもので、労働にはげむものたちは、働くべくして朝に軽い食事を摂った。らしい。時代によってもこの習慣はまちまちだろうけど、さもありなん。
 では、教会の一日最初の食事は、breakfastだろうか。
 わざわざ破るというくらいだから、毎日の習慣についてこう呼ぶのは変だなぁと思うのは、ぼくだけだろうか。
 禁を破るとは、恐ろしい言葉だと思う。教会で聖職に就くものが、自らに、破るなんて語彙を使うだろうか。逆で、正当性を守るために、より自然な行為だと示さなければ、民は神に仕えるものとして納得しないかもしれない。
 昼は正餐であり、これを摂ることは当たり前のことである、と。
 断食を破る、というのは、この正餐より前に食事を摂ることじゃなかろうか。断食というのは、食べてはいけない期間を示すものだと考える。何時から何時までと決まっている。
 当たり前のように朝食を摂るなら、それはもう断食の期間を過ぎているのではないか。
 まぁbreakにそれほどの強い意味があるかどうかは知らないのだけど、正餐より前の労働前に摂る、規律外の軽食、くらいが、原義かと思う。
 正餐を摂ることはbreakfastではない。

 ところで、この食生活と文化について、異世界ながら、中世~中近世を舞台にしていると思われる「狼と香辛料」では、丁寧に描写されている。それができるくらいには考察が行われていることが察せられる(またかと言うなかれ。この作品は本当によく書かれているのだ)。

 ま、気になったので調べたよ、という話。

related: ことば

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