2008-12-09
目次
りんご味Ruby 第37回 藤本 尚邦
真偽値を返すアクセッサを宣言するための(疑似)構文糖衣 attr_predicate を使えるようにする手段として:
・クラス継承 – 構文糖衣を実装した親クラスを用意
・ミックスインモジュール — 構文糖衣を実装したモジュールを用意
・オープンクラス – Moduleクラスに直接、構文糖衣を実装
のうち、前回は、クラス継承とミックスインモジュールについて取り上げました。今回はオープンクラスによる方法を見ていきましょう。
■ オープンクラス — Moduleクラスに直接、構文糖衣を実装
Rubyでは既存のクラスを実行時に直接変更することができます。この特徴は、「すべてのクラスはいつでも変更できるように開かれている」という意味で、オープンクラスと呼ばれています。オープンクラスにより、attr_predicate を組込みの attr_reader や attr_writer とまったく同レベルの機能として実装することができます。attr_reader や attr_writer はModuleクラスのインスタンスメソッドなので、attr_predicate も同じようにModuleクラスのインスタンスメソッドとして実装しましょう。
--------------------------------------- attr_predicate.rb ---
Module.class_eval do
def attr_predicate(*names)
names.each do |name|
define_method("#{name}?") do
instance_variable_get("@#{name}") ? true : false
end
end
end
private :attr_predicate
end
----
クラスに対して、ブロック付きでclass_evalメソッドを呼ぶことにより、既存のクラスに定義を追加・変更することができます。この例では、Moduleクラスにインスタンスメソッドのattr_predicateを追加的に定義しています。このライブラリをロードすると、すべてのクラス定義で attr_predicate を使うことが出来るようになります:
require 'attr_predicate'
class TodoItem
attr_reader :summary
attr_predicate :done
# 以下略
end
クラス継承とミックスインモジュールによる方法が、能動的にクラス継承したりモジュールを組み込まない限り使えないのに対して、オープンクラスにより直接Moduleクラスを変更する方法では、attr_predicate を定義したライブラリをロードするだけで、どのクラス定義でも attr_reader と同じレベルでattr_predicate を使うことが出来るようになります。
(補足 — オープンクラスにより直接書き換えてしまう方法は、とくに多数のプログラマが使うケースでは、予期せぬ事態を引き起こしかねないので、乱用しないように注意して使いましょう!)
■ ClassクラスとModuleクラス
ところで、なぜ attr_reader や attr_writer は Moduleクラスのインスタンスメソッドなのでしょうか? これまで、モジュールやクラスとは何か? ということにほとんど触れていませんでした。Rubyでは、Objective-Cと同様に、クラスやモジュールも、あるクラスのインスタンスであり、数値・文字列・配列などと同等に扱うことのできるオブジェクトです。そのことを踏まえた上で、以下に説明していきます。
Moduleオブジェクト(=Moduleクラスのインスタンス)の主な役割は、メソッドの実装を持つことです。以下のモジュール定義例では、speakerとspeakという2つのメソッドの実装を持つSpeakableというModuleオブジェクトを定義しています:
--------------------------------------- speakable.rb ---
module Speakable
def speaker
if not defined? @@speaker
require 'osx/cocoa'
@@speaker = OSX::NSSpeechSynthesizer.alloc.init
end
@@speaker
end
def speak
speaker.startSpeakingString(self.inspect)
end
end
----
Moduleオブジェクトは、それを派生させて新しいモジュールを作ったり、そのインスタンスオブジェクトを生成したりすることはできません。単に、メソッドの実装を持つことができるだけです。そこでClassオブジェクト(=Classクラスのインスタンス)が登場します。Moduleオブジェクトの特徴に、派生クラスを定義する機能やインスタンスを生成する機能を加えたものがClassオブジェクトです。
このような構成は、ClassクラスがModuleクラスの派生クラスとして定義されていることによっています。Classオブジェクト(=Classクラスのインスタンス)がメソッド定義を持つことが出来るのは、Moduleオブジェクト(=Moduleクラスのインスタンス)としての特徴によるものと見做してもいいでしょう。
Moduleオブジェクトは、クラスオブジェクトの定義(平たく言うとクラス定義)のとき Module#include を使ってインクルードすることにより、多重継承なしで、メソッドの実装を共有することができるようになっています。あるいは、Object#extend を使うことにより、クラス単位ではなく、任意のオブジェクトに対してだけモジュールを組み込むこともできます。オープンクラスにより、既存のクラスに後からモジュールをインクルードすることもできます。
require 'speakable'
Object.class_eval { include Speakable }
とすると、すべてのオブジェクトでspeakメソッドを使うことが出来るようになります:
12345.speak # 12345を(英語で)読み上げる
[1,2,3,"hello"].speak # 配列の内容を順に読み上げる
話を attr_reader に戻しましょう。Rubyでのメソッド定義には def 構文を使うというのが基本ですが、attr_reader や attr_writer、そして自分で機能追加した attr_predicate は、def構文でメソッドを定義する代わりの手段として、アクセッサメソッドの定義を簡単に記述するために用意された(疑似) 構文と考えることができるでしょう。実際に、attr_predicate の実装では、インスタンスメソッドを定義するためのメソッドdefine_method(これについては次回以降で説明予定)を使ってアクセッサメソッドを定義しています。
ということで、attr_readerなどの疑似構文は、そのインスタンスがメソッド定義を持つのが特徴であるクラス、つまりModuleクラスのインスタンスメソッドとして実装されているわけです。似たような理由で、モジュールをクラスに組み込むための(疑似)構文糖衣である include やdefine_method などもModuleクラスのインスタンスメソッドとして実装されています(次回に続く)。
藤本裕之のプログラミング夜話 #150
前回まで、映画とコンピュータゲームとを比較して得られた結論を簡潔にまとめると、「どちらもその制作(開発)に巨額な費用がかかるが、ヒットしなかった場合のリスクはコンピュータゲームの方が大きい」ということになる。ひらたく言えばいまやコンピュータゲームを作って一発当てようってのは映画製作よりハイリスクな博打だってことだ。
もちろんコンピュータゲームも映画も、その気になりさえすれば……あ、なるだけぢゃダメか、その気になり、その気を持続することができれば制作費をそれほどかけずに、たとえばたった一人でも作れないわけではない。試しにちょっとそっち方面を考えてみようか?
まずは映画、実写で出演者が自分一人だけというのはかなり難しいだろうが(難しいというか必然的にアバンギャルドになっちまうような気がする)、アニメを作ると仮定すれば、自分で絵を描いて撮影してちょっと違う絵を描いて撮影してまたちょっと違う絵を描いて撮影してまたまたちょっと違う絵を描いて撮影して……と、確かに大変な作業ではあるが、それでも「鉄腕アトム」の昔と違い、今オレがやったようにコンピュータでコピー&ペーストが使えるのだからかなり楽になってはいる。
音楽だってGarage bandで作れるし、アニメの声なら全部の登場人物を一人で演じることだって不可能ではあるまい(初期の「サウスパーク」では製作者の1人、トレイ・パーカーがほとんどの登場人物を演じていた)。こう見てくると映画の場合の問題は、制作よりも公開と資金回収のシステムにあるようだ。個人で作った映画なんて普通の配給会社は観てもくれないだろうし、自主上映は多くの場合持ち出しである。
え、コンテストに出す? それはスジ違いである。というか、ここで考えているのはその制作物でいかにして食って行くかであり、それを足がかりにして(コンテストで認められるなどして)次の仕事を得ることではない、違います。……というか、そういうミチスジしか浮かばないほど個人制作の映像作品をお金にする道は険しいということである。公開するだけだったらYouTubeでもいいんだろうけどね。
ではコンピュータゲームはどうだろう。一人で制作できるか、と言えばもちろんそれは可能だろう。映画と比較すれば一人でやることによるディスアドヴァンテージは小さいかも知れない。公開だってWebサイトからダウンロード可能にするだけ。簡単だ。代価の回収もベクターなどの料金回収システムを使えばさほどの手間ではない。なんだ、コケたときに将来に渡って全然金が入って来る見込みがないってことさえ我慢すれば道はあるのか……と思いました?
ちっちっち、でもここまでの話には「一人で金をかけずに制作できる映画あるいはゲーム」のクオリティに関する視点がすっぽり抜けているんだよね。……確かに、プログラマなら誰でも知っているように、コンピュータプログラムのクオリティの高さはそれに関わったスタッフの人数に比例しない。いや、むしろ反比例することさえある(……と、しといてやるが大抵反比例しますよね)。
だけどコンピュータゲームという商品はもはやプログラムコードだけで構成されているものではないんである。根本にあるゲームのアイディア、コンセプト、シナリオなどは別としても、プレイヤーが目にするインタフェース部分のグラフィックの量はビジネス・アプリケーションの比ではないし、ただ絵があればいいわけではなくて、ゲームの内容により親しみやすかったり可愛らしかったりかっこうよかったり怖かったりしなくてはならない。
それをまた3Dにして動かすとなると、あのモデリングという……なんつかな、オレのような「面倒くさいことが大嫌いだからプログラマやってる」みたいなニンゲンには「同じコンピュータの前に座ってニンゲンがやる行為とは思われない」ようなちまちまちまちまちまちまちまちました作業が必要になり、またそこに出来不出来が生じてくるし、それがまたゲームのクオリティとして云々されるわけで……。
早い話、どんなに超人的な努力をしても、今どき一から十まで一人で制作しましたなんてゲームで「一発当てる」ことは不可能に近いのである。確かにオレは前の原稿でテトリスの例を出したけど、逆に言えばテトリス以来ああいう例を知らないでしょ? ちうことは、プログラマがゲームを書いて食って行くってことはとりもなおさずゲームを作るチームの「スタッフの一員」となるってことなんだよね。次回はちょっとその辺を……。
(以下次回 2008_12_05)
高橋真人の「プログラミング指南」第148回
〜XcodeによるPowerPlant X入門(37)〜
こんにちは、高橋真人です。
前回の最後で投げかけたテーマは、“値をどこに保存しておくか”ということでした。
C言語では、変数にスコープという概念があります。これは、「有効範囲」ということを意味しますが、この「有効」の中には二つの要素があります。一つが生存期間、そしてもう一つがアクセス可能範囲です。
Cにおける変数のスコープには、代表的にはグローバルとローカルの二つがあります。いわゆるグローバル変数とローカル変数です。グローバル変数は、プログラムが動いている間ずっと有効です。つまり、メモリ上に存在し続けます。
これに対しローカル変数は、その変数が定義されている部分を囲む範囲がその有効範囲となります。一般にローカル変数は関数の先頭で定義されることが多いですが、任意の場所での定義も可能です。簡単なサンプルを見てみましょう。
01: void foo(void)
02: {
03: int a = foo1();
04: if (a % 2 == 0) {
05: int b = a * 20;
06: ++b;
07: foo2(b);
08: }
09: foo2(a);
10: }
面白みもない関数ですが、注目していただきたいのは変数のaとbです。これらはいずれもローカル変数ですが、スコープが異なります。aのスコープは関数の実行される間ずっと、ということになります。対してbは、4行目のif文の条件が成立した場合にのみ、存在します。そしてその有効範囲は7行目まで。
9行目のfoo2(a);の時点ではbはもう存在しませんから、仮にここにfoo2(b);とでも書こうものならば、コンパイル時に「bなんて変数、知りませんよ」と言われてしまいます。
では、次にちょっとだけ変更してみます。
01: void foo(void)
02: {
03: int a = foo1();
04: if (a % 2 == 0) {
05: static int b = a * 20;
06: ++b;
07: foo2(b);
08: }
09: foo2(a);
10: }
どこが変わったか分かりますか? ほんのわずかなところですが、5行目のbの宣言の頭にstaticというのが付きました。
実は、このコードはCではコンパイルできません。「初期化の要素が定数でないので」と出てしまいます。では何故こんなコードを書いたかと言いますと、C++では問題なくコンパイルが可能だからです。
ローカル変数にstaticという識別子が付くと、これはただのローカル変数ではなく、静的変数というものになります。静的変数になってもローカルであることに変わりはなく、アクセス可能な範囲としては前の例と同様ですから、やはり9行目でfoo2(b);と書けば、「そんな変数は、知らん!」と言われます。
しかし生存期間という意味では、7行目で終わることはありません。プログラムが動いている間は存在し続けます。ただし、静的変数がグローバル変数と寿命において異なるのは、「どの時点で生成されるか」です。
グローバル変数がプログラムのスタート時から終了時まで、ずっと存在し続けるのに対して、静的変数は定義の行われている行、つまり上記のコードでは5行目が最初に実行される時点で生成されます。それはつまり、もしプログラムでこの部分が実行されることがなければ、この変数は生成されないままプログラムの終わりを迎えるということも意味します。
さて、static識別子が付くことにより起こる変化でさらに興味深いのは、初期化の部分です。変数bの宣言(定義)部分では、
static int b = a * 20;
となっています。これは、bが定義されると同時にa * 20の値で中身が初期化されるということを示しています。静的変数における初期化も、他の変数の場合と同様、「変数が生成される時に設定される値」を意味しますから、メモリ上にずっと存在し続けるbという変数の初期化は最初に5行目が実行された時に行われることになります。
よって、このfoo()という関数が複数回呼び出された場合でも、bの値がa * 20に設定されるのは最初の1回のみです。従って、この関数が呼ばれる度、bは6行目にあるインクリメントの処理によって増加しますから、7行目のfoo2()に渡される値は、1つずつ増えていくのです。仮に、最初のaの値が20であったとすると、bは呼び出されるごとに401、402、403…となっていくという具合です。
あ、もちろん6行目のコードが実行されるためには4行目の条件が成立しなければなりませんね。とりあえず、毎回aは偶数であるということにしておきます。
さて、今回の例では、bの初期化に単純な式を使っていますが、この部分が関数の呼び出しになっていても構いません。そうしますと、例えば「最初の一回だけ呼び出される関数」などという仕組みも作ることが可能になります。
今回のこの静的変数の使い方がこれからの説明で重要になりますので、よく理解しておいてください。
mosa entranceだより 第2回 エンドウ ヒデオ
11月25日(火)、第4回 mosa entranceを開催いたしました。うれしいことに前回以上に、多方面からの参加者が増えて来ています。
前回までお話を伺っていた小池邦人さんが多忙のため、急遽iPhoneアプリ「今日の地震」をリリースされたばかりの、南保さんからお話を伺うことになりました。
前回までは、メモリ管理やインターフェースビルダーなどプログラミング手法についての内容でしたが、今回はアプリのリリースまでのお話や、「今日の地震」で行なっている、外部サーバとの連携方法など、リリースしたからこそ経験できた貴重なお話を聞かせて頂きました。
リリース当初、相当なインパクトがあるため、外部サーバからデータ取得を行なう場合、負荷の分散を検討しなければならない点や、リリース後のちょっとした修正に対応できるよう、設定ファイルの持ち方を検討するなど参考になるお話を幾つも頂きました。
何より驚いたのは、南保さんは学生で、Macユーザになって半年ほどで、iPhoneアプリのリリースまで漕ぎ着けたしまった点に、一同驚きをを隠せませんでした!
また、毎回終了後に飲み会を行なって親睦を深めている効果もあってか、前回以上に活発で、楽しく時間を過ごすことが出来ました。
雰囲気そのままお伝えできないのが残念ですが、一人でふらりと突然やって来ても、きっと良い時間を過ごすことができると思いますので、興味をお持ちの方は、是非足をお運びください。
◆エンドウ ヒデオさんのプロフィール
はじめまして、プログラマをしておりますエンドウと申します。小さな会社〜大きな会社までを経験して、現在はフリーで活動しています。開発に携わったシステムは、発電所制御、鉄工所制御、ウエハー検査機、病院内ネットワーク、飲食店向けなど様々な業種で設計、開発を行なってきました。現在はウェブを中心に開発を行なっています。
なんだか、MacやiPhoneの世界と接点がなさそうな雰囲気ですが、漢字Talk7.5.2の頃から、Macの世界に魅了され現在に至ります。当時はプログラマでも自宅でパソコンを持っている人は少なく、インターネットにやっと接続できるようになった頃で、通信速度同様ゆるやかな時代でした。そんな時代からのMacユーザです。
Macのアプリケーションを何時かは作ってみたいと思いながらも、だいぶ時間が経ってしまいましたが、iPhone&iPod Touchをきっかけにプログラミングを始めてみようと決心した次第です。
そうはいっても、日本語化されたドキュメントも整備されていなかったので、なかなか重い腰が上がらなかったのですが、数ヶ月前に、Dashcodeを使えば簡単にiPhoneサイトを作成できることを知りました。自分のためだけに作った稚拙なものでしたが、通常のブラウザで動作するのとは違った、遠い昔に経験したような「アプリケーションが動作した!」という単純なよろこびがありまし
た。プログラムを作るのが楽しいと思えるなんて何年ぶりだろうと振り返ってしまうほどでした。それからコツコツと機能を増やして公開したところ、びっくりするほど嬉しい反応と感謝の言葉を頂きました。使って頂いた方が、私に気持ちを伝えたくなるなんて、なんだかスゴイことが起きたように思えました。
「MOSA」の言葉の中には「アーティスト」の意味も込められていますが、これは人に見せて初めて成立することのように思います。使って頂いた方が「ちょっとイイね」と思えるような楽しいもの、誰かのもとに届いて心に響いてくれるようなものは、結局のところ、最初の自分の気持ちに帰結するのかなと思います。ミュージシャン、画家など一般的に言うアーティストのインタビューで、「まず自分が楽しむこと」とよく目にします。全く論理的な言葉ではありませんが、経験を通してそれが全てを言い表しているように今は感じられます。沢山の人に評価されるのはちょっと怖い気もしますが、本当に自分が良いと思えるものならば、自分の気持ちがゆっくりと、沢山の方に浸透していくのかなと感じています。ちょっと遠回りしてiPhoneアプリに着手することになりましたが、また新しい気持ちで、今度はiPhoneアプリを沢山の人に届けたいと思っています。そしてアプリケーションを作る喜びを味わいたいと思っています。
最後まで読んで頂きまして、ありがとうございました。
◇MOSAからのお知らせと編集後記は割愛します◇