トップ «前の日記(2016-12-01) 最新 次の日記(2016-12-11)» 編集

日々の破片

著作一覧

2016-12-04

_ メタプログラミングについてのメモ

メタプログラミング、日本語で書けば超プログラミング。

最初に「超」を強く意識させられたのは、大学2年の一般教養の数学だ。 なぜか受講するのがおれ1人だったので、教授が初日にあきれたのか、1冊の本を買えと言ったかくれたのか覚えてないが、読んで出席して質問しろ形式となった。

それが数学から超数学へだった。

数学から超数学へ―ゲーデルの証明(E.ナーゲル)

超数学は、数学を斜め上(「斜め」の部分はレトリック)から研究する学問だというのは読み始めてすぐにわかる。数学という学問がどのようなお膳立てで成り立っているかを解明することにある。いずれにしても、メタに考えるには常に「上から目線」の立場に自分を置くべきだ。「上から目線」で語れるということは実に良いことなのだ。

メタプログラミングの場合、それはプログラミングを斜め上からプログラムすることになる。

ちょっと前までは最初に出会うメタプログラミングは、Cのプリプロセッサだろう。

IDEによる補完サポートがないCのプログラミングで構造体SのインスタンスAへインスタンスBから同名のフィールドAからZへ詰め替えを行うコードは以下のようにうんざりするものとなる。

typedef struct {
  int field_A;
  // (24行省略)
  int field_Z;
} S;
S A;
S B;
A.field_A = B.field_A;
...(24行省略)
A.field_Z = B.field_Z;

(もちろん、全フィールドのコピーであればA = B;で済むわけだが、他にもたくさんフィールドがあって、ここではこの26個のみを対象としたい場合とする)

うんざりするのが嫌な人(それ自体はまっとうな感覚である)は、コピペを覚えて、最初の行をコピーして25回ペーストして50個のAをそれぞれ2個ずつのBからZへ直していく。コピペプログラマの出来上がりである。彼はここで学んだコピペ技術を一生大事にするだろう。

それは悪いことではない。正しく常に機械のように正確に字の置き換えが可能ならば。

が、そこで満足せずに斜め上からコードを眺めてみるとまた違った風景が見える。

このばかげた26行のコードは良く見れば同じパターンの繰り返しだ。

繰り返しはばかげているので、繰り返し部分をマクロにする。

#define B2A(x) A.filed_##x = B.field_##x;
B2A(A)
...(24行省略)
B2A(Z)

ずいぶん手数が減った。

ここではA.field_(x) = B.field_(x);というコードの生成をプリプロセッサマクロによってプログラムしたわけだ。

が、26行の打ち込みが必要なことは変わらない。

とは言え、ここでコピペ技術を駆使すると、最初は各行に対して2文字を置き換える必要があったのが、わずか1文字で済むようになっている。作業が半分に減った! 当然、あり得る置き換えミスも半分に減った勘定だ。

だが、なぜ手作業で25個も文字を置き換えなければならんのだ?

もしfield_A~Zの型が同じであれば、次のように定義すれば詰め替えはさらに楽になる。

typedef enum {
   field_A,
   ...(24行省略)
   field_Z,
   filed_count
} SFiled;
typedef struct {
  int field[filed_count];
} S;
S A;
S B;
for (int i = field_A; i < filed_count; i++) {
    A.field[i] = B.field[i];
}

構造体のフィールドは配列に変えたが、アクセスに直定数ではなく、filed_Aやfield_Zという列挙子を使う限りにおいて安全性は最初のものとそれほどは変わらない(個々のフィールドへのアクセスについては、フィールド名を記述しなければならない分だけ手数が増えるのでフィールド名か列挙名を工夫する必要がある)。

これはソースコードレベルでのメタプログラミングではないが、フィールドの定義というデザインレベルのプログラミングによる、メタプログラミングと言えなくもない。

同じように、ソースコード上でのメタプログラミングを離れても、エディターマクロであるとか、スクリプトを書くとか、他にもソースコード記述時の繰り返し作業をなくして人間の代わりにソースコードを吐き出す手段はいくらでもあることに気付く。

これらもプログラム(ここではソースコード)をプログラミング(ここでは生成するプログラムを作る)する意味においてメタプログラミングなのだった。

結局のところメタプログラミングの目的は、人間の作業の低減にある。

人間の作業は確実に間違いを伴う。しかも手の動く速度が律速となる。したがって、手作業をどれだけ減らすかが作成したプログラムの品質の高さと生産性のキーとなる。

そのためには、機械化しやすいようにあらかじめターゲットとなるシステム内に出現する要素の名前をデザインしておくことも重要となる。

DRYにやれるのは、逆に言えば、名前なり操作なりパターンなり要素なり構成部品なりが重複しているからだ。

重複しているからこそ、重複点を串刺しして、単一のものにまとめることが可能となる。

テーブル名、カラム名、モジュール名、パラメータ名、ファイル名、モジュール名、クラス名、メンバ名などのシステムのさまざまな構成要素の同じ意味にマップされるべきものが、あらかじめ厳密に異なり、マッピング不可能な名前として定義されていたら、しかも意味も微妙に異なっていてある時は同一、ある場所では包含、ある処理においては無関係で他の要素によって置換されたりしていたら、これらをDRYに処理すること自体が難問となる。データの重複をなくしておくことも必要となるわけだ(バックアップや代替構成が不要という意味ではない)。

すると、メタプロングラミングのためには、あらかじめまともなデザインが必要だし、それを決めるのはアーキテクチャだ。

というわけで、個々のプログラミング言語でのメタプログラミングの技術も肝要だし

メタプログラミングRuby 第2版(Paolo Perrotta) (やりやすい言語というものはある)

クラスライブラリ(というよりも仮想マシンのレベル)が実行時のプログラム自体の情報取得と操作をサポートしていることも重要だが

メタプログラミング.NET(Kevin Hazzard)

より重要なのは、ソフトウェアシステム全体をどう考えるかという上から目線となる。

で、エバーグリーンで常緑な達人プログラマーはそういう観点について学ぶための本なのだ。

新装版 達人プログラマー 職人から名匠への道(Andrew Hunt) (したがって書かれている要素技術は20世紀のものなのだが、常緑を歌えるということなのだ)

2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|

ジェズイットを見習え