2008-11-11
目次
りんご味Ruby 第35回 藤本 尚邦
前回に引き続き、Rubyでの構文糖衣(syntax sugar)やDSL(Domain-SpecificLanguage=ドメイン固有言語)の作り方について書いていきます。
■ 構文糖衣=メソッド呼出
Rubyには、クラス定義でアクセッサ宣言するために attr_reader,attr_writer, attr_accessor などの構文糖衣があります(以下はこれらを使ったクラス定義の例)。
class TodoItem
attr_reader :summary
attr_writer :done
def done?
@done ? true : false
end
def initialize(summary)
@summary = summary
end
end
上のプログラムの attr_reader, attr_writer を使っているところが、「パッと見」あたかもRuby言語に最初から備わっている宣言文のよう見えるでしょうか? どう見えるかというのはまったく主観的なものなので、「いやそうは見えない」と言われてしまうとそれまでなのですが、ここはあたかも宣言文のように見えるということにしてください。これまでに強調してきたように、このattr_reader, attr_writer は、Rubyの本物の構文ではなく、メソッド呼出による疑似的な構文です。
さて、これがメソッド呼出であるからには、メソッドを呼び出すメッセージの
受け手(=レシーバオブジェクト) が存在するはずです。attr_readerの場合、
レシーバは何になるのでしょうか?
答えはselfです。これらのメソッド呼出ではレシーバが省略されているので、レシーバはselfということになります(第8回,第22回を参照のこと)。Rubyのプログラムが実行(評価)されているときには、常に、省略されたときの暗黙のレシーバであるself が存在しています。
試しに以下のようなテストプログラム:
class Hello
p self, self.class # クラス定義に実行される
def hello
p self, self.class # このメソッドが呼び出されたときに実行される
end
end
Hello.new.hello
を実行してみましょう。ここで p というのは、デバッグなどのためにRubyに組み込まれている印字メソッドで、引数で渡された各オブジェクトの文字列表現(Object#inspectの結果)を改行文字付きで出力します(TodoItemクラスの定義にpの呼出を埋め込んで試してみるのもよいでしょう)。結果は:
Hello
Class
#
となりました。この結果から:
・クラス定義が実行されたときのselfは、ClassクラスのインスタンスオブジェクトであるHello
・インスタンスメソッドhelloが実行されたときのselfは、Helloクラスのインスタンスオブジェクトである#
ということがわかります(注: 2つめの p が実行されるのは、クラス定義時ではなくメソッド呼出時)。
以上のことから、TodoItemのクラス定義における attr_reader メソッド呼出時のレシーバとなるオブジェクトは、「ClassクラスのインスタンスオブジェクトであるTodoItem」ということになります。Rubyでは
Classクラスのインスタンスメソッド=クラスメソッド(のようなもの)
なので、attr_reader もクラスメソッドのようなものということになります。
ここで、ちょっと遠回りになりましたが、前回、構文糖衣の例として上げた、真偽値を返すアクセッサを宣言するための構文糖衣 attr_predicate の定義に戻ります。ここまでのことから、attr_predicate を TodoItem のクラスメソッドとして定義できそうだ、ということが見えてきたでしょうか。以下は、attr_predicate を TodoItem のクラスメソッドとして定義したものです。
class TodoItem
# クラスメソッドattr_predicateの定義 (このselfはClassクラスのインス
# タンスであるTodoItem)
def self.attr_predicate(*names)
names.each do |name|
define_method("#{name}?") do
instance_variable_get("@#{name}") ? true : false
end
end
end
attr_reader :summary
attr_writer :done
attr_predicate :done
def initialize(summary)
@summary = summary
end
end
このクラスを使って
item = TodoItem.new("りんご味Rubyの原稿を書く")
item.summary # → "りんご味Rubyの原稿を書く"
item.done? # → false
# ...
item.done = true # 原稿を書いたので「完了」とする
item.done? # → true
というようなプログラムを実行することができます。ポイントはattr_predicate 宣言によるアクセッサメソッドの done? です。
こうして、attr_predicate という(疑似)構文糖衣を使うことが出来るようになりました。しかし、クラス定義のたびに、attr_predicate をクラスメソッドとして定義するのではあまり意味がありません。attr_predicate も、attr_reader や attr_writer と同様に、クラス定義時には暗黙で使えるようにしたいですよね。ということで次回に続きます。
藤本裕之のプログラミング夜話 #148
前回の終わりが「渡辺久美子って誰だって? それはあなた「ケロロ軍曹」を演ってる声優のひとであります。では次回はそのアイディアについて」だったので,なんかオレが今回の原稿で「渡辺久美子のアイディアについて書く」と思って妙なことを期待してるヒトがごく一部にいた(?)ようだがそれは文脈を取り違えてる。
そーではなくて,ゲームのアイディアの話です。そもゲームのアイディアというのはそうそう路傍の石のようにそこらにごろごろと転がっているものなのか。そうぢゃないよね?
そりゃあ,テトリスに限らず今流行りの数独だってオセロゲームだって,時代を遡れば囲碁将棋麻雀の類だってモトはと言えばニンゲンの考えたものでは,ある(いや「ヒカルの碁」によれば囲碁界には「囲碁は神の考えたゲーム」って説もあるんだけど)。
だから,ある朝あなたが目覚めたら,今まで誰も考えたことがなく,それでいて一度やったら誰もがヤミツキ,やめられないとまらないのエビセン的面白さがあり,しかもコンピュータにインプリメントするのも簡単で,かつ作成するのにコストのかかる可愛らしいキャラクターやコワいモンスターのCGも要らず,そしてできれば他人が真似をするのはちょっと難しい……みたいなゲームが頭の中にでき上がっていたんですよあっはっは,というようなことが今後ないとは,言いきれない。
でもそういうのはやっぱりマレだと思うでしょ? 宝くじで2億円当たってそれが原因で殺されるほどマレではないとしても,ただ宝くじで2億円当たるくらいにはマレぢゃないですか?
ちうか,前々回にオレが「ゲームを作って一発当てる可能性」と書いた時に,あなたの頭のなかに上のようなシチュエーションが浮かんだか,というと浮かばなかったでしょ? もし違ったらごめんなさいだけど,大方のヒトの頭に浮かんだのは,前回オレが「第1群」と分類した「ありもののコンピュータ版」か,「第2群」にしてもテトリスではなく,例えば「ドラクエ」や「ファイナルファンタジー」,その他オレが知らないだけでそのスジでは有名なゲームのシリーズ……すなわち,アイディアというよりはビジュアル,アルゴリズムよりもシナリオがメインのものだったと思うのだ。
が,ここで思い出して欲しいのは,ずっと前にここに書いた「ソフトウエアの独立性」の話である。……もう一度同じことを書くのは嫌なので,覚えていないヒトは #135〜137あたりを読み返してくだされ。……あの議論を踏まえると,あなたが思い描いたようなゲームの製作というのはなんちうか,とってもとってもとってもとってもリスキーな事業なんだよね。
いっすか(ちょいと渡辺久美子入ってるな)? 今を去ること7年前,大ヒットゲーム・シリーズ「ファイナル・ファンタジー」の制作会社であるスクウェア(現スクウェア・エニックス)は,ハリウッドに進出,大枚1億3700万ドルをかけて全編フル3DCGのアニメ映画「ファイナル・ファンタジー」(英語では「Final Fantasy:The Spirits Within」)を作った。あれ,どのくらい
の興行収入を上げたかというと全米で3200万ドルだそうな。なんでもギネスブックにも載ったほどの大コケで,スクウェアが計上した特別損失が130億円……これでどっかに「ファイナルファンタジーの権利を10億で買いませんか?」とか言ってりゃ今ごろ逮捕されてるかも知れんという……いや,それは別の話っすね。
斯様にどでかい損失を出した映画「ファイナル・ファンタジー」であったが,それでもその後まったく金を産まなかったわけではない。一応4年後のクリスマスイブには地上波でも放映されいくばくかの放映権料が入ったし,DVDだって売っている。あんな映画のDVD誰が買うんだと言うなかれ。「デビルマン」のDVDだって買うヤツはいたのである。
ここで一つ思考実験。大コケしたのが映画ではなく本家のゲームの方だったらどうだったか?
(以下次回 2008_11_06)
高橋真人の「プログラミング指南」第146回
〜XcodeによるPowerPlant X入門(35)〜
こんにちは、高橋真人です。早速続けます。
ようやくアタッチメントを外すためのViewを得るところまで説明が終わりました。残っている部分はそんなに複雑ではないので、ざっと見ていきましょう。
前回までのお話で、Viewを検索した時に指定した100という識別子の値ですが、これはViewの生成時に設定している値です。また、アタッチメントを付けるのもViewの生成直後に行っているわけです。
MyApplication.cpのDoSpecificCommand()の中にその実際のコードがありますので、参考にしながら読んでいただきたいのですが、アタッチメント(MyViewAttachment)は単純にnew演算子で生成を行います。そして、初期化をしてからViewにアタッチします。
アタッチメントを外す時にはこの逆の手順になります。アタッチされていたViewを見つけたら、今度はそのViewに対して外すべきアタッチメントの検索をします。何故いちいち検索などということをする必要があるかと言えば、アタッチメントは一つのオブジェクトに対して複数アタッチすることができるからです。
今回のプログラムでは、MyViewAttachmentのインスタンスを特定するために、kMyViewAttachmentという定数を指定しています。これはUInt32の値です。
外すべきアタッチメントが見つかったら、Viewに対してそれを取り外すように依頼するわけです。MyWindow.cpのDoWindowDeactivated()、これは前回まで説明していた部分です。ここで、Viewの取り外しを行っていますが、コードは以下のような感じです。
MyViewAttachment *attachment =
PPx::SafeDynamicCast
RemoveAttachment()という関数が、取り外しを行う処理ですが、そのあとにdelete演算子でattachmentを消去していますね。何故これがあるかと言えば、Viewはアタッチメントをポインタの配列として保持しているからで、単に“外した”だけではアタッチメントのインスタンスが存在したままだからです。いわゆるメモリリークの状態です。
ところが、このdelete演算子には、単にメモリリークを防ぐというか、メモリの後始末をするという以上の意味があるのです。
実際、このdeleteの行をコメントアウトしてプログラムを動かしてみると、不思議や不思議、ボタンにかぶさった青い覆いは何度Windowを切り替えようが、ずっと残ったままになります。ちゃんとRemoveは行われているのに、です。
アタッチメントという名前が付けられているこの仕組みですが、実のところ、特定のオブジェクトにアタッチされているかどうかは関係がなく、単に、Initialize()の時に対象のオブジェクトを指定しただけでこのアタッチメントの“動作を変更する”対象は決まってしまっています。
ただ、Viewにアタッチしておかないと、後で外す時にそのアタッチメントを特定することが難しいために、“便宜上”Viewに持たせる格好にしているということなのです。
以上の説明でお分かりになった方もおいでと思いますが、PPxのアタッチメントは、そのインスタンスが消去される時点で内部に仕組まれていたイベントハンドラが始末されます。アタッチメントによって暫定的に加えられていた、このイベントハンドラが取り除かれることにより、“本来の”イベントハンドラがそのまま機能するようになるわけです。
といった感じで、「説明は終わり」と言いたいところですが、ここで素朴な疑問。では、アタッチメントにおけるイベントハンドラはどこに仕込まれていて、消去される時にどのようにイベントハンドラが取り除かれるのでしょう?
常識的に考えれば、MyViewAttachmentかそのいずれかの継承元のどこかに、イベントハンドラを保持する何らかのクラスがメンバ変数として保持されていると考えるのが自然です。ところが、いくら探してもそのようなメンバ変数はありません。これは一体どうしたことでしょうか?
実は、この辺にも、PowerPlant Xのちょっとした面白い仕組みが隠れています。次回はその辺を探ってみたいと思います。
◇MOSAからのお知らせと編集後記は割愛します◇