プログラミング言語論’10 ＃ 5
久野靖∗

2010.5.13

1

今回の内容

(list ’* (diff (nth exp 1) var) (nth exp 2))(list ’* (nth exp 1) (diff (nth exp 2) var)))))□ ＋αな話題(diff ’(* x x) ’x)→ (+ (* 1 x) (* x 1))• 前回の積み残し (Clojure を使って) --- マクロ、 (diff ’(+ (* x x) (* a x) b) ’x)
Java 連携、マルチメソッドの実際→ (+ (+ (* 1 x) (* x 1)) (+ (* 0 x) (* a 1)) 0)• ス ク リ プ ト 言 語 、組 み 込 み ス ク リ プ ト ---□ 以下 Clojure の積み残し:

JavaScript

マクロ、Java 連携、マル

チメソッド• 総称的プログラミング --- Java Generics、C++
Templates

2.1• Aspect Oriented Programming --- 概論+AspectJマクロ□ Lisp 系の言語 --- 構文が単純 (全部 S 式) →マクロによる拡張が自由2

Lisp 系および Clojure の続き…

□ 疑問:

• Lisp 系のマクロの動作: 引数をそのままの形でマクロに渡す→マクロは式を組み立てて返す→その返

なぜこんなヘンな言語があって使われているのされた式を改めて Lisp 処理系が評価• よくある例: unless を作る --- 関数ではだめか?

• 構文が「(...

(...

(defn unless [cond body] (if cond nil body))(def x 0)(unless (odd? x) (print "Y")) → Y をプリント(unless (even? x) (print "Y")) → Y を プ リ ン ト(!!)))」みたいなのしかない、しかもそれがプログラムが扱うデータでもありプログ
ラムでもある• プログラムを扱うプログラムが簡単に書ける(eval S 式) → S 式を Clojure プログラムとして実行(eval ’(+ 1 2 3)) → 6(list ’+ 1 2 3)) → (+ 1 2 3)(eval (list ’+ 1 2 3)) → 6(def x 4)(eval (list ’+ 1 2 x)) → 7• 関数は引数を常に評価してしまう→副作用があればそれが起きてしまう (副作用がないとしても計算は起きてしまう)• マクロで定義すれば「そのまま」取り扱われるのでこの問題がない(defmacro unless [cond body](list ’if cond nil body))(unless (even? x) (print "Y")) → Y は打ち出されない(macroexpand-1’(unless (even? x) (print "Y")))→ (if (even? x) nil (print "Y")) …これを実行• これを系統的にしたのがマクロ→拡張可能言語• そのほか、「弱い型の言語」「GC のある言語」「記号処理」などの側面が大きかったが、今では他の言語も結構ある• 記号処理についてはやっぱりまだ Lisp に 1 日の長• 例:

