2008-06-10
目次
りんご味Ruby 第26回 藤本 尚邦
テストプログラム test-yahoo-honyaku.rb を実行すると top_page_opened? が実装されていないためにエラーとなりました。そこで今回は YahooHonyakuクラスのインスタンスメソッド top_page_opened? の実装から始めます。
YahooHonyaku#top_page_opened? は、YahooHonyakuオブジェクトが「ヤフー翻訳のトップページをすでにGETしているかどうか?」の真偽値を返します。実装は以下のようになります:
def top_page_opened?
@top_page_opened ? true : false
end
3項演算子「?」を使って、インスタンス変数 @top_page_opened が真ならtrue を 偽なら false を返しています。
■ インスタンス変数
Rubyでは、名前の先頭に「@」をつけることによりインスタンス変数を表します。@top_page_opened は YahooHonyakuクラスのインスタンス変数ということになります。インスタンス変数を宣言する必要はなく(そもそも変数を宣言するための特別な構文がありません)、上の @top_page_opened の様にいきなり使うことができます。初期値は nil です。
オブジェクトの生成時にインスタンス変数に初期値を与えたい場合には、オブジェクト生成直後に必ず呼ばれるインスタンスメソッド initialize の中に実装するのが良いでしょう(例):
def initialize
@top_page_opened = false
end
インスタンス変数はオブジェクトの外に対して非公開(private)です。外からインスタンスの値にアクセスしたい場合はアクセッサとなる公開メソッドを用意する必要があります。
■ Rubyの真偽値
if、unless、while、until、3項演算子の「?」など条件分岐のある式において、条件節の真偽は以下のように判定されます:
偽 — false または nil
真 — false でも nil でもない全ての値(つまり偽でない全ての値)
もちろん0を含めて全ての数値も真となります。Cプログラマの方々、0は偽ではなく真ですよ、ご注意ください。
■ メソッド名の最後についている「?」は何?
Rubyでは、メソッド名の最後に「?」と「!」を使うことができます。これは単にメソッドの名前に使えるという以上の意味は持ちません。慣習として、「?」で終わる名前は真偽値を返すメソッドに、「!」で終わる名前はオブジェクトの状態を変える破壊的メソッドによく使われます。しかし、これはあくまでも慣習であって、そのメソッドに特別な意味を与えたり振る舞いに影響したりするわけではありません。
■ メソッドの返す値
メソッド実行時に最後に評価された結果の値がメソッド実行の結果となります。ですから top_page_opened? は、@top_page_opened の値の真偽により true または false を返します。
■ 補足 — returnについて
ところで、C言語系統のプログラマの方は top_page_opened? の中で、returnが使われていないことにむず痒さを感じませんでしたか?
def top_page_opened?
return @top_page_opened ? true : false
end
あるいは
def top_page_opened?
if @top_page_opened
return true
else
return false
end
end
のように書きたいなど…この場合(単純なメソッド定義)に関しては、return を使おうが使うまいがおそらく期待したとおりに動きます。動くのですが、私の経験上、必要のないreturn式は使わないことをおすすめします。使う場合には、return式の仕様(C言語ほど単純にはすまない)をしっかりと理解していないとドツボに嵌まる可能性があります。
Rubyを使い込んでくると、手続きオブジェクト(あるいはブロック)を値として扱う機会が増えてくるはずです。手続きオブジェクト(あるいはブロック)は、プログラムの部分を値として扱うというような意味を持ちます(Lispでのlambda式に相当)。
手続きオブジェクト(あるいはブロック)の手続き本体が実行されるときに、その中で使っているreturn式が「何から」戻るのかというのを考えてみてください。その手続きから戻るのか? あるいは手続きを定義している外側のメソッドから戻るのか?そもそも戻りたいときに外側のメソッドは存在するのか? などなど…
return式には、プログラマが無意識に期待する振る舞いと仕様(=処理系実装)との間に、気付きにくい違いが出てしまうことがあります(私自身が何度かはまっています)。Ruby処理系の開発チームでもこのことは気にしていて、どうすべきか思案していたようなので、Ruby 1.9 では何らかの変更が入っているかもしれません(未確認)。ですが、return 式の仕様をきっちり理解するまでは、必要なとき以外returnを使わないような習慣をつけておいた方がよさそうです。
なんだか煙に巻くような説明になってしまいましたが、いずれ手続きオブジェクトの話を書く機会がきたら、そのときに、1.9の仕様を確認した上でもう少しきちんと説明したいと思います。
■ 今回のコードをテストしてみる
説明が長くなってしまいましたが、今回書いたコードをテストしてみましょう。
$ ruby test-yahoo-honyaku.rb
(中略)
1) Error:
test_s_translate(TestYahooHonyaku):
NotImplementedError: まだ実装してねぇ、誰か書いてくれ!
./yahoo-honyaku.rb:15:in `open_top_page'
(中略)
2 tests, 0 assertions, 0 failures, 2 errors
となりました。ということで次回は open_top_page を実装することになりそうです。
■ まとめ
・先頭に「@」のついた名前がインスタンス変数を表す
・メソッド名の最後に「?」と「!」を使うことができる
・慣習として「?」で終わる名前は真偽を返すメソッド名に使われる
・慣習として「!」で終わる名前は破壊的なメソッド名に使われる
・不要なreturnは使わない習慣をつけよう
藤本裕之のプログラミング夜話 #139
前回、「バンドルソフト」というものの数々の利点、メリットを並べ立ててみたわけであるが、忌野清志郎が歌うように世の中「いいことばかりはありゃしねぇ」。光りあるところに影があるというか、広い目長い目にゃんこの目で観ると(にゃんこの目は関係ないか)それほど手放しで喜べることばかりではない、なかったというところを挙げてみたい。
誰しもがまず考えつくであろう問題は「ハードのおまけのソフトは『おまけ』品質だと思われてしまう」ということだろう。われわれソフト業界の人間から見れば、ハードメーカーがあるソフトウエアを自社製品にバンドルしようと思うのは、その製品が他者の同種のソフトより勝っている(まぁそれが性能ではなく値引きその他の営業努力に負うものである場合があるとしても、だ)からだ、と思われる。思ってたでしょ?
ところが世間一般のモノの見方というのは違うのである。というか、我々だって自分の業界を離れれば違う常識の中で生きているんだよね。例えば「グリコ」のおまけに入っているおもちゃはどう考えても店で単体で売ってるおもちゃと同等ではない。「りぼん」の付録のなんとかちゃんパスケースは、たしかにビニールで作ってあるオリジナル・デザインだけれども耐久性に乏しくすぐ破ける。小学館の学習雑誌の組立付録、「懐中電灯で楽しめるスライド幻灯機」は相当しっかり作らないと中にセットする懐中電灯の重みで自壊した。だって厚紙なんだもの(笑)。
ワタシが子供のころカルビーがカードのおまけがついた「仮面ライダースナック」というのを流行らせて、そのカード欲しさにこれを買ってお菓子の方を食べない子供がいると問題になったことがあった。……たしかにあれは美味しくなかった。子供心に「これがえびせんだったら売り上げが倍増するだろうにカルビーはバカだ」と思ったのを覚えてる。けどさ、それでも店でカネを払ってカードだけ貰ってくる子供はいなかった。やっぱり「おまけはおまけ」なんですわ。
総括する。すくなくとも20世紀後半から21世紀初頭、すなわち現在に至るまでの世間的常識では、なんかの商品を購入した場合に「おまけ」として添付されてくるものはそのためにわざわざ制作されたものであり、故に投下されているコストもおのずと知れている。すなわち結論として、それ単体で商品として流通するだけの価値を持たない品質だ、とこういうことになっているのである。
ここでわれわれは当然、ソフトウエアの場合は全く違って、ただ流通している商品をコピーすればいいのであり、わざわざ品質の劣る「おまけ用」を作るほうがバージョン管理、ユーザサポートなどの面でコスト高なんである、と反論したくなるわけだが、それがまた落ち込んだ穴の中でさらに墓穴を掘るように作用してしまったのである。
「ほら、奥さん。ソフトウエアはちゃうんです。このソフトは確かにおまけやけど、キレイな箱とか印刷されたマニュアルがないだけで、店で売ってるのと同じように使えますんや」
「あらほんまやわ。そゆこと、なんで早う言うてくれんの。ソフト屋さんっていけずやわ」
と、納得してくれるようなユーザは、「ソフトウエアっておまけでもちゃんとした商品」と思ってくれる代わりに「ソフトウエアっておまけに付いてくるやつで十分」と学習してしまう。これを難しい言葉で言うと(たまには難しい言葉を使わないとまじめな論考であると思ってもらえないからな)、こんな風にバンドルソフトというのは「市場におけるソフトウエアのハードウエアから
の独立性を阻害する」という側面を持っていた、と言えるわけだ。
しかもこの事態は、遠い昔、メインフレームやミニコンを企業に納入する際にその費用の内訳におけるハードとソフトの割合を調整したというたぐいの話ではない。ユーザーの側が、自分の払ったお金はすべて「コンピュータの対価」と思ってるのはあの頃と同じだが、今度の場合は文字通りホントにその通りなのである。下手をするとバンドルソフトの作り手への分け前は、「グリコ」のおまけを作ってる業者より少ないのではなかろうか。
でもバージョンアップが美味しいんだからいいぢゃん、と前回本稿もお読みになったマメなあなたは言うかもしれない。確かにその筈でした。……でもそういう「損して得取れ」みたいな悠長な時代は20世紀と共に終わってしまったのである。しかもおそらくはこの業界の人が誰も予想しない(少なくとも予想していた人をオレは知らない)形で。では次回はその話。
(以下次回 2008_06_06)
高橋真人の「プログラミング指南」第137回
〜XcodeによるPowerPlant X入門(26)〜
こんにちは、高橋真人です。
さて、MyViewが継承しているPPx::PushButtonがどのようにしてボタンコントロールを描いていたかが分かったところで、MyViewの本来の目的であるPushButtonのカスタマイズに取りかかりましょう。
まずは、今までやっていたことの延長線上ということで、見た目についてのカスタマイズをやってみます。せっかく、PushButtonが描いてくれるボタンの外見があるのですから、これを生かした形での拡張をしてみます。
それでは、例によってコードを。プロジェクトは前のを複製してPPxForXcode08としてからやってください。今回の修正個所はわずかです。MyView.cpのDoControlDrawメンバ関数の実装のみです。以下の通りに書き換えます。
OSStatus
MyView::DoControlDraw(
PPx::SysCarbonEvent& ioEvent,
ControlRef inControl,
ControlPartCode inPartCode,
RgnHandle inClipRgn,
CGContextRef inContext)
{
#pragma unused (ioEvent, inControl, inPartCode, inClipRgn)
ioEvent.CallNextHandler();
PPx::CGContextSaver saver(inContext);
if (IsControlHilited(inControl) and IsActive()) {
CGContextSetBlendMode(inContext, kCGBlendModeColorDodge);
HIRect frame;
GetLocalFrame(frame);
CGContextSetRGBFillColor(inContext, 1.0, 0.0, 0.0, 0.5);
CGContextFillRect(inContext, CGRectInset(frame, -2.0, -2.0));
}
return noErr;
}
さっそく走らせてみましょう。ちなみに今回使用しているCGContextSetBlendMode()が10.4以降でしか使えないので、10.3では動かせません。
さて、走らせてみてどうでしたか?
ボタンをクリックした時に、通常青っぽくハイライトするはずが赤っぽくなるのが確認できましたでしょうか。
簡単にコードを解説します。
まず中ほどのif文ですが、この部分はコントロール(View)がハイライト状態になっているかどうかを判定して、もしハイライト状態であればボタンの上に赤い四角を描いています。CGContextSetBlendMode()という行をコメントアウトして走らせてみると、単純にボタンの上に透明な赤い四角が描かれているのが分かります。ColorDodgeというブレンドモード(表示を重ねた時の表現の仕方)にすることでボタンコントロールの表示部分(つまり、白くない部分)が赤い色の影響を受けて染められることになります。染まり方がちょっと怪しいですが(笑)、まあその辺は気にしないということで…
それから、関数冒頭のioEvent.CallNextHandler()というのが、表示部分のポイントです。この部分が、正に前回の結果を受けて「ボタンコントロールの表示を“先に”行っている」部分です。
前回までは関数の最後でeventNotHandledErrを返すことで、継続してドローイベントを後続のハンドラに渡していました。ですが、ハンドラの処理においては後続するハンドラを“先に”処理させたい場合があるわけです。正に今回のようなケースです。そういう場合のためにCarbonイベントには「次のハンドラを呼ぶ」というやり方があります。
Carbonイベントのプログラミング経験のある方なら必ずイベントハンドラを一度は書いたことがあるはずでしょうが、このイベントハンドラの第一パラメータにあるCallEventHandlerRefというものが何なのかが気になったことはないでしょうか?
このパラメータは、正にこのためだけに用意されていると言ってもよいものです。この引数を使ってCallNextEventHandler()という関数を呼び出すことで、通常は“後に”呼ばれるはずのイベントハンドラを“先に”呼び出すことができるのです。
PPxの場合、イベント絡みの情報は処理する関数(今回はDoControlDraw)に直接渡されるのではなく、ioEventという名前のSysCarbonEventクラスのオブジェクトの中にまとめられた形で渡されてきます。ですから、このオブジェクトのCallNextHandler()というメンバ関数を呼び出すことで同様のことが行われる形になっています。
ところで、最初に説明したコントロールがハイライト状態になっているかを判断するif文のところで「コントロールがアクティブであるか」を併せて判断しているのは、この条件判断を外してためしてみれば分かりますが、ボタンをクリックしてアラートが表示された際に、コントロールのハイライト状態が残ってしまうからです。(andというのは、C++における&&演算子の別名表記で
す)
次回からはさらに細かいカスタマイズに取り組んで行きたいと思います。
◇MOSAからのお知らせと編集後記は割愛します◇