MOSA Multi-OS Software Artists

MOSAはソフトウェア開発者を支援します

  • iPhone/iPod touch アプリ紹介
  • MOSA掲示板
  • 活動履歴
  • About MOSA(English)

MOSA Developer News[MOSADeN=モサ伝]第307号

2008-07-29

目次

  • りんご味Ruby         第29回  藤本 尚邦
  • 藤本裕之のプログラミング夜話   #142
  • 高橋真人の「プログラミング指南」  第140回

りんご味Ruby   第29回  藤本 尚邦

前回は、HTMLパーサの Hpricot を使って YahooHonyaku#parse_form! を実装し、YahooHonyaku#open_top_page を完成させました。ここで、第24回で作成したtest-yahoo-honyaku.rb を使ってテストしてみましょう。

 

$ ruby test-yahoo-honyaku.rb
 (中略)
   2) Error:
 test_translate(TestYahooHonyaku):
 NotImplementedError: まだ実装してねぇ、誰か書いてくれ!
     ./yahoo-honyaku.rb:32:in `post_text'
 (中略)
 2 tests, 0 assertions, 0 failures, 2 errors

YahooHonyaku#post_text が実装されていないためにエラーになりました。と
いうことで、今回は YahooHonyaku#post_text

 def post_text(mode, text)
   raise NotImplementedError, "まだ実装してねぇ、誰か書いてくれ!"
 end

の中身を実装していくことにします。post_text は、翻訳したいテキスト(text)と何語から何語に翻訳したいか(mode)を引数にとり、Yahoo!翻訳に対してリクエスト(HTTP POST)します。そして、レスポンスのボディ部すなわち翻訳結果を含むHTMLを返します。2つの引数は、どちらも YahooHonyaku#translateの引数をそのまま引き継ぎます。

 (注) translateメソッドの引数の形式(インターフェース)は、別の翻訳システムを使って実装しても同じように使えることを考慮します

text引数は、翻訳したいテキストの文字列です。mode引数は:

 "ab" または :ab  -- A語からB語へ
 "ba" または :ba  -- B語からA語へ


というような形式にすることにします。このような形式では、世界にあるたくさんの言語すべてに対応するのには不十分かもしれませんが、話を単純にするためここは目をつぶってください。

 (注) ここで「”」で囲まれてる “ab” や “ba” は文字列です。一方、「:」で始まる :ab や :ba はシンボル(Symbol)という型(クラス)の値です。ここではシンボルについて詳しい説明は省きますが、Rubyプログラムでは、文字列リテラルを書くような場面(例えばHashリテラルのキーの値など)で、その見た目のシンプルな代用品としてシンボルがよく使われています。

一方、Yahoo!翻訳のHTTP POSTのパラメータを調べると、eidというパラメータに何語から何語への翻訳かを指定するようになっています:

 

eid=CR-JE     -- 日本語から英語へ   (日英)
 eid=CR-EJ     -- 英語から日本語へ   (英日)
 eid=CR-JK     -- 日本語から韓国語へ (日韓)
 eid=CR-KJ     -- 韓国語から日本語へ (韓日)
 eid=CR-JC-CN  -- 日本語から中国語へ (日中)
 eid=CR-CJ     -- 中国語から日本語へ (中日)


mode引数はPOSTするためにeidパラメータの値に変換する必要がありますから、それをprivate なメソッド _eid_ として定義します。

仮に日中変換のeidが他のパラメータと同じように単純に”CR-JC”ならば、(エラー処理のことを考えなければ)場合分けが不要になり、_eid_ の中身は単純に:

 "CR-#{mode.to_s.update}"

だけで済んでしまうところでしたが、残念ながら条件分岐が必要になります。ここでは、case文式を使って “JC” のときの分岐を表現しました。_eid_ の定義は以下のようになります:

 

def _eid_(mode)
   mode = mode.to_s.upcase   # modeを(Symbolなら)文字列化・大文字化
   case mode
   when "JC" then "CR-JC-CN"
   else           "CR-#{mode}"
   end
 end


■ case文式について

もちろん、ここでは、分岐のもっともプリミティブな表現であるif文式を使うことも出来るのですが、あえてcase文式を使いました。Rubyのcase文式は、一見するとC言語のswitch文と似た感じに見えますが、オブジェクト指向言語の特性を活かした高度な仕組みを含んでいます。

case文式は、おおざっぱに以下のような構文になります:

case ある値
when 条件A1, 条件A2, ... then 文式の並びA
when 条件B1, 条件B2, ... then 文式の並びB
...
else                          文式の並びN
end


thenがくるところで改行した場合はthenを省略できます。これをあえて日本語で読み下すと:

・ある値について:
・条件A1または条件A2(または…)にマッチしたら、文式の並びAを実行して、case文式の結果として返す
・条件B1または条件B2(または…)にマッチしたら、文式の並びBを実行して、case文式の結果として返す…
・いずれの条件にもマッチしなければ、文式の並びNを実行して、case文式の結果として返す

となります。

さて、オブジェクト指向言語の特性が活かされているポイントはどこにあるのでしょうか? それは、条件にマッチしたことの判別方法にあります。同じものを、条件分岐のもっともプリミティブな構文であるif文式を使って表現すると、おおざっぱに以下のようになります:

if    条件A1 === ある値 or 条件A2 === ある値 or ... then
 文式の並びA
elsif 条件B1 === ある値 or 条件B2 === ある値 or ... then
 文式の並びB
...
else
 文式の並びN
end


となります。つまり、case文式を日本語で読み下したときの「条件にマッチする」は

 「条件 ==== ある値」が真である

ということになります。この === は「条件」に相当するオブジェクトのインスタンスメソッドになっていて、所属性(という言葉が適切かどうか定かではないのですが)のチェックを意味します。

 (注) – まだ書いてなかったと思うのですが、Rubyでは、C言語で数値や論理 の二項演算子に相当するものの多く(例えば +,-,*,/,== など)が、左辺の値のインスタンスメソッドになっています。

デフォルトの Object#=== は Object#== と同じなので、基本的には、case文式は条件との等値性をチェックということになります。しかし、一部のクラスでは、そのクラスにおける「条件にマッチした」という感覚に見合うように===が再定義されています。そのようなクラスの代表的なものとしては以下のものがあげられるでしょう:

 範囲型(Rangeクラス)         – 範囲.include?(ある値)
 モジュール型(Moduleクラス)  – ある値.is_a?(モジュール)
 正規表現(Regexpクラス)      – 正規表現 =~ ある値

このような性質を使って、例えば以下のようなcase文式を書くことができます:

def check_a_value(a_value)
 case a_value
 when "hello" then '"hello"という文字列でした'
 when /world/ then '"world"を含む文字列でした'
 when String  then '文字列でした'
 when 12345   then '12345という整数値でした'
 when 0..100  then '0以上100以下の数値でした'
 when Integer then '整数値でした'
 end
end


説明が込み入ってしまったため複雑に感じられてしまったかもしれませんが、このような仕組みが背景にあって、case文式は高度な条件式をシンプルに書くことが出来るようになっているわけです。

今回は、途中からcase文式の説明になってしまいましたが、次回、引き続きpost_text の実装をする予定です。

藤本裕之のプログラミング夜話 #142

 承前、iTunes Storeの売り上げばかりが喧伝されている感のある音楽のダウンロード販売だが、その商売の大部分を占めるのは実はケータイ電話への着うたであってコンピュータやiPodない。しかもコンピュータiPodにダウンロードするヒトタチ(例えばオレのことだけど)と違い、この着うたをダウンロードするヒトタチはそれを「とっておく」ことをしないで(その方法もない
んだけどさ)惜しげもなく捨ててしまうのだ、というトコロまでハナシは進んでいたと思う。

 ほんで何が言いたいかというと、これは音楽という「ソフトウエア」の消費の仕方に訪れた「変化」なんだということなのである。何度も書くがオレのような古い……いいたかないが古いニンゲンは、音楽をCDとか、ダウンロードしてもハードディスクだのCD-Rだのというメディアに保存して「所有」する、という「消費」をしてきた。高校生のとき3個食いたい昼飯のパンを2個にして
キース・ジャレットのレコードを買ったりしたのである。わかるでしょ?

 もちろん「所有せずに消費する」方法だっていくらもあった。音楽なんてラジオでも聴けるし、飲み屋やパチンコ屋から有線にリクエストすることだってできる(これ、やった経験のある人わりと少ないので驚くのだが)。でもオレの学生時代から流行り出した「貸レコード屋」の店頭にカセットテープが山積されていたことからも解るように、消費形態の主流はあくまで「所有」だった
はずだ。貸レコード屋でレコード借りて、ダビングもしないで返してたヒトなんて……オトモダチにいましたか? なのに今の若いヒトはお金を払って音楽を「聞き捨て」るのである。しかもあ〜た、着うたフルってiTunes Storeで買うより高いンだぜ。

 こんなハナシもある。こないだ新聞で読んだのだが、確かにここ数年音楽CDの売り上げは落ちていて、製作会社をから街のレコード……いやCD屋さんまで大変経営が苦しくなってきている。が、では音楽産業全体がダメなのかというとそうではなく、特にライブ、コンサートなどの売り上げは増加傾向にあるというのだ。そう言われてみればコンサートなんて所有できない音楽消費形態の最たるもんではありませんか。

 こういう傾向がなぜ生まれたのか。オレは経済学者でもなければ心理学者でもないので難しい理論はわからないが(そんでそういうのって説明されてもいつもなんか納得がいかないんだが)、音楽に関して言えば未だにiTunes StoreよりもCDを購入するほうを選ぶオレが、他のモノでは確かにそういう消費形態になりつつあるのだった。うんうん確かに似てるのだ。どーゆーことかと言う
と、オレはこのところほとんどDVD、すなわち映像ソフトを買わなくなったのである。なるほど、つーことは、オレ自身の行動のココロを探れば、この変化について何か解るかも知れぬ。

 というわけで自問してみる。なぜ最近オマエは以前ほどDVDを買わないのか?……第一の理由はもちろんビンボーだからのような気がするが、そんぢゃ以前はビンボーぢゃなかったかというと、以前もやっぱりビンボーだったよな気がするのでそれはあんまり関係ないだろう。

 ほんぢゃぁ先ほどからキーワードになっている「所有」というトコロから攻めてみよう。買わなくなったのは「所有」したいような映像ソフトが少なくなったから? そう訊かれて(自分にだけど)自分のDVD棚を改めて見返す。ここに並んでいるモノすべて「所有欲を刺激されて買った」モノか? 確かに中には手塚治虫アニメラマ三部作とか岡本喜八「大誘拐ーrainbow kids」みた
いに「これは持っとかなくちゃ」と買ったものもないではないが、「ローズ家の戦争」とか「エド・ウッド」とかもそうなのかと訊かれるとなぁ。それも違うような気がする。そうそう、一番最近買ったのは「チャイルド・プレイ:チャッキーの種」で、これは公開時に映画館で観ているのだが、ここまでのシリーズ全部をDVDで持ってる関係で「これは買っとかないと」だったのだ。な
にしろあの殺人グッドガイ人形のチャッキーに二世ができるというハチャメチャな話で……。

 おっと、ハナシがちとあらぬ方向に逸れた。てなぐあいであれこれ考えてみた結果、ワタシがたどり着いた結論はなんつか「所有してなくてもそう苦労せずに手に入る、というか、観ることができるようになった」というあたりに落ち着いたのである。つまりは視聴できるTV局が増え、観たいものはどっかで放送してくれるぢゃないか、ということなんだが……。
(以下次回 2008_07_26)                       

高橋真人の「プログラミング指南」第140回

プログラマのためのオブジェクト指向再入門(46)

〜XcodeによるPowerPlant X入門(29)〜

 こんにちは、高橋真人です。
 さて、前回までに完成させた「自前で作ったボタンコントロール」に、さらに拡張を施してみたいと思います。ただ、サブクラス化することで拡張するのでは前にやったのと同じで面白くありませんから、今度はAttachmentという仕組みを使ってやることにします。
 Attachmentというのは、簡単に言うと「既存のオブジェクトに動的に挙動などを付け加えたり外したりできる仕組み」です。オリジナルのPPをご存知の方ですとLAttachmentというクラスがあったのを覚えておいでかもしれませんが、考え方はあれと同じです。
 ただ、PPxにおけるAttachmentはCarbonイベントの仕組みの上に成り立っています。そのため、オリジナルのPPにおけるLAttachmentに比べるとクラス自体の実装は極めて簡素になっています。(LAttachmentほどのきめ細かい制御はできないように見えます。)
 Attachmentにおいて重要なのは、アタッチされる相手のオブジェクトであるPPx::Attachableが多くのクラスの継承元になっていることです。ですから、ほとんどのオブジェクトに対して、特にそのオブジェクト自体に手を加えることなく“動的に拡張を施せる”というところがポイントです。

 それでは早速コードです。例によって、以前のプロジェクトを複製しPPxForXcode10を作ってください。
 最初に既存のクラスに対する修正です。もちろん、MyView自体は変更しません。
 まずは、Application.cpです。先頭のヘッダファイルをincludeしている末尾に、

#include "MyViewAttachment.h"

と加えます。
 次にDoSpecificCommand()の中でViewを生成し終わったあと(view>SetFrameAdapter(adapter);のあと)に以下の4行を加えます。

view->SetID(100);

MyViewAttachment *attachment = new MyViewAttachment;
attachment->Initialize(view);
view->AddAttachment(attachment);


 次はMyWindowです。MyWindowはちょっとした新たな挙動を加えますので、ヘッダもソースも両方直します。まずはヘッダファイル(MyWindow.h)から。 include部の末尾に、

#include 

と加えます。次に、MyWindowの継承元にWindowDeactivatedDoerを追加します。よって、クラスの冒頭部は以下のようになります。

class MyWindow :
   public PPx::Window,
   public PPx::SpecificMenuCommandDoer,
   public PPx::WindowDeactivatedDoer
{
...


 あと、DoSpecificCommand()の次に(protected:部)、以下のメンバ関数宣言を追加します。

virtual OSStatus DoWindowDeactivated(
                   PPx::SysCarbonEvent&    ioEvent,
                   WindowRef               inWindow);


 今度はソースファイル(MyWindow.cp)です。include部の末尾に以下を加えます。

#include 
#include "MyView.h"
#include "MyViewAttachment.h"


 それから、ヘッダで宣言した新しいメンバ関数の実装です。

OSStatus
MyWindow::DoWindowDeactivated(
   PPx::SysCarbonEvent&    ioEvent,
   WindowRef               inWindow)
{
#pragma unused (ioEvent, inWindow)

   MyView *view = PPx::SafeDynamicCast(
                       GetContentView()->FindViewByID(100));

   MyViewAttachment *attachment =
       PPx::SafeDynamicCast(
       view->FindAttachmentByID(kMyViewAttachment));
   if (attachment) {
       view->RemoveAttachment(attachment);
       delete attachment;
   }

   return noErr;
}


 それと、FinishInit()の末尾に新しいEventDoerクラスのInstallを忘れないように加えます。ちなみに、PPxでコーディングしているとついこのInstall部を書くのを忘れて「あれ、動かない。どうしてだ?」と悩むことが多いので、常にこれには注意しましょう。

PPx::WindowDeactivatedDoer::Install(targetRef);

 それでは最後にアタッチメントクラスの全体です。少し長いですが一気に行きます。

====================== MyViewAttachment.h =========================
#pragma once

#include 
#include 

const UInt32 kMyViewAttachment = 'MVAt';

class MyViewAttachment :
   public PPx::TargetAttachment,
   public PPx::ControlDrawDoer
{
public:
                   MyViewAttachment();
   virtual         ~MyViewAttachment();

   void            Initialize(PPx::EventTarget* inTarget);

protected:
   virtual OSStatus DoControlDraw(
                       PPx::SysCarbonEvent&    ioEvent,
                       ControlRef              inControl,
                       ControlPartCode         inPartCode,
                       RgnHandle               inClipRgn,
                       CGContextRef            inContext);

private:
   void                FinishInit();
   virtual CFStringRef ClassName() const;

   PPx::SysEventHandler    mEventHandler;
};

====================== MyViewAttachment.cp =========================

#include "MyViewAttachment.h"

#include 
#include 

MyViewAttachment::MyViewAttachment()
{
   SetID(kMyViewAttachment);
}

MyViewAttachment::~MyViewAttachment()
{
}

void
MyViewAttachment::Initialize(PPx::EventTarget* inTarget)
{
   SetEventTarget(inTarget);
   FinishInit();
}

void
MyViewAttachment::FinishInit()
{
   EventTargetRef target = GetEventTarget()->GetSysEventTarget();

   if (target) {
       EventHandlerRef handler = PPx::ControlDrawDoer::Install(target);

       mEventHandler.Adopt(handler);
   }
}

CFStringRef
MyViewAttachment::ClassName() const
{
   return CFSTR("MyViewAttachment");
}

OSStatus
MyViewAttachment::DoControlDraw(
   PPx::SysCarbonEvent&    ioEvent,
   ControlRef              inControl,
   ControlPartCode         inPartCode,
   RgnHandle               inClipRgn,
   CGContextRef            inContext)
{
#pragma unused (inPartCode, inClipRgn)

   OSStatus err = ioEvent.CallNextHandler();

   if (err == noErr) {
       PPx::CGContextSaver saver(inContext);

       HIRect frame;
       ::HIViewGetBounds(inControl, &frame);
       frame = ::CGRectInset(frame, -2, -2);

       float alpha = 0.5;

       if (::IsControlHilited(inControl) and
               ::IsControlActive(inControl)) {
           alpha = 0.8;
       }

       ::CGContextSetRGBFillColor(inContext, 0.0, 0.0, 1.0, alpha);
       ::CGContextFillRect(inContext, frame);
   }

   return err;
}


 以上です。以下は簡単な動作の説明です。
 新しいウインドウを開くとボタンがありますが、そのボタンには青い“覆い”がかぶさっています。クリックしてアラートを出すか、さらにウインドウを作るなどして、最初のウインドウがディアクティベートされる(アクティブでなくなる)と、青い覆いは消え去ります。

◇MOSAからのお知らせと編集後記は割愛します◇

 

 MOSA Developer News   略称[MOSADeN=モサ伝]
        配信停止 mailto:mosaden-ml@mosa.gr.jp
 記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
      記事投稿受付 http://www.mosa.gr.jp/?page_id=850
Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等
はそれぞれ各社の商標ならびに登録商標です。
このメールの再配信、および掲載された記事の無断転載を禁じます。
特定非営利活動法人MOSA  http://www.mosa.gr.jp/
Copyright (C)2007 MOSA. All rights reserved.