数式を微分する□ マクロは「Lisp プログラムを組み立てる関数」(defn diff [exp var](cond(nil? exp) nil(number? exp) 0(symbol? exp) (if (= exp var) 1 0)(= (first exp) ’+)(cons ’+ (map (fn [x] (diff x var)) (rest exp)))(= (first exp) ’*)(list ’+

• →実際には「ほとんど Lisp プログラムをそのまま書くが、一部だけパラメタとして受け取ったものを埋め込む」形• →バッククォート記法ないし構文クォート (上記の作業を簡単にこなす仕掛け)‘(…そのまま… ~x …そのまま…) ←要素埋め込み‘(…そのまま… ~@l) ←リストの残りを接合• これを使うと先の unless は次のように書ける∗ 筑波大学大学院経営システム科学専攻1□ Java のクラスを作り出すには→ proxy(defmacro unless [cond body](if ~cond nil ~body))• (proxy 名前 [クラス or インタフェース…] [変数…] 関数…)□ マクロを使うと「言語の自在な拡張」が可能になる ←Lisp 族ではプログラムがすべて S 式というデータの形(def btn1action(proxy [java.awt.event.ActionListener] [](actionPerformed [evt](let [i (Integer/parseInt (. lab1 getText))](. lab1 setText (. (inc i) toString))))))(. btn1 addActionListener btn1action)になっていることがそれを可能にしている□ マクロによくある問題→記号の捕捉 (capture)• マクロの中で識別子を導入したときに起こる(defmacro myor [e1 e2]‘(let [tmp ~e1] (if tmp tmp ~e2)))(def tmp 0)(myor (> tmp 0) tmp) → ???

• proxy は既存のクラスやインタフェースに従うオブ
ジェクトを作るだけだが、自前のクラスを生成することも可能 (ここでは略)• 普通の Lisp だとこれは次のように展開□ Clojure のデータも実はすべて Java オブジェクト ←(let [tmp (> tmp 0)] (if tmp tmp tmp)) → true

• 基本的な問題:

JVM 上で動いているのだから、基本型でないものはすべて Java のオブジェクト

マクロの中で作業用に使っている

tmp とパラメタで渡されて埋め込まれる tmp とか衝突/混同• よくある対処法:

2.3

マクロ中では「普通使わなそう□ 通常のメソッド呼び出し (Java も) →レシーバの種別

な」名前を使う→根本的な解決でない(クラス) に応じた動的分配• Scheme の synatx-case --- 非常にややこしい• Clojure のアプローチ: ランダム番号つきの名前を自動生成する• より柔軟に→ (1) 複数の引数に応じたメソッド選択、(2) メソッド選択をユーザがコーディング(defmacro myor [e1 e2]‘(let [tmp# ~e1] (if tmp# tmp# ~e2)))(macroexpand-1 ’(myor (> tmp 0) tmp))→ (clojure.core/let [tmp__43__auto__ (> tmp 0)](if tmp__43__auto__ tmp__43__auto__ tmp))• (defumlti 名前 選択関数) :• (defmethod 名前 選択値 [引数…] 本体) :• 選択関数 : 引数を受け取り、選択値のどれかを返す• 例: Java の「+」と同様、「引数どちらから文字列
なら連結、そうでなければ加算」するメソッド mysum(defn sum-dispatch [x y](cond(isa? (. x getClass) String) :string(isa? (. y getClass) String) :string
true :other))(defmulti mysum sum-dispatch)(defmethod mysum :string [x y](. (. x toString) concat (. y toString)))(defmethod mysum :other [x y](+ x y))□ Clojure の特徴の 1 つ→ JVM 上の言語→ Java オブジェ
クトを自在に活用

Java オブジェ

クトの生成

• (.

オブジェクト メソッド 引数…) :Java オブジェクトのメソッド呼び出し• (パッケージ/クラス/メソッド 引数…) :メソッドの呼び出し• 型は適当に変換して渡してくれるメソ

ッドを 1 つ定義

Java との連携• (new パッケージ/クラス 引数…) :マルチメソッドであることの宣言• 必要なら捕捉が起きるようにすることも可能

2.2

マルチメソッド

static□ 自由度、柔軟性は非常に高まるが…これほどの柔軟性が必要な場面にはめったに遭遇しない (よくある例としては、ここで挙げたような演算の選択)(def win (new javax.swing.JFrame))(. win setSize 600 400)(. win setVisible true)(def pane (new javax.swing.JPanel))(. win add pane)(. pane setLayout nil)(def btn1 (new javax.swing.JButton "Button 1"))(. pane add btn1)(. btn1 setBounds 50 20 120 40)(def lab1 (new javax.swing.JLabel "1"))(. pane add lab1)(. lab1 setBounds 50 80 120 40)

3スクリプト言語、JavaScript□ って、聞いたことありますか?

何を意味すると思う?

□ たとえば…

• (答 1) コマンドを並べたもの←シェルスクリプト?
• (答 2) いい加減な/適当な言語←そんなの目標にするか?

2• (答 3) インタプリタ方式の言語← Lisp なんかもそうなの?

<script type="text/javascript">
コード...
</script>• (答 4) ささっと書いて動かせる言語←コンパクト、<script type="text/javascript" src="ファイル"></script>

インタプリタ• (答 5) くっつける (glue) 言語←組み込み型• この場合、コード中で「document.write(...)」を実行して出力したものは HTML 中のその場所 (script
要素のある場所) に埋め込まれる□ スクリプト言語世代説• 第 1 世代→シェルスクリプト、Awk →コンパクト、適
当に書ける<pre><script type="text/javascript">
document.writeln(’ 乱数: ’ + Math.random());</script></pre>• 第 2 世代→ Perl →それ 1 つで結構何でも書ける (汎用)、自立した言語。but キタナイ

□ その 2:

HTML タグの「onmouseover="..."」(マウスが上に乗ったら〜) 「onclick="..."」(クリックした• 第 3 世代→ Python、Ruby →かなり美しい、オブジェ
クト指向ら) などの指定 (イベントハンドラ) の中• JavaScript も第 3 世代でいいと思うが…

3.1• 長いものは書きづらいのでよそに関数を定義して呼ぶことが多い<p onmouseover="window.alert(’
こ</p>

JavaScript の由来□ Netscape Navigator 2 →当時の「最先端の」ブラウザ□ まとめると…

• Netscape 社はブラウザにスクリプト言語を組み込んで様々なことができるようにしようとした• 常に実行するもの (関数定義、ページ中への埋め込み) → script 要素• 言 語 は 当 初「LiveScript」と い う 名 前 で 開 発 →• ユーザの操作に対応するもの→イベントハンドラ

お り か ら の Java ブ ー ム → Sun に お 金 を 払って<html><head><title>文書のタイトル…</title><script type="text/javascript">
常に実行するコード (関数定義等)...
</script></head><body>
ページ内容…
<script type="text/javascript">
document.write() を使うようなコード…
</script>... onclick="..." ...
</html>「JavaScript」にした• 文法はできるだけ Java そっくりにしたが、言語としてはまったく別の言語。よく JavaScript と Java を混同している人がいるので迷惑• ブラウザ組み込みのスクリプト言語として「事実上の標準」• MSIE もこれと互換の「JScript」を搭載□ 標準化の必要性→ ECMA(欧州情報通信システム標準化機構) による標準化→ ECMA-262 規格として成立• そ の 後 、ブ ラ ウ ザ 組 み 込 み だ け で な く Flash のスクリプト言語 (AcitonScript)、MS ASP(Active
Server Page) のスクリプト言語としても採用

3.3• ECMA によるコア部分は共通だが組み込まれている追JavaScript の特徴□ 制御構造の構文は Java にソックリ

加機能はそれぞれの環境で異なる• while、for、do-while、if、try-catch□ 以下では (当然) ブラウザ組み込みの JavaScript を扱• 文字列との連結の「+」も同じ

う (どこにでもあって簡単に試せる)

3.2

var x1 = 1, x2 = 1;
while(x1 < 50) {document.writeln(x1);var z = x1+x2; x1 = x2; x2 = z;}

JavaScript 入門□ HTML の説明はしませんので…

□ JavaScript コードを書ける場所…HTML ファイル中の次 □ オブジェクト指向言語の 3 箇所□ その 1:

乗った ね’)">こo = new Object();
document.writeln("...");

script 要素 (埋め込み、別ファイル)3□ コンストラクタも実はただの関数だが、「new 関数名(...)」の形で呼び出すと中で this が使える

□ 弱い型→型宣言はない、変数に型がない
var x = "ABC", y = 1;

• 文字列/文字の区別はなく「’x’」でも「"x"」でも同じこと□ 「値」と「オブジェクト」の 2 種類がある (Java と同じ)
□ 「値」は数値型 (1 つだけ)、論理値型 (Java と同じ)、
文字列型 (文字型も兼ねる。Java では文字型は値、文字function Counter(n) {this.count = n;this.add = function(n) { this.count += n; }this.getCount = function() { return this.count; }}var c1 = new Counter(3), c2 = new Counter(5);c1.add(2); c2.add(2);document.writeln(c1.getCount() + ’, ’ +c2.getCount());

列はオブジェクト)□ 3 種類の「値」それぞれに対応するオブジェクト種別も □ これで Java と遜色なくオブジェクト指向…だと思う?
□ ちゃんとあるもの
ある (Java と同じ)
□ 値に対してメソッドを呼ぼうとすると自動的にオブジェ
クトに• オブジェクト、インスタンス変数 (プロパティ)、メ
ソッド

var n = 3.141592;
document.writeln(n.toExponential(4));• 同種のオブジェクト、コンストラクタ←かなり ad hoc• メッセージ送信記法□ 関数 (function) がある、関数もオブジェクト• 動的分配←型がないので簡単□ 関数リテラルがある。下の 2 つは同じこと□ 足りないと思うもの…

function add(x, y) { return x+y; }var sub = function(x, y) { return x-y; }document.writeln(add(3, 5) + ’, ’ + sub(3, 5));• クラス→クラスは必要なのか?

(単なる構文単位?)• カプセル化→確かに保護は「全然」ない→スクリプ□ 関数オブジェクトはクロージャ(まわりの環境を一緒に
持つ)

トだから?

(ブラウザと組み合わせた場合、ブラウ
ザ側での保護はアリ)• 継承 (のようなもの) →以下で説明

function test(n) {return function() { return ++n; };}var f = test(5), g = test(10);document.writeln(f() + ’ : ’ + g());
document.writeln(f() + ’ : ’ + g());
document.writeln(f() + ’ : ’ + g());

3.5

プロトタイプ方式オブジェクト指向言語□ オブジェクトの種類 (形) を表す (定義する) やり方→おもに 2 通り□ オブジェクトはすなわち連想配列 (任意の型の値を添字• 「こういう種類のオブジェクトはこういう形」という定義 (==クラス) を書く→クラス方式

として取れる配列)□ オブジェクトのプロパティ(フィールド) は連想配列の• 必要なプロパティ、メソッドを持つオブジェクトを要素と同じまず作り、あとはそれをコピーする (概念的には) →□ 一般に「x.y」と「x[’y’]」は同じものを指す

プロトタイプ方式

var x = new Object();• 実際には全部コピーすると領域が無駄なので、プロx[’a’] = 10; x[’bc’] = 11; x[100] = 12;トタイプ (親) へのポインタを持っておき、「自分がx.b = 13; x.de = 14;
持っていないものは親が持っているものを使う」よfor(var i in x) document.writeln(’x[’+i+’] == ’+x[i]);

うにする

3.4

□ JavaScript ではどうなっているか…

JavaScript のオブジェクト指向機能• 「new 関数 (...)」によってオブジェクトが作成さ□ オブジェクトのプロパティとして関数を入れる→メソッドれるときに、□ メソッドの中ではオブジェクトを「this」で参照できる• その「関数」に prototype というプロパティが定義されていると、

var o = new Object();o.count = 100;o.show = function(msg) {document.writeln(msg + this.count);}o.show(’My number is: ’);• その値が作成されたオブジェクトの「親」としてセッ
トされる

4• 大きなアプリケーションがあったとして、それをカ
スタマイズすることを考える→どうしますか?

• (実際には関数を作ると prototype プロパティには「new Object()」の結果が自動的にセットされる→
そこに色々設定すればよい)□ 沢山パラメタがあって、それを設定して調整すればいfunction Counter(n) { this.count = n; }Counter.prototype.add =function(n) { this.count += n; }Counter.prototype.getCount =function() { return this.count; }var c1 = new Counter(3), c2 = new Counter(5);c1.add(2); c2.add(2);document.writeln(c1.getCount() + ’, ’ +c2.getCount());

いか?
□ パラメタは静的• 「こういう場合はこう」というのが書きにくい• 「動き」はつけにくい□ →パラメタを設定する代わりに「プログラム」を設定する□ プロトタイプ方式の特徴…

• →そのプログラムが動いてさまざまな動作をすればよい• いつでも親にメソッドを追加したりしていじることができる (単なるオブジェクトだから)• 場合によっては親を指しているポインタをとり替えることで、オブジェクトをまったく別物に「変身」させられる (ECMA-262 では不許可、ただし一部の実装
ではこれを許している)• →組み込み型スクリプト• (例は既に出て来た…「マウスが乗ったら〜をする」)□ 組み込み型スクリプトに使われる言語…

function Counter(n) { this.count = n; }Counter.prototype.add =function(n) { this.count += n; }Counter.prototype.getCount =function() { return this.count; }var c1 = new Counter(3); c1.add(4);Counter.prototype.sub =function(n) { this.count -= n; }c1.sub(2);document.writeln(c1.getCount());• tcl →もともとこのような用途のために作られた。しかし言語仕様があまり美しくないので普及はいまい
ち (tcl/tk としてだけ有名)• scheme →もともとは Lisp 系の汎用言語だが、コンパクトな処理系が作れるので組み込みスクリプト用としても使われるように。gwm(ウィンドウマネージャ)、

gimp(画像加工ソフト)• ECMA-262(JavaScript) →元はブラウザ用の組み込□ 継承みたいなのはどうするの?

みスクリプト言語だが、他の用途にも進出 (Flash• 親 (プロトタイプ) を探して見つからない場合はさらActionScript など)にその親を探しに行く。
• 例: Counter オブジェクトを継承してメソッド sub □ JavaScript の場合、オブジェクト指向ならではの利点を追加するには…
→具体的には?
function Counter(n) { this.count = n; }Counter.prototype.add =function(n) { this.count += n; }Counter.prototype.getCount =function() { return this.count; }function ExCounter(n) { this.count = n; }ExCounter.prototype = new Counter(0);ExCounter.prototype.sub =function(n) { this.count -= n; }var c1 = new ExCounter(7);c1.add(4); c1.sub(2);document.writeln(c1.getCount());□ アプリケーション自体および、アプリケーションが扱う
データ→オブジェクトであると考えることができる• オブジェクトを自由に操作するには、オブジェクト指向言語がよい (アタリマエ)• これまでと違うところ→オブジェクトは既に大量に(アプリやそのデータとして) 存在している→既存のオブジェクトを操るのが主目的• 従来のプログラムでは自前でオブジェクトを設計したり生成したりしてからそれを使っていた→大きな□ プロトタイプ方式の特徴をまとめると…

変化 (どんな?)• 実行系のデザインはわりとシンプルにできる□ できあいのオブジェクトを操作する場合の考慮点• コンパイラよりインタプリタ向き。動的• 自分が設計したデータ構造でないのでよく分からない• その分、何をやっているのか分かりにくい• 向こう側はチェックして例外を投げる側、こちらはだ
ましだまし使う側• (きちっと構文を作って行くとクラス方式みたいに…)• きちんと型を書くのが煩雑 (しかし型検査はあった

3.6

組み込み型スクリプト方がよいと思うが…)□ スクリプト言語の 1 つの目的→組み込み型処理系□ 具体例は次の DOM で

5

3.7

□ 結局、DOM で何ができるかというと…

Document Object Model (DOM)• ブラウザが表示している内容→内部のデータ構造が対応□ DOM →ブラウザ内のページをオブジェクトの組み合わせで表現• データ構造 (オブジェクト) を任意に操作→自由に内• W3C による標準化→ http://www.w3.org/DOM/

容が変更できる• 現在のブラウザでは DOM level 2(DOM2) が中心• →究極のカスタマイズ (?)• DOM2 の 標 準 は Core、Views、Events、Style、Traversal、HTML と分かれている

3.8• 以下では例題とともに簡単に説明

本節のまとめ□ スクリプト言語→ささっと書ける言語、glue 言語□ DOM2 では文書全体は Node オブジェクトの木構造として表される□ JavaScript →ブラウザ内蔵むけスクリプト言語が発端• Node オブジェクトのプロパティ/メソッドを用いて• 弱い型の言語

木構造を自由に変更できる• プロトタイプ方式のオブジェクト指向言語<script type="text/javascript">function rot() {var n = document.body.lastChild;
document.body.removeChild(n);document.body.insertBefore(n,document.body.firstChild);}
function dup() {var n = document.body.firstChild;var m = n.cloneNode(true);
document.body.appendChild(m);}function del() {var n = document.body.firstChild;
document.body.removeChild(n);}□ 組み込み型スクリプト• ブラウザ組み込み→ブラウザの制御、DOM によるページ内容の制御総称的プログラミング

4□ 配列型→「int の配列」「char の配列」など型をパラメ
タに持てる• 見かたを変えれば…1 つの「もの」が複数の型に対し利用できる←「総称的」(generic) という• 組み込み演算子なども総称的。5 / 2 == 2, 5.0 /□ HTML 要素に対応するノードのスタイル→その Node オブ

2.0 = 2.5。

ジェクトの style プロパティに値をつけることで色々操• 総 称 関 数 … max(int,int)→
int,max(double,double) → double 等 。← オ ー

作できる

バローディングである程度はできるが、有限個数• 位置の変更→ CSS の位置指定機能で実現できる• array[int]、array[double] …型がパラメタになっている (型パラメタ) →無限の場合に対応。

<script type="text/javascript">
var xpos = 500, ypos = 400;function change(dx, dy, col) {var n = document.getElementById(’x1’);
n.style.backgroundColor = col;n.style.left = (xpos += dx) + ’px’;
n.style.top = (ypos += dy) + ’px’;}</script><button onclick="change(-10,0,’yellow’)">B1</button><button onclick="change(0,-10,’purple’)">B2</button>□ そういうものを「自前で」(ユーザ定義で) 作れるようにするには?

• 以下で説明する Java Generics を使うには JDK 1.5
以降必要。C++ テンプレートは最近の C++コンパイ
ラはだいたい実装ずみ。

□ その他できること…

4.1

コンテナクラス• テキストノードの中の文字列を操作できる (挿入、□ C++でも Java でも配列は「個数を最初に指定」→途中
削除、…)
で大きさが伸び縮みできるものが欲しい→ライブラリで• マウスイベント、キーイベントを受け止めて処理で用意。
きる□ Java (JKD1.4 まで) では → 例: ArrayList クラス。
任意のオブジェクトを格納できる配列。
• フォーム部品の内容 (値など) を操作できる (わりと昔からある機能)6□ 動かすときは…当然、JDK 1.5 の javac を使う。逆にimport java.util.*;

1.5 の javac で 1.4 のソースを動かすときはオプション

public class Sample31 {public static void main(String[] args) {ArrayList a = new ArrayList(); //サイズ 0
for(int i = 0; i < 10; ++i) a.add("X" + i);
for(int i = 0; i < a.size(); ++i) {String s = (String)a.get(i); // 取出し
System.out.println(s);
}
}
}

指定必要

• javac -source 1.4 Sample31.java ← JDK1.4
ソース• javac Sample31b.java ←JDK1.5
ソ ー ス(Generics)
import java.util.*;• 何か疑問な点はありますか?
• この方法の弱点は何だと思う?
□ ダウンキャスト … 「元の型にキャストし戻す」こと。

• a.add("abc") --- Object 型のパラメタに String
を渡す→ OK• String s = a.get(i) --- Object 型は String 型• これで先の弱点のうち a、b、e、f が解消。残る c はダメ。

に入れられない。×。

• String s = (Object)a.get(i) --- ダウンキャス
ト:親クラスの型から子クラスの型へのキャスト。こ• 実はこのコードを実行するとき内部的には先と同様ののとき「元々なに型だったか」がチェックされる。

String でなかったものは String にはキャストでき

コードが使われている。つまり ArrayList の実装は 1
つ (均一な実装、homogeneous implementation)。
だから c の制約が残っている。
• c の 緩 和 策 と し て 、JDK 1.5 で int とInteger、double と Double 等 の 自 動 変 換(AutoBoxing/Unboxing) を追加。

ない (ClassCastException が投げられる)。
□ 弱点 (まずいこと) のまとめ

• a.

public class Sample31b {public static void main(String[] args) {ArrayList<String> a = new ArrayList<String>();for(int i = 0; i < 10; ++i) a.add("X" + i);
for(int i = 0; i < a.size(); ++i) {String s = a.get(i);System.out.println(s);}}}ダウンキャストは繁雑。

ArrayList<Integer> a = ...
a.add(3); // JDK1.5 で○...
int i = a.get(0); // 〃• b. 実行時にチェックが必要→オーバヘッドになる(Java はそれを気にしない言語だということはあるが) 。

□ C++では→言語設計上のポリシーとして、c は受け入れられない。また、C++では配列は「直接そのオブジェク• c. オブジェクトでないと入れられない。たとえば
トが埋め込まれた」ものになるので、コンテナクラスもint は Integer クラスのインスタンスにして入れな同様でなければ受け入れられない。←フルスピードで動ければならない (これもオーバヘッド)。
く C に負けないことが原則。

• d.

配列だから「均一なものの並び」にしたいのに、何でも入ってしまう。

• e.

間違ってヘンなものを入れてもコンパイラは怒ってくれない。

//sample31#include <vector>#include <iostream>
int main() {vector<int> a;for(int i = 0; i < 10; ++i)a.push_back(i+9);for(int i = 0; i < a.size(); ++i)
cout << a[i] << ’\n’; // operator[]}• f. そのヘンなものが取り出されて来たところで実行時にエラー。
□ 実行してみないとエラーが出ないというのはコンパイルする言語にとっての敗北。
□ 根本的な問題…本来「型パラメタ」によって複数の型 (ク• C++では型パラメタをソースのその位置に埋め込んラス) を使用するべきところを 1 個の ArrayList クラで全体をコンパイルするという実装 (非均一な実装、スで済ませていること。

heterogenous implementation)。だから型パラメ□ JDK 1.5 から「型パラメタ」が使えるようになった (Javaタごとに別のコードができる。その代わり、その場

Generics)。ArrayList<T>のようにパラメタは「<>」の中に指定する。T のところには任意の型 (ただしクラス)展開ができる→極めて高速。

を入れてよい。

7

4.2

型パラメタと継承関係

4.4

イテレータ

□ X ⊃ Y で継承関係を表すものとする。たとえば Object □ コ ン テ ナ の 種 類 → 可 変 長 配 列 (ArrayList<T>、vector<T>)、連 結 リ ス ト、B 木 、ハッシュ表 、な⊃ String。
□ パラメタつきクラス C<T>を考える。

どなど。

□ クイズ:• どれでも「順番に要素を処理する」などの操作は必要→それをどれでも同等に扱えるようにしたい (後• たX ⊃ Y ならば、C<X>⊃ C<Y> だといえるか?
とえば、ArrayList<Object> ⊃ArrayList<String> か?

でコンテナを取り替えたりしても利用コードは変え

YES/NO?

ないで済むように)。

□ 次のように考えるべき• 「イテレータ (iterator、反復子)」→次々に要素にアクセスしていくためのオブジェクト。
• X ⊃ Y とは、X 型オブジェクトの代わりに Y 型オブジェクトを使っても OK であること (互換性がある □ Java では共通の Iterator インタフェースとして規定。
JDK 1.5 では Iterator<T>のように各要素の型が型パこと)
ラメタになる。
• 例: 「自動車」クラスの変数に「乗用車」を格納しbool hasNext() --- 終わりかどうか調べる
T next() --- 次の値 (T:パラメタ型) を取り出す
void remove() --- 現在値を削除 (今回は使わない)ても OK(互換性がある)□ ArrayList<Object> a = new ArrayList<String>;
は OK?

import java.util.*;• a.get(0) ...

String が取り出されるから OK。

• a.set(0, Z) ...

public class Sample31c {public static void main(String[] args) {ArrayList<String> a = new ArrayList<String>();for(int i = 0; i < 10; ++i) a.add("X" + i);for(Iterator<String>i = a.iterator(); i.hasNext(); ) {String s = i.next();
System.out.println(s);}}}Object が入れられないと困るが String しか駄目!!!
□ よって先の質問の答えは「NO」であるべきなんだけど…
□ 実は配列でも同じことが起こる。
□ しかし!

Java では X ⊃ Y ならば X[] ⊃ Y[] になっている• 「X[] a = new Y[10];」が可能。そのあとで「a[0] □ さらに!
いては= new X();」→ ArrayStoreException 発生

for(E v: X) { ...

□ 一 般 に こ う い う の を contravariance/covarianceproblem というらしい

4.3

Iterable<E>を実装するオブジェクト X につという for ループ (for-in ループ) を書くと以下と同等に動作
for(Iterator<E> i = X.iterator(); i.hasNext(); ) {E v = i.next(); ...

Java Generics の制約□ Java Generics では実行時には型パラメタ情報がなく□ たとえば上の例も ArrayList<E> が Iterable<E> を実装しているので次のように書ける。

なっている→そのため、型検査の抜け道が起きる可能性→それを防ぐための制約

import java.util.*;• たとえば、「パラメタ型の配列は作れない」「パラメ

public class Sample31d {public static void main(String[] args) {ArrayList<String> a = new ArrayList<String>();for(int i = 0; i < 10; ++i) a.add("X" + i);for(String s: a) System.out.println(s);}}タつき型の配列は作れない」
ArrayList<String>[] lsa = new ArrayList<String>[5];// ↑これは本来は許されない
Object[] oa = (Object[])lsa; // Java では OKoa[0] = new ArrayList<Integer>(); // !!!
...
String s = lsa[0].get(0); // 実行時例外!!!

□ C++では共通のインタフェースはない (各コンテナごとに違う) が形としては次のもの。
*p --- 現在の要素を参照++p、p++ --- 次の要素に進める
p == q --- 同じ要素を指すかどうか判定

8

public class Sample32 {public static void main(String[] args) {Last2<Integer> a = new Last2<Integer>(0, 0);a.put(1); a.put(2); a.put(3);System.out.println(a.get());
System.out.println(a.get());
System.out.println(a.get());
Last2<String> b = new Last2<String>("", "");
b.put("ab"); b.put("cd"); b.put("ef");
System.out.println(b.get());
System.out.println(b.get());
System.out.println(b.get());}}//sample31b#include <vector>#include <iostream>
int main() {vector<int> a;for(int i = 0; i < 10; ++i)a.push_back(i+9);for(vector<int>::iterator
i = a.begin(); i != a.end(); )cout << *i++ << ’\n’;}• 「vectior<int>::iterator」→ vector<int>クラスにおいて typedef で定義されている iterator とclass Last2<T> {T v1, v2;
public Last2(T x, T y) {v1 = x; v2 = y;}public void put(T v) {v2 = v1; v1 = v;}
public T get() {T x = v1; v1 = v2; return x;
}
}

いう型名。

• 普通の「配列要素を指すポインタ」もイテレータとして使える• コンテナにもよるが単なるポインタではないイテレー
タが大半

4.5
□ 例:

型パラメタつきクラスを作る「最後の 2 つの値を記憶するバッファ」(前回やったもの) ただし、記憶される値の型は「任意」

4.6

Standard Template Library (STL)□ C++版 (クラス定義の中に直接コード本体も書いている。
こうするとインライン展開が可能に。または従来通り分□ C++の標準ライブラリの土台となった、テンプレートをけて書いてもいいが、その場合は inline と指定。)
駆使したライブラリ群→現在では C++標準ライブラリのコンテナクラス部分のことをそう呼ぶ。重要なアイデア://sample32#include <iostream>using namespace std;

• テンプレートを使った効率のよいコンテナクラス群• 要素アクセスはコンテナクラスのメソッドではなくtemplate<typename T> class last2 {T v1, v2;
public:last2(T x, T y) { v1 = x; v2 = y; }void put(T v) {v2 = v1; v1 = v;}
T get() {T x = v1; v1 = v2; return x;}};

イテレータ経由 (その方が高速化しやすい)• イテレータに対して働くアルゴリズム群。find (線形探索)、sort (整列)、count(計数)、などなど。
• アルゴリズム群はテンプレート関数 (型パラメタつき関数) →高速。
□ 例:

普通の swap(C 版)

void swap(int *x, int *y) {int z = *x; *x = *y; *y = z;}
int a[100]; ... swap(&a[i], &a[j]) ...

int main() {last2<int> a(0, 0);a.put(1); a.put(2); a.put(3);cout << a.get() << ’\n’;
cout << a.get() << ’\n’;
cout << a.get() << ’\n’;
last2<char*> b("", "");
b.put("ab"); b.put("cd"), b.put("ef");cout << b.get() << ’\n’;
cout << b.get() << ’\n’;
cout << b.get() << ’\n’;}• 関数呼び出しのオーバヘッド、ポインタ経由のアクセス→遅い。しかも型ごとに用意する必要。
□ 関数テンプレート版の swap(C++版)
template<typename T> inline void swap(T& x, T& y) {T z = x; x = y; y = z;}float a[100]; ... swap(a[i], a[j]) ...

□ Java 版。

• 関数テンプレートの型パラメタは推定される (a[i]import java.util.*;等が float だから T は float) →繁雑な指定が不要

9• 「float z = a[i]; a[i] = a[j]; a[j] = z;」
がこの場所に埋められているのと同様→高速

4.7

テンプレートの特殊化□ テンプレートで一般向けの実装のほかに「特殊ケース」□ アルゴリズム関数を利用すれば制御構造をこちら側で書かなくてもよくなる。たとえば for each を使うとループが不要になる。

を用意したいことがある。

• たとえば bool(0/1) のバッファだったら 1 ビットず
つ入れれば 32 個の値が 1 語で入るとか…

//sample33#include <algorithm>#include <vector>#include <iostream>using namespace std;

• コンテナクラスの場合、「ポインタ用」はそれなりのポインタ用のものを使う方がよさそう… (「ポイン
タ用」のクラスを 1 つ用意して残りはすべてそれを呼ぶ→コードを何回も展開しなくて済む→コード量

template<typename T> void f(T x) {cout << x << ’\n’;}の爆発を防ぐ上で重要なテクニック)□ そのような「特殊ケース」を別途用意できる→特殊化

int main() {vector<int> a;for(int i = 0; i < 10; ++i)a.push_back(i);for_each(a.begin(), a.end(), &f<int>);}

template <typename T> class last32 {//一般}
template <typename T> class last32<T*> {// ポインタ用}
template <> class last32<bool> {// bool 用}• これで for each の中で「f(値)」が繰り返し呼ばれる→「ベクタの各要素を出力」ができる。ループは不要。

• しかし「合計を取る」だとどうするか?

4.8型パラメタに対する制約□ 関数オブジェクト… operator() を持つようなオブジェ
クト。先の「f(値)」というのは関数でなく operator() □ 例: maxbuf<T>というコンテナを作る。
の呼び出しでもよいので。
• 次々に put(T x) で値を入れていく。
//sample33b#include <iostream>#include <vector>#include <algorithm>• T get() でその時点での最大値を取得できる。
//sample34#include <iostream>using namespace std;

template<typename T> class maxbuf {T max;
template<typename T> class sum {public:T res;
maxbuf(T x) : max(x) { }public:void put(T x) { if(max < x) max = x; }sum(T init) { res = init; }T get() { return max; }void operator()(T x) { res += x; cout<<res<<"\n"; }};T result() const { return res; }};int main() {int a1[] = { 5, 9, 2, 7, 6 };int main() {maxbuf<int> m1(a1[0]);vector<int> a;for(int i = 1; i < 5; ++i) m1.put(a1[i]);for(int i = 0; i < 10; ++i)
cout << "a1: " << m1.get() << ’\n’;
a.push_back(i);double a2[] = { 3.0, -2.7, 6e2, 4e1, 0.5 };sum<int> s(0);maxbuf<double> m2(a2[0]);s = for_each(a.begin(), a.end(), s);for(int i = 1; i < 5; ++i) m2.put(a2[i]);cout << s.result() << ’\n’;
cout << "a2: " << m2.get() << ’\n’;}}• ループ周回を通じて保存されて欲しいものは関数オブ □ 型 T には演算子「<」が必要。これを満たさないと…
ジェクトのインスタンス変数にすればよい。→ ルー//sample34b
プ構造と処理の分離#include <iostream>

template<typename T> class maxbuf {T max;
public:10

public Maxbuf(T x) { max = x; }public void put(T x) {if(max.compareTo(x) < 0) max = x;}public T get() { return max; }maxbuf(T x) : max(x) { }void put(T x) { if(max < x) max = x; }T get() { return max; }};}class point {double x, y;
public:point(double a, double b): x(a), y(b) { }};□ Comparable<T>というのは x.compareTo(T y) というメソッドを持ち、このメソッドが x と y の大小関係に応
じて正/負/零を返す。
□ この方が確かにあらかじめチェックはできるが、窮屈かも

int main() {point a3[] = { point(0,0), point(1,1),
point(2,2), point(3,3), point(4,4) };maxbuf<point> m3(a3[0]);for(int i = 1; i < 5; ++i) m3.put(a3[i]);}…
□ あらかじめ Comparable<T>を implements しているク
ラスにしか使えない→自分がソースを持っていればいいが、そうでないと…

□ 上記のをコンパイルすると…

• 実は C++版にも同様の問題がある。たとえば char*の maxbuf を取ろうとすると「アドレスが最大の」もIn method ‘void maxbuf<point>::put(point)’:
22:
instantiated from here
7: no match for ‘point & < point &’のが取れてしまう…求めていることと違う。

□ 確かに「<」がない、と言われる… 次のを入れればよくなるが。

□ C++の場合は、単独関数テンプレートと特殊化を使えばOK。
//sample34c#include <iostream>#include <cstring>using namespace std;bool operator<(point &p) {return x*x+y*y < p.x*p.x+p.y*p.y;}• しかし「コンパイルしてみたら無かった」では実装 template<typename T> bool lt(T x, T y) {return x < y;の中に立ち入っていてエラーメッセージとしてあまり良くないのでは…

• 実際、極めて分かりづらいエラーメッセージに遭遇することがよくある}
template<> bool lt(char* x, char* y) {return strcmp(x, y) < 0;}

• 本来だったら「この型パラメタにはこういう型しか template<typename T> class maxbuf {T max;
あてはめてはいけませんよ」という指定ができた方がいいのでは?

→ 賛否両論。否定派:

指定が繁雑になり自由度が減る。ともあれ、Java Generics
ではこちらの立場。
□ 上記の Java Generics 版。整数は Integer クラスを使う必要。
import java.util.*;public class Sample33 {public static void main(String[] args) {int[] a1 = { 5, 3, 8, 2, 4 };Maxbuf<Integer> m1 = new Maxbuf<Integer>(a1[0]);for(int i = 0; i < 5; ++i) m1.put(a1[i]);System.out.println(m1.get());
String[] a2 = { "this", "by", "foo", "z", "x" };Maxbuf<String> m2 = new Maxbuf<String>(a2[0]);for(int i = 0; i < 5; ++i) m2.put(a2[i]);System.out.println(m2.get());}}

public:maxbuf(T x) : max(x) { }void put(T x) { if(lt(max, x)) max = x; }T get() { return max; }};int main() {int a1[] = { 5, 9, 2, 7, 6 };maxbuf<int> m1(a1[0]);for(int i = 1; i < 5; ++i) m1.put(a1[i]);cout << "a1: " << m1.get() << ’\n’;
char* a2[] = { "this", "by", "foo", "z", "x" };maxbuf<char*> m2(a2[0]);for(int i = 1; i < 5; ++i) m2.put(a2[i]);cout << "a2: " << m2.get() << ’\n’;}□ 特殊化の応用• 一般の T →「<」が使われる• char* → strcmp が使われる• その他特殊ケースはいくらでも対応可能 … C++の実用性に 1 日の長があるかも?

class Maxbuf<T extends Comparable<T>> {T max;

11

4.9

テンプレートによる mixin クラス□ flavors ではこれでできる (どっちの update が呼ばれるかの規則があって OK)。しかし C++ではそもそも□ 前回やったように、mixin とは「他のクラスに混ぜるた

getValue() や update() の宣言がないと型検査できな
くてアウト。

めのクラス」• それ自体単独でインスタンスを生成しないことから「抽象サブクラス」と呼ぶことも□ ではどうするか…

• count や history で親クラスを指定すればもちろん動くようになる。

• 前回の話では flavors などの多重継承を使って混ぜていた• しかしさまざまなものにこれらの機能を組み込もうと思って作ったのに、決め打ちで親クラスを書いて• しかし C++のような静的検査な言語では多重継承ではうまく行かないしまうのでは 1 つにしか使えない (または繰り返し□ たとえば、次のようなクラスを考える。
class balance {int value;
public:balance() : value(0) { }void update(int v) { value += v; }int getValue() { return value; }};コピーになる) のでよろしくな。

• ではどうすればいい?
□ 答: その親クラスをテンプレートパラメタで指定する!!
//sample35#include <iostream>class balance {int value;
public:balance() : value(0) { }void update(int v) { value += v; }int getValue() { return value; }};class maxbuf {int max;
public:maxbuf() : max(0) { }void update(int v) { if(v>max) max=v; }int getValue() { return max; }};class maxbuf {int max;
public:maxbuf() : max(0) { }void update(int v) { if(v>max) max=v; }int getValue() { return max; }};□ 「update の回数を数える」
class count {int num;
public:count() : num(0) { }void update(int v) { ++num; update(v); }int getCount() { return num; }};template<class T> class count : public T {int num;
public:count() : num(0) { }void update(int v) { ++num; T::update(v); }int getCount() { return num; }};□ 「更新履歴を記録する」
class history {int record[1000], nrecs;
public:history() : nrecs(0) { }void update(int v) {record[nrecs++] = getValue(); update(v);}void showHistory() {for(int i = 0; i < nrecs; ++i)
cout << ’ ’ << record[i];cout << ’\n’;}};

template<class T> class history : public T {int record[1000], nrecs;
public:history() : nrecs(0) { }void update(int v) {record[nrecs++] = T::getValue(); T::update(v);}void showHistory() {for(int i = 0; i < nrecs; ++i)
cout << ’ ’ << record[i];cout << ’\n’;}};□ これを次のようにして使いたい。
class MyClass :public balance, count, history {};int main() {int a[] = { 5, 9, 2, 7, 6 };history< count< balance > > c1;
count < history< maxbuf > > c2;
for(int i = 0; i < 5; ++i) {12• もっと複雑なデータを処理したり、複雑な手順を持つ関数を生成したりすることもできる → テンプレー

c1.update(a[i]); c2.update(a[i]);}cout << "c1: " << c1.getCount(); c1.showHistory();cout << "c2: " << c2.getCount(); c2.showHistory();

トメタプログラミング}• 使いこなすのはとっても難しいと言われている (あまりやりたくない)□ mixin クラスを使うことで、1 つのクラスに盛り込まれている複数の側面を分離して記述し、使う時に組み立てることができる。

4.11

本節のまとめ□ しかし、実際には 1 つの「側面」が複数のクラスにまた □ 総称的プログラミング (型パラメタ、パラメタつき型)
がって存在していることが普通→このような「一群のク→ C++ テンプレート、Java Genrics で実現 (内容はかラス」を 1 つの「層」として、それを積み重ねてシステ

なり違っている)。主な用途:

ムが構成されるというイメージ。
□ そのようなものを実現するには→ C++でもクラスの中にクラスが書けることを利用し、外側クラスをパラメタつきとする。

コンテナクラス□ STL(C++) → イテレータによるアクセス、汎用アルゴ
リズム□ 型パラメタの制約 → 賛否分かれるところ。Java では
インタフェース/サブクラスによる制約指定可能 (だがtemplate<class T> class ThisLayer : public T {public:class First : public T::First { ... };class Second : public T::Second { ... };...
};不自由でもある) → C++に対する制約機構 (concepts)
は設計途上 → 後で少し紹介□ mixin クラス (C++) → 親クラスを拡張するための抽象
サブクラス□ こうすれば、一群のクラスで Fist は First どうし、 □ 全体として C++テンプレートは強力、しかし難しい。より詳しく知りたい人は:
Second は Second どうし、…で縦に合成が起きる。

• ThisLayer の中にはこの層が実現する機能のためのデータやコードをまとめて入れることができる。

• アンドレイ・アレキサンドレスク著, 村上訳, Modern• このような構成を「mixin layers」と呼ぶ。

• επιστημη, 高橋晶, C++テンプレートテク

C++ Design, ピアソン, 2001.
ニック, ソフトバンク, 2009.

(少しやさしい。筆頭著者の読みは「えぴすてーめー」)• このような「構成法」の代替案→言語自体の拡張 (AOP
言語) →すぐ後で

4.10

テンプレートメタプログラミング (簡単に)(むずかしい)5□ テンプレートの展開→関数の評価のようなもの→計算に利用できる

AOP — Aspect-Oriented Programming□ オブジェクト指向 (OO、Object-Orientation) は確か//sample36#include <iostream>に問題を「人間に分かりやすく構造化」する手段を提供してくれた (と思う)。

template<int n> class Fact {public:enum { RET = Fact<n-1>::RET * n };};template<> class Fact<0> {public:enum { RET = 1 };};□ しかし、どのようにうまく「構造化」しても、その構造化から「洩れている」側面が必ず存在する→横断的側面(Croscutting Concern、 XC)□ OO が 定 着 し た 現 在 で は 、こ の XC を う ま く 取 り 扱う方法が言語研究者の関心時の 1 つとなっている→

Aspect-Oriented Programming(AOP)
int main() {std::cout << Fact<5>::RET << ’\n’;}• AOP によって OO だけでは扱えない部分をうまくカバーした言語が作れる (はずだし、実際そういう言• これを動かすと当然「120」が表示されるが、それは語を提案している) → AOP 派の主張

実行時に計算しているのではなく、コンパイル時に □ 以下で紹介するのは AOP 派の主張であり、それに「説「5*4*3*2*1*1」という式が生成され、それが計算さ得」される必要は必ずしもありません。
れている→高速13• こ の よ う な も の を「 横 断 的 側 面 (Croscutting
Concern)」と呼ぶ• ある言語 X が 99%のニーズをうまく満たしているな
ら残り 1%をカバーするため言語を 2 倍複雑にすることは引き合わない。

• しかしこれが「80%満たしている」のと「1.2 倍複雑」
だったら引き合うのかも知れない。

XC• なお、いつまでたっても「100%カバー」はあり得ない。
• 実は OO が従来の手続き型に対してやったことも似たようなもの→ということは、OO による進歩のあとで「残っていること」がどれくらい大きいのかは疑問な気もする。

• また現状の AOP 言語による「カバーのしかた」がいいのかどうかも分かっていない (研究段階)□ 横断的側面はオブジェクト指向による構造化と直交しているので、無理矢理オブジェクト指向で扱うよりも、新

なぜ OO だけでは不足なのか?

5.1

しい言語機構を導入して扱う方がよい。

□ OO がもたらしたもの→プログラムの扱う世界を「もの」• …というのが AOP の主張ですが、OK と思う?

の集まりとして位置付け、「もの」ごとに「性質」(イン疑問?

スタンス変数など) と「動作」(メソッドなど) を記述するようにした5.3• なおかつ「もの (の種類)」どうしの関係についても取り扱えるようにした (インタフェース/動的分配による総称性、継承による階層化、実装の引き継ぎ)。
□ じゃあ「それで十分じゃん」と思いますか?

Programming• プログラムはいくつもの「側面」を持っている→
そ れ ら を そ れ ぞ れ「 分 け て 」取 り 扱 う の が よ い

何か「こ(separation of concerns)

ますか?

• そのような「側面」のうちには、さまざまな部分にバラバラに現れて来るものがある→横断的側面

図形を扱うアプリケーションで「図形が変化したことを記録する」(croscutting concern)• それらを分けて扱うため、メソッドやクラス階層と• いろいろな図形がいろいろな風に変化する

は「別の」メカニズム (=アスペクト) を導入して、• これまでの方法→変化させるメソッドに記録のコー
ドを挿入

これを通じて横断的側面を記述可能にする→ Aspect

Oriented Programming• ×本体の動作と記録の動作がごちゃまぜになる• AOP 言語→そのような機構を採り入れたプログラミ
ング言語• ×記録コードそのものは類似したものをそこら中にばらまくことに• 用語:□ この他にもさまざまな「あちこちに同じもの挿入」の例

join point → 複数の側面を「接合させる」

点のこと

がある

□ 代表的な AOP 言語の流派 (?)• データの永続化• Adaptive Methods --- メソッドを状況に応じて変化 (適応) させることでさまざまな場合に使えるよう• 表示の更新• トレース、アンドゥに → Demeter/Java (DJ) ライブラリ• などなど…

5.2

Oriented□ AOP の基本的な考え方

ういうことは欠けているのではないか」というのはあり□ 例:

Aspect(AOP)• MultiDimensional Separation Of Concerns(MSDOC) アプローチ--- 複数の「側面」をそれぞれ記述し合成して 1 つのシステムに → Hyper/J 言語

横断的側面□ 結局、これらの問題は「オブジェクトの階層構造とは直交した横断的な機能や動作」をプログラムに組み込もうとするところに起因する。

14• Composition Filters --- 複数の側面を組み合わせるフィルタを用意してこれらを集めてシステムを組み立てる• after() : P C { コ ー ド } --- pointcut P C
実行直後にこれこれを行え。

• Aspect/J --- Aspect と呼ばれる記述を別に用意して既存のクラス構造を修正/拡張 ← AOP 言語の「代表」の地位を占めつつある• after() returning :P C { コ ー ド } --- 同上ただし正常リターン時のみ。

□ 以下では Aspect/J を例にどんな感じか見ていただく• after() throwing :P C { コード } --- 同上

ただし例外で戻った場合のみ。

5.4

Aspect/J• 返 値 の 型 around() : P C { コ ー ド } --pointcut P C の代わりに実行するコードを指定。

□ Gregor Kiczales らが作成した Java を土台とした AOP

コード中で proceed(...)

言語

くとそこで本来の実行コードが呼ばれる。

• http://www.eclipse.org/aspectj/ で フ リ ー 配
布

5.7

□ 特徴…

• Java の 上 位 互 換 (ふ つ ー の Java プ ロ グ ラ ム は
AspectJ のプログラムでもある)というものを書いてお下敷きとなる Java の例題□ 前回の例題の類似品• アイコンをマウスでドラグして移動できるだけ• 既存の Java プログラムにアスペクトを追加して行け import java.awt.*;
る (既存のクラスを後づけで変化)
import java.awt.event.*;
import java.awt.geom.*;□ 以下では最低限感じが分かる程度の例題のみ説明// 窓を開くアプリケーション
public class Sample41 extends Frame {DrawSet set = new DrawSet(); // アイコンの集合
public Sample41() {set.addObj(new DraggableIcon("A", 100, 100));
5.5 Pointcut
set.addObj(new DraggableIcon("B", 120, 120));
set.addObj(new DraggableIcon("C", 140, 140));□ Pointcut --- プログラム実行時の「ここ」という点。
setSize(400, 400);
addMouseListener(new MouseAdapter() {• call(返値の型 クラス名. メソッド名 (引数の型…))public void mousePressed(MouseEvent evt) {--- これこれのクラスのこれこれのメソッドが呼ば
set.mousePressed(evt.getX(), evt.getY());
repaint(); // マウスダウン時の処理れるところ}• call(クラス名.new(引数の型…)) --- これこれのpublic void mouseReleased(MouseEvent evt) {set.mouseReleased(evt.getX(), evt.getY());
クラスのコンストラクタが呼ばれるところ
repaint(); // マウスアップ時の処理• initialization(クラス名.new(引数の型…)) --}});同様だが、初期設定が行われるところ
addMouseMotionListener(new MouseMotionAdapter() {• cflow(call(...)) --- 指定した呼び出し以下でpublic void mouseDragged(MouseEvent evt) {行われる実行
set.mouseDragged(evt.getX(), evt.getY());
repaint(); // ドラグ時の処理• cflowbelow(call(...)) --- 同様だが、その呼び}出し自体は含まない});
addWindowListener(new WindowAdapter() {□ 上記の基本的な pointcut を組み合わせて名前をつける
public void windowClosing(WindowEvent evt) {System.exit(0); // 窓が閉じる→終了
ことができる}});• pointcut 名前 (...) : 定義すみ pointcut の組}
合せ ;public void paint(Graphics g) { set.draw(g); }public static void main(String[] a) {(new Sample41()).setVisible(true);} // ここで窓を開く
5.6 Advice}• 網羅的な解説とかは上記サイトで□ Advice --- 「既存のプログラムのここをこういう風に直せ」という指定。

• before() :P C { コード } --- pointcut P C

実行直前にこれこれを行え。

// 画面上に現れるものを表すインタフェース
interface DrawObj {public void draw(Graphics g);
public boolean hit(double x, double y);public void mouseDown();

15

大変そう

public void mouseUp();public void mouseDrag(double x, double y);• 「記録する」のはどこか 1 箇所で何とかしたい}□ 案 2:// DrawObj の集まりを保持しマウスで選択可能に
class DrawSet {DrawObj hit = null; // 選択中のもの
DrawObj[] a = new DrawObj[200];
int count = 0;
public void addObj(DrawObj o) {if(count < a.length) a[count++] = o;}public void draw(Graphics g) {for(int i = 0; i < count; ++i) a[i].draw(g);}public void mousePressed(double x, double y) {hit = null;for(int i = count-1; i >= 0; --i) {if(a[i].hit(x, y)) { hit = a[i]; break; }} // マウスの座標に当たった最初のものを選択
if(hit != null) hit.mouseDown();}
public void mouseReleased(double x, double y) {if(hit != null) hit.mouseUp();hit = null;}public void mouseDragged(double x, double y) {if(hit != null) hit.mouseDrag(x, y);}}

アスペクトとして別途分離して記述□ AspectJ で記述した変化表示アスペクト
import java.awt.*;
aspect DisplayChange {int count = 0;
pointcut displayChange() :call(void DraggableIcon.moveTo(double, double)) ||call(void DraggableIcon.setColor(Color));after() : displayChange() {System.out.println("changed: " + (++count));
}
}

□ 読み方

• DraggableIcon の メ ソッド moveTo() ま た は
setColor() が呼ばれたところ→「表示変化」という pointcut にする。
• 「表示変化」という pointcut の直後に「いくつ目の変更」というメッセージ表示を挿入する。

□ 動かし方// ドラグできるアイコン
class DraggableIcon implements DrawObj {Font fn = new Font("Serif", Font.BOLD, 24);% ajc Sample41.java DisplayChange.java
String label;% export CLASSPATH=.:/compat/linux/user/local/
double xpos, ypos, rad = 25.0;
Java/aspectj1.5/lib/aspectjrt.jar
Color col = Color.yellow;% java Sample41
public DraggableIcon(String s, double x, double y) {label = s; xpos = x; ypos = y;
• ajc --- AspectJ Compiler}• 実行はこれまでと同じ ← 生成されるコードは普通
public void draw(Graphics g) {int r2 = (int)(rad*2);の Java バイトコードだからそのまま客先に配布でg.setColor(col);きる
g.fillOval((int)(xpos-rad),(int)(ypos-rad),r2,r2);
g.setColor(Color.black); g.setFont(fn);• ただし! 1 つだけ追加のライブラリファイル (.jarg.drawString(label, (int)(xpos-12), (int)(ypos+8));ファイル) がある→それを CLASSPATH に追加 (上記}AspectJ 配布キットにドキュメント)public boolean hit(double x, double y) {return (xpos-x)*(xpos-x)+(ypos-y)*(ypos-y)<rad*rad;}□ 驚くべきこと
public void moveTo(double x,double y){xpos=x;ypos=y;}public void setColor(Color c) { col = c; }• もとの Sample41.java には何ら手を加えていないpublic double getX() { return xpos; }• 「ここをこう直したい」ということはアスペクト側
public double getY() { return ypos; }public void mouseDrag(double x,double y){moveTo(x,y);}だけで指定
public void mouseDown() { setColor(Color.pink); }• これによって複数のアスペクトを混合しても対処可
public void mouseUp() { setColor(Color.yellow); }}能に• またアスペクトの書き方を工夫すれば再利用可能に

5.8

例: 画面の変化を記録するとしたら…

5.9□ 案 1:

DraggableIcon クラスのメソッドに記録用のコードを挿入してまわる

例: 画面の変化を記録し後戻り可能にする□ たとえば上のプログラムをさらに次のようにする• もっといろいろなものが画面に現れるようになると

16• とりあえず「動き」だけについて記録

• 記録の各レコードを表すクラスを用意する

DraggableIcon icon; double xpos, ypos;
public History(DraggableIcon i, double x, double y) {icon = i; xpos = x; ypos = y;}public void apply() { icon.moveTo(xpos, ypos); }• 位置変化ごとにそのインスタンスを配列に追加する• 後戻りボタンを画面に貼り付けておく• ボタンが押されたら配列から最後の位置記録を取り}出してそこへ戻す}□ こういうのを実現するとしたらどういう機能がさらに□ まず最後に記録用の入れ子クラスがつけてある。これの必要?

インスタンスにアイコンとその位置を記録する□ さっきのと何が違うか?

→ 挿入したコードの中からア □ 冒頭の配列 record にインスタンスを順次入れていく
イコン等のオブジェクトを参照しなければできない□ pointcut は 3 つに増えている。いずれもパラメタを取• 実は pointcut の冒頭で「変数宣言」できる。

るようにしている• さらにその宣言した変数が「カレントオブジェクト• Sample41 のインスタンスを作るところである」「メソッド呼び出し対象オブジェクトである」• アイコンが動くところ、ただし History からの呼び出しは除く等の指定を書くことであてはめが行われ、変数に値が取り出せる。

• アイコンの色が変わるところ• その pointcut を利用する側でこのパラメタを参照できる。
□ さっきより少し長いが全部まとめて示すと…
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;□ 位置変化の「後」の動作はさっきと同じ□ 位置変化の「前」に配列にに位置を記録する□ Sample41 が初期化された「後」にボタンを貼り付ける• ボタンの動作の中で後戻りを実現• ボタンの動作中から app を参照するには変数 app はaspect RecordChange {History[] record = new History[5000];
int count = 0;
Button b1 = new Button("Back");
pointcut createApp(Sample41 app) :this(app) &&initialization(Sample41.new(..));
pointcut positionChange(DraggableIcon i) :target(i) &&call(void DraggableIcon.moveTo(double, double))&&!cflowbelow(call(void History.apply()));
pointcut colorChange(DraggableIcon i) :target(i) &&call(void DraggableIcon.setColor(Color));final である必要がある

5.10

AspectJ と AOP のまとめ

□ AspectJ にはほかにもいろいろ機能があるけど…

• 要するに「外から」既存のクラスの動作前後に介入できることがポイント• SOC (Separation of Concern) とはこういう感じ• 他の AOP 言語もそれぞれ違った側面からこのような「分離」に挑戦している□ それで…
after(DraggableIcon i) : positionChange(i) {System.out.println("moved: " + (++count) + " : " +i.getX() + "," + i.getY());• ○このような言語はすばらしい! と思いましたか?
}または
before(DraggableIcon i) : positionChange(i) {• ×こんなに複雑にしてもしょうがないじゃん! と思record[count] = new History(i, i.getX(), i.getY());}
いましたか?
after(Sample41 a) : createApp(a) {final Sample41 app = a;app.setLayout(null); app.add(b1);b1.setBounds(20, 20, 80, 40);
6 演習問題b1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent evt) {□ (9) JavaScript を用いて、自分の Web ページに動きや
if(count > 0) {機能をつけるような「ライブラリ」(つまり自分だけに
record[--count].apply(); app.repaint();}しか使えないのではなく、他人にあげたら使ってもらえ}るような形のもの) を作ってみよ。その経験に基づいて、});
JavaScript によるプログラミングと他の言語 (Java}static class History {等) によるプログラミングの違いについて考察せよ。
「動17

きや機能」の例としてはたとえば次のものが考えられる(ただしこれらに限定せず自由にアイデアを出してよい)。
• HTML 要素を動かしたり現われさせたり消したりさせる• ポップアップ/プルダウンメニュー機能 (サブメニューも出せるとなおよい)• ページ内でクリックすることでクリックした個所を強調するなどページ内容が加工できるようにする□ (10) C++テ ン プ レ ー ト ま た は Java Generics (JDK

1.5 以降必要) を用いて、自分なりの「汎用的な役に立つクラス」を開発してみよ (イメージとしては max バッ
ファや last2 バッファのような感じでよい)。実際に複数の型をあてはめて動作させること。また、それを使った場合と使わずに「普通に」書いた場合の性能比較も行うこと。(さらに、C++テンプレートと Java Genericsの両方作って両者の比較もするとなおよい。)□ (11) AspectJ の処理系を入手し (入手先は本文中に記載)、本文の例題を動かしてみよ。納得したら次のような拡張を行ってみよ。

• アイコンの色変化も記録/再現できるようにしてみよ。
• もっと他の図形や他の動きも入れたプログラムにして、それを記録/再生できるようにしてみよ。

• その他、AspectJ の機能を活用した例題を新たに考えて作成してみよ。
□ (12) C++テンプレートメタプログラミングで階乗以外の何らかの値の計算をさせてみよ (組合せの数とかフィボナッチ数とか?)。その上で、普通のコードで計算した場合と性能比較を行ってみよ。もしコンパイルが異常に遅いならその原因も究明せよ。

7

さいごに

□ この講座では、さまざまなプログラミング言語に現れる
概念を、その設計思想、用途、実装、特徴、問題点などの観点から整理して見てきたつもりです。
□ 構成としては、1 回目→プログラミング言語一般〜抽象
データ型、2 回目→オブジェクト指向の導入、3 回目→
オブジェクト指向の主要部分＋ドリトル言語、4 回目→
オブジェクト指向言語のさまざまな機能＋ Clojure、5回目→組み込みスクリプト＋総称的プログラミング＋

AOP、のように構成しました。
□ プログラミング言語にはさまざまな機能や側面があり、それらを取捨選択して 1 つの言語としてまとめるのはな
かなか簡単ではないことがお分かり頂けたかと思います。

18