MOSA Developer News[MOSADeN=モサ伝]第281号
2007-12-25
目次
- りんご味Ruby 第16回 藤本 尚邦
- 藤本裕之のプログラミング夜話 #129
- 高橋真人の「プログラミング指南」 第127回
- 開発ツールよもやま話 Xcodeのコード補完
りんご味Ruby 第16回 藤本 尚邦
最近、1万個弱のHTMLファイルの中のscriptタグで埋め込まれたJavaScriptプログラムのバグをまとめて修正するという作業をしました。このときに初めて使ったHpricotがなかなか便利。ということで、今回はHTMLパーサライブラリのHpricotについて紹介しようと思います。
この修正作業には、おおよそ以下のようなRubyプログラムを書いて一気に書き換えました。
require 'rubygems'
require 'hpricot'
require 'fileutils'
Dir["#{HTML_DIR}/**/*.html"].each do |path|
modified = false
hdoc = Hpricot(File.read(path))
hdoc.search("script").each do |script_elem|
if has_bug?(script_elem.inner_html)
script_elem.inner_html = fix_bug(script_elem.inner_html)
modified = true
end
end
if modified
FileUtils.mv(path, "#{path}.backup")
open(path,"w"){|f| f.write(hdoc.to_original_html) }
end
end
このプログラムの手順を日本語で書き下すと:
・HTML_DIR以下のすべてのHTMLファイルについて:
・Hpricotでパース
・全てのscript要素を検索
・各script要素について:
・inner_htmlにバグがあれば
・修正して書き換える
・バグ修正した場合:
・オリジナルをバックアップ
・ファイルを更新
となります。(余談 – この種の書き捨てプログラムの実行は取り返しがつかないことにならないようデータのバックアップをとって慎重にやらなくてはいけませんね。LeopardならTime Machineの使いどころでしょうか?)
上のプログラムで、Hpricotがどのように使われているかに着目してみましょう。
まず
require 'rubygems'
require 'hpricot'
でHpricotをロードしています。LeopardにはデフォルトでHpricotがインストールされています。次に
hdoc = Hpricot(File.read(path))
で、HTMLを読み込んでHpricotでパースして、結果のオブジェクトにhdocという名前を付けました。
hdoc.search("script").each do |script_elem|
...
end
では、HTMLドキュメントの中のすべてのscript要素を検索して、それぞれについて処理をするループを構成しています。ループの中では、各script要素について:
script_elem.inner_html
で、script要素のinner_htmlを取り出します。バグがあれば修正して
script_elem.inner_html = 修正したHTML片
のように、script要素のinner_htmlを修正後の内容に書き換えます。script要素のループから抜けたとき、バグ修正が発生していた場合は、修正後のHTMLデータ
hdoc.to_original_html
をファイルに書き出します。to_original_html メソッドは、リファレンスマニュアルによると、*修正したところ以外は*もとのHTML文字列を返すメソッドです。完全なオリジナルのHTMLを返すわけではないのですね。
以上のような感じでHpricotを使い、シンプルに目的の作業を実行するプログラムを書くことができました。
上のプログラム例はそのままでは試すことができないので、そのかわり、実際に動かせる例として、Googleを検索して、Hpricotを使って検索結果からタイトルとリンクだけを抽出するRubyプログラム(コマンド)を書いてみました。
------------------------------------------- google.rb --
#!/usr/bin/ruby -Ku
class String
require 'nkf'
def u8() NKF.nkf("-w", self) end
end
class Google
require 'rubygems'
require 'hpricot'
require 'open-uri'
require 'cgi'
URL_FORM = "http://www.google.com/search?q=%s&hl=ja"
def self.search(*keywords)
keywords = keywords.map{ |kw| CGI.escape(kw) }.join('+')
url = format(URL_FORM, keywords)
hdoc = open(url) { |io| Hpricot(io) }
hdoc.search("div.g").map { |elm| new(elm) }
end
def initialize(element) @element = element end
def title() @element.search("h2")[0].inner_text.u8 end
def link() @element.search("h2/a")[0].attributes["href"] end
end
Google.search(*ARGV).each do |i|
puts i.title
puts i.link
puts
end
-------------------------------------------
このプログラムをgoogle.rbという名前で保存し、ターミナルで
$ ruby google.rb macbook ssd
$ ruby google.rb 初音ミク ニコニコ動画
のように検索語を並べて実行すると、上位数件の検索結果のタイトルとURLが空行区切りで出力されます。出力の加工やパースの工夫次第でおもしろい使い方ができそうですね。
Hpricot作者のwhy the lucky stiffさんはHpricotの他にもいろんなプログラムを発表しています。私は2006年のRails Conferenceに参加したとき、whyさんと会ったことがあるのですが、夜の部では、ビデオ・アニメ・楽器などを駆使したひとくせあるライブパフォーマンスを行うなど、まさにアーティストというような活躍をされていました。きさくで陽気でとても楽しい方です。
Hpricot
http://code.whytheluckystiff.net/hpricot
Hpricotのリファレンスマニュアル
file:///usr/lib/ruby/gems/1.8/doc/hpricot-0.6/rdoc/index.html
why the lucky stiff さんのソフトウェア作品
http://code.whytheluckystiff.net/
藤本裕之のプログラミング夜話 #129
回を重ねてこの連載も129回……ちとキリは悪いが(128の方がキリがいい,よな?)、今回を「第三部:青春群像編」……ぢゃなかった「ツールバー編」の締めくくりとしたい。話題はツールバーの「表示モード」と「サイズモード」である。
前回までで我々が(実はオレがだけど)作ってきたテストプログラムが表示するツールバーは、それぞれのアイテムのアイコン(ポップアップボタンもあるけど)の下にそれぞれラベルとして設定した文字列が表示されている。このアプリに限らず、たとえばこの原稿を書いているJeditのツールバーもそう、Finderのウインドウもそうだ。
このツールバーを隠すには、前にやったようにウインドウの右肩部分にあるツールバースイッチをクリックすればいい。で、これをコマンドキーと共にクリックすると表示モード、サイズモードを切り替えることができる。知らなかった人はやってみてくだされ。
ツールバーの表示モード、並びにサイズモードは、NSToolbar.hに以下のように定義されている。
typedef enum {
NSToolbarDisplayModeDefault,
NSToolbarDisplayModeIconAndLabel,
NSToolbarDisplayModeIconOnly,
NSToolbarDisplayModeLabelOnly
} NSToolbarDisplayMode;
typedef enum {
NSToolbarSizeModeDefault,
NSToolbarSizeModeRegular,
NSToolbarSizeModeSmall
} NSToolbarSizeMode;
個々の説明は必要ないよね。で「コマンドキー押しながらツールバースイッチをクリック」でこれらが以下の順番で変わるわけだ。
最初:NSToolbarDisplayModeIconAndLabel で NSToolbarSizeModeRegular
1回:NSToolbarDisplayModeIconAndLabel で NSToolbarSizeModeSmall
2回:NSToolbarDisplayModeIconOnly で NSToolbarSizeModeRegular
3回:NSToolbarDisplayModeIconOnly で NSToolbarSizeModeSmall
4回:NSToolbarDisplayModeLabelOnly で NSToolbarSizeModeRegular
5回:NSToolbarDisplayModeLabelOnly で NSToolbarSizeModeSmall
6回:最初に戻る。
もしアプリケーションの起動時、あるいは特定のウインドウを開くときに、これらのモードを特定のものにしたければ、NSToolbarのオブジェクトに対してsetDisplayMode: とsetSizeMode:で好きなものを設定する。それをしなければこれらの初期値は「NSToolbarDisplayModeDefault」と「NSToolbarSizeModeDefault」であり、これらは前回アプリケーションが終了したときの値(最初に起動されたときは上にあるようにNSToolbarDisplayModeIconAndLabel で NSToolbarSizeModeRegular)になる。
「~/Libraries/Preferences」にある初期設定ファイルを開くと「NSToolbarConfiguration_MOSA_SAMPLE」というキーで参照できるDictionaryとして保存されているのでご確認を。
ここまでの解説に使ったテストプログラムはソース、nibファイルなど一そろいにしてMOSAのサイトに公開してもらうので、興味や疑問がある人はダウンロードしてみてください。そんでは今年も一年、ご愛読ありがとうございました。皆さん、よいお年を。
(2007_12_21)
◇編集部から◇
今回のサンプルプログラムはMOSAのwebサイトからダウンロード可能です。
http://www.mosa.gr.jp/?p=1475
高橋真人の「プログラミング指南」第127回
プログラマのためのオブジェクト指向再入門(35)
〜XcodeによるPowerPlant X入門(18)〜
こんにちは、高橋真人です。
まずは訂正です。前回の説明で一カ所指示が抜けていました。
PPx::BaseViewをMyViewというサブクラスに変えた段階で、Viewの生成コードも差し替えなければならなかったのですが、その指摘を忘れていました。
MyApplication.cpのDoSpecificCommand()の中で、前回追加した部分の最後、
PPx::BaseView *view = PPx::CreateView
を
MyView *view = PPx::CreateView
と変更する必要があります。あと、ソース冒頭に加えた
#include
は
#include "MyView.h"
と変更してください。まあ、このくらいの変更は言われなくても分かった方は大勢いらしたかもかしれませんが。戸惑われた方はすみませんでした。
さて、それではコードの解説をしておきましょう。
今回のプログラムは真っ黒な四角が1つ表れるだけの余り面白みのないものです。ですが、ただの黒い四角に見えてもこれはれっきとしたHIViewですから、ここを起点にしていろいろな挙動を加えていくことが可能です。今のうちのシンプルな段階で構造を確実に理解しておくと、あとがラクになると思います。
では、まずはMyViewです。
このViewはPPx::BaseViewから継承して作ったサブクラスです。このBaseViewというクラスが、HIViewにおける“Viewのカスタマイズ”にかかわる面倒な部分、特に“Cを使ってオブジェクト指向を実現”したためにいくぶん無理の出ているところをうまく吸収してくれているのです。
従って、PPxを使う限りにおいてはHIViewの拡張も特に難しいことを考えずに、あたかもHIView自体も最初からC++で作られたフレームワークの一部であるかのような感覚で作業していくことができるようになっています。
さて、BaseViewは前回の説明でも見ましたように、それ自体だけでは目に見えるようになりません。そのままでもHIViewとしては完全なものであることに疑いの余地はないのですが、ただ単に「そこにあるだけ」では面白くも何ともありませんね。
そこで、今回作ったMyViewではとにかく表示するようにしました。それには、PPxに用意されている「Viewに振る舞いを加える仕組み」を使っています。
この表示するための振る舞い以外にも、BaseView(に限らずHIViewを引き継ぐすべてのView)に新たな振る舞いを加えるためのたくさんのクラスがPPxにはあらかじめ用意されています。大掛かりで複雑な仕組みのように見えてしまうかもしれませんが、構造を理解すると、そんなに複雑ではないことがわかるはずです。
これらのクラスがやっているのは、単にHIView(昔の呼び方ではControl)に関係するCarbonイベントを処理するためのハンドラを付加することだけです。それがCarbonイベントの種類の数だけ用意されていると考えてください。
その一覧は、PPxViewEvents.hというヘッダファイルの中にあります。もっとも、厳密にはすべてのViewのCarbonイベントをカバーしているわけではなく、実際細かく見てみると足りないものがあることに気付くと思います。ただ、それらは滅多に必要となることのないものですので、既存のものでまず不足することはありません。まあ、それでもどうしても必要になった場合には、自分で簡単に補うことができますので心配はいりません。
では、動作の仕組みを理解するために、今回のプログラムで使用している
PPx::ControlDrawDoerを調べてみましょう。
クラスの定義部分(PPxViewEvents.h:96行目から)を見ていただくと分かるように、このクラスは少し前に解説したコマンドの処理をするクラスと極めて似た形になっています。何よりControlDrawDoerが直接継承しているSpecificEventDoerというクラスは、連載の114回目でCommandProcessDoerが引き継いでいるクラスとして登場したので憶えておいでかもしれませんね。
実際、ControlDrawDoerの構造を見てみると、CommandProcessDoerと構造が全く同じであることが分かります。HIViewにおいても動作の中心にあるのはCarbonイベントなのでこれは当然のことなのですが、Carbonイベントに慣れていないと少し見通しが悪いかもしれません。ただ、逆に言えば、このPPxでのCarbonイベントの処理パターンが把握できてしまえば、PPxのかなりの部分は理解できたも同然で、あとはその線に沿って必要な形で拡張していくだけで済むのです。
ここで、少し横道にそれますが、このSpecificEventDoerを継承して作られているこれらのクラスについて、言語の面から見て見ることにします。
Carbonイベントの処理を行うさまざまなクラスは、すべてが抽象クラスです。抽象クラスについては、連載のかなり前に説明しました(調べてみたら、何と連載の38回目!)が、忘れた方も、読まれていない方もおいででしょうから簡単に解説します。
抽象クラスというのはそれ自体は実体化(インスタンス化)できないクラスのことです。C++では、純粋仮想関数を備えたクラスを抽象クラスと呼びます。純粋仮想関数というのはクラス宣言の末尾に「= 0」と付いているメンバ関数のことで、この純粋仮想関数を1つでもメンバ関数の持つクラスは抽象クラスとなります。
最近はオブジェクト指向言語が花盛りで、かつて「実用になる唯一のオブジェクト指向言語」と言われたこともあったC++ですが今や“特殊視”されることも多くなってきたようです。そんな今の時代に書かれたオブジェクト指向の解説を読むと、その多くで“多重継承”を批判的に書かれているのが目立ちます。実際、私も今までいくつものオブジェクト指向言語を使ってきましたが、その中で多重継承を実装しているのはC++だけでした。
分からない方もおいででしょうから、多重継承についても簡単に解説しておきましょう。
オブジェクト指向言語において、既存のクラスを元にサブクラスをつくることを“継承”と言いますが、通常は一つのクラスを継承してサブクラスを作るのでこれを“単一継承”と呼びます。
これに対し既存の複数のクラスを同時に継承することを多重継承と呼びます。
AとBという二つのクラスがあった場合、この両方のクラスを同時に継承してCというクラスを作ると、新しくできたCというクラスはAのサブクラスでもあり、Bのサブクラスでもあるわけです。両方のクラスを継承していますから、当然両者の性質を共に受け継ぐことになるわけです。
A、B、それぞれのクラスにDoSomethingというメンバ関数があったとします。単一継承では、AのDoSomethingはBよよってオーバーライドされるので、CのインスタンスがDoSomethingを呼ぶと、呼び出されるのはBのDoSomethingです。(あえてオーバーライドしないことも可能ですが、今はそれは考えません)
しかし、多重継承の場合、AとBのDoSomethingはオーバーライドの関係にはありませんから、CのインスタンスでDoSomethingを呼び出した時にどちらのDoSomethingが呼び出されるかが特定できません。
さらに、AとBには実は共通のXというクラスがあって、XでもDoSomethingが定義してあったら? さらに、BにはDoSomethingがなかった場合に、呼び出されるのはどれ?
このように、最終的にどれが呼び出されるのかが曖昧なのを解決する手段はきちんとC++に用意されてはいるのですが、それでもコードが複雑化することはさけられません。
そんなこんなの事情があって、多重継承は余り好まれていないのです。詳しく話すともう少しいろいろあるのですが、ここではそんな雰囲気だけを感じてもらえれば充分です。
もっとも、こうしたやっかいなところを除けば、複数のクラスの機能をミックスして新たなクラスとしてまとめられるのが有意義なわけです。そのためほとんどのオブジェクト指向言語では多重継承を使わずにこれを実現する方法を持っています。Javaではインターフェースというものがそれに当たり、Objective-Cではプロトコルというものがそれに当たります。各言語ごとに呼び方や実現の仕方は異なりますが、この仕組みは一般に“mix-in”と呼ばれます。
C++においてもmix-inは実現されていて、それには抽象クラスを使うのです。そうすると多重継承にはならず、前述のやっかいさとも無縁でいられるのです。そんなわけで、C++ではほとんど多重継承が使われることはなく、実際、私が今まで経験してきた中でも、一度も多重継承の実例には遭遇していません。
それでは何でこのようなものがあるのでしょうか?
そこがC++のC++らしいところです。C++という言語は「書き方の自由度は極力許容するけど、それによって起こるすべても使用者責任で」という考え方の言語なので、言語でそう“書ける”からと言っても、それが“正しい”かどうかはまた別、ということになるわけです。
まあ、プログラミング言語であれば、多かれ少なかれこの辺は言えるわけで、無秩序に書いてもちゃんと動く言語などあり得ないので、この辺は“程度問題”なのですが、その程度の“折り合いどころ”を議論してもたぶん生産的ではないので、やりません。
いずれにせよ、先ほども言ったように現代においてC++が“特殊視”されているという状況が世の中のトレンドの一つを示していることは確かなのでしょう。
開発ツールよもやま話 Xcodeのコード補完 高橋 政明
レパード(Mac OS X 10.5)で開発環境も大きく変わりました。私自身戸惑うことがありました。時間を取って違いを確認するべきですが常にそうできるとは限りません。まだ翻訳されている資料は少なく、日々の作業に追われている状況ではなおさらです。XcodeUserGuide.pdfは日本語ですが逆に540ページもあり読破するのはタイヘン〔^_^;〕です。
そこでXocedeを中心としたMac OS X用の開発環境の記事を隔週でお届けしようと思います。なるべく実用的な内容にしたいと思っております。
◆Xcodeのコード補完
初回は先日のMSMの二次会でちょっとだけ話題になったXcodeのコード補完についてです。
コード補完使っていますか? 私は最近は頻繁に使っています。キーワードのスペルは増々長くなります。正直な所、短くてもそして頻繁に使う語でも正確なスペルを記憶・入力できなくなってきている〔^_^;〕のですが、それはさておきコード補完を利用すると大文字小文字の区別を正確に入力する必要がなくタイピングが楽になるのです。
設定によりカスタマイズできますがこんな感じです。
「nsst」までタイプしescを押すと候補がでます。NSStringが選択状態ならリターンを押すだけで入力(確定)されます。
Xcode2.4では環境設定の「コード入力補助」で設定すると使えます。
・環境設定の「コード入力補助」の主な項目(Xcode2.4)索引:すべてのプロジェクトで有効にする
有効にするとソース内の変数名などローカルなスペルも補完してくれます。
コードの補完:補完機能を利用できる時に知らせる
入力中に下線が表示され補完可能なことがわかります。
ポップアップリストに引数を表示
引数もあると見分けやすくなりますが横幅も必要です。
補完用の引数のプレースホルダを挿入
プレースホルダとは馴染みがない単語ですが、引数に対応する仮の<>で囲まれた語です。
自動的に候補を表示:しない/メンバーの呼び出し/アクセス時/常に
三種類から好みで選ぶことができます。候補があればescキーで呼び出せ
るので私は「しない」を選んでいます。
候補の一つを選び補完結果を入力すると、引数のある場合最初のプレースフォルダが選択された状態になります。そのまま引数を入力する事ができて便利です。
次のプレースフォルダへ移るにはcontrolキーを押しながら/(スラッシュ)です。この設定はXcode環境設定のキーバインド/編集/次のプレースホルダを選択で変更できます。『次のプレースフォルダへ移る』のキー設定はMetrowerks互換のデフォルト設定ではcontrol+カンマです。カンマから次の引数を連想できるで私はこちらが気に入っています。
常にリストがポップアップするのは煩わしいですがXcode3ではコード補完が少し変わりました。候補がポップアップではなく入力行に直接表示されます。この機能をインライ
ン表示と呼ぶようです。未入力部分は薄く表示されます。リターンで確定、escキーで他の候補がポップアップするのは同じです。
・環境設定の「コード入力補助」の主な項目(Xcode3.0)自動的に候補を表示:しない/すぐに/すこし経ってから「しない」以外に設定している場合インライン表示されます。
そのほかはXcode2.4とほぼ同じです。「補完機能を利用できる時に知らせる」はなくなっています。
◆便利なマクロ
2007年3月にMOSAdeBBの会員談話室にも書きましたがコード補完と同じ操作でマクロを呼び出す事ができます。
logまでタイプしてescキーを押すと
NSLog(@"<#message#>");
と展開されます。
pmだと
#pragma mark <#label#>
aだと
[[<#class#> alloc] init]
aaだと
NSArray * array;
maだと
NSMutableArray * array;
nsdだと
NSDictionary
nsmdだと
NSMutableDictionary
とそれぞれ入力されます。
すべてのリストは
http://developer.apple.com/documentation/DeveloperTools/Conceptual/XcodeUserGuide/Contents/Resources/ja.lproj/03_03_editing_editor_window/chapter_22_section_7.html
(日本語XcodeUserGuide.pdfの236ページ「補完プレフィックスのあるテキストマクロ」)に載っています。頻繁に登場するだけにキーのタッチ回数が節約できます。
私も食わず嫌いであったコード補完機能ですが、今では重宝しています。環境設定で好みに調整できますので試してみてはいかがでしょうか。
◆本日のまとめ
コード補完を上手に使うとタイピングが楽になり生産性が上がります。
◇MOSAからのお知らせと編集後記は割愛します◇
配信停止 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.