本を読む

読書やコンピュータなどに関するメモ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

「メタプログラミングRuby」

メタプログラミングRuby
Paolo Perrotta
アスキー・メディアワークス
売り上げランキング: 1366

 実は最初、「method_missingとかdefine_methodとかinstance_evalとかが紹介されてるだけの本じゃない?」とかナメてました。すいません。

 概要としては、メソッド探索メカニズムとかオープンクラスとかを含めた、広い意味での「Rubyでのメタプログラミング」を紹介している本。が、本書が面白いのは、むしろそれらを使うイディオムとか使いどころを紹介してる点だと感じた。

 しかも会話形式にすることで、実例とか途中の課題とか失敗例とかを折りまぜて解説することに成功していると思う。これ、失敗すると単なる「入り込めないシナリオ」になるのだけど。

 Ruby勉強中の自分にとって、へぇだったのは以下のあたり。

  • incluceしたモジュールのメソッドは、インクルードするクラスの真上に入る
  • Ruby 1.9では、ブランクスレートとしてBasicObjectが使える(emasaka注:ブランクすぎる気はするけど)
  • define_methodのブロック引数はクロージャなので、フラットスコープに使える
  • ブロック、Proc、lambda、メソッドの違い
  • 多くのRubyistはクラス変数を使わずにクラスインスタンス変数を使っている(emasaka注:本当?)
  • オブジェクトの特異クラスのスーパークラスは、オブジェクトのクラス

 ちなみに、以前からActiveRecordがなんでdefine_methodじゃなくて文字列のevalでメソッドを定義してるんだろうと思ってたら、本書もこれに言及していた(p.244)。

define_method()は、いくつかのRuby処理系では、defよりも遅いことがわかっている(そうでないものもある)。これが、Railsの作者がアクセサの定義にコード文字列を選択した理由の1つである。

 なるほど。

スポンサーサイト

RubyKaigi 2010(初日)参加しました

 Rubyのイベント「RubyKaigi 2010」に参加してきました。都合により今年は初日のみ参加。しょぼん。

 といいつつ、その初日(しかも午後から)に遅刻するというありさま。やれやれ。

 以下、メモ。ちゃんとしたレポートはgihyo.jpの記事あたりを。

jpmobile on Rails 3.0 (conceal_rs)

 Tokyu.rbのツートップのひとり、小川さんによる、jpmobileとそのRails 3.0対応について。Rails 3.0対応の苦労話を主目的に聴いてみました。

 jpmobileは、携帯向けWebアプリを作るときに、文字コードや絵文字、メールのコードなどの差を吸収し、あるいはセッション管理やview切りかえ、リンクヘルパーなどを提供するRailsプラグイン。このへんで、各キャリアごとのコードの違いやformから送信されるテキストの挙動などが細かく説明されました。au端末から送信される絵文字がコードがシフトされているとか、ドコモの2G端末が面倒とか。あと、暗黙のview切りかえとか、GPS link helperとかも便利そう。

 あと、最近はコード変換など一部の機能をRackミドルウェアに移しているという話で、へーすごいなと。Sinatraとかからも使えるんだとか。キャリアごとの端末クラスを定義していたのに加えて、JPMobile::AbstractMobile::SmartPhoneクラスを作って、それを継承した::IPone/::Android/::WindowsPhoneクラスを作ったという話もしてました。herokuでテスト中とか。

 会場を唖然(慄然?)とさせたのが、タイトルにもあるRails 3.0対応にからんで、Railsのバージョンが変わるたびにフックポイント(というかオーバーライドすると都合のいいメソッド)が変わって大変という話。実際に、view切りかえに使うためにviewが選ばれるロジックを追って、数十メソッドをクラスを横断して探す様子を説明していました。AbstractControllerこわい(半分違う)。

Ruby on Railsではじめる携帯電話向けオープンソーシャルアプリケーション開発(Masaki Yamada)

 モバゲーとかmixiとかグリーとかのソーシャルアプリのうち、モバイル用の開発をRailsでやる話でした。「ゲーム=UIが超不親切なCMS」という位置づけが面白かった。

 PC用のオープンソーシャルアプリとモバイル用ソーシャルアプリとは仕組みからしてだいぶ違って、そもそもモバイル用はSNSサーバーとアプリのサーバーが通信する仕組みになっているとのこと。サーバーは大変だけど開発者はモバイル版が楽だという話でした。

 で、オープンソーシャル用のURLでlink_toを使うためにact_as_opensocialプラグインを作ったとか(いわく「acts」にすればよかった、と)、OAuthのプラグインを作ったとか(質疑応答で、既存のものあるじゃんと突っ込まれてたり)。でも自然にRails化したので、普通のRailsアプリとコードの見た目が同じになったそうです。ほか、DBを簡易シャーディングするacts_as_multi_connectionとか、EC2管理スクリプトec2toolsとかを作った話もしてました。

 大変そうだなーと思ったのが、プラットフォームの仕様(エンドポイントURLとか)をどこまでソース中に書いていいかわからないので抽象化を躊躇するという話でした。

Rubygems, Bundler, and the future(Carl Lerche)

 Rails 3で採用された、gem管理機構のBundlerの話でした。英語なのでよくわかってません。

 ライブラリから別のライブラリを呼んでいるときに、依存しているライブラリのバージョンが別だと面倒、と。特に、Rails 3は一枚岩からたくさんのライブラリを組み合わせるようになったので、よりややこしい。rackのバージョン問題とか、ぐぐってみても世界がハマってる。しかも組み合わせの数が爆発するので、gemのNP完全問題や~、と。

 なので、Rails 3ではBundlerを採用したよ、と。Gemfileに依存情報を書いて、アプリ側では「require 'bundler/setup'」とかしておいて(Railsのboot.rbとか)、bundle installでgemを取得、gemfile.lockに依存情報を出力、だそうです。

 あと、Railsのdevelopmentではgemはシステムにインストールするけどproductionではvendor/bundleにインストールしたほうがデプロイしやすいとか、特定のgemを更新するときに全部が変わっては困るのでbundle update rackみたいにするとか、build dependenciesの記述とか、Optional /Recomended dependenciesの記述とか、meta gems(jsonとjson_pureとか)とか、よくわからないながらも興味深く聴きました。

われわれは、GCをX倍遅くできる(nari)

 GC本が3刷だそうで、おめでとうございます。会場でも買った人が多数でしたが、通読した人は少なかった様子。…すみません、ようやくDalvik VMの章まで読んだところです。

 内容は毎年恒例のCRubyのGC話。保守的GCを戸籍問題にからめて笑いをとってました。

 本題は、去年のRubyKaigiでコミットするという話になったLazySweep GCの話。フルでMark-Sweepすると停止時間が長くなるので、Markは一度にするけどSweepはアロケーションのタイミングでブロック単位でやるということでした。これにより、停止時間の合計は長くなるものの、1回の停止時間が短かくなるのがメリットと。けっこうマークしたままスイープが走らないブロックもあったりして、そういうところで速度的には有利なのかなと思いました。そのへんを、ヒープのビジュアライザとかゲームソフトとかでデモしてたのもウケてました。ちなみに、Ruby 1.9.2では入らなかったけど、1.9.3には順調に行けば入るとか。

 あと、ファイナライザはフルスイープのときだけ走る仕様だそうで、どうしてそうなってるのかがRubyのメンテナやコミッタの方々の間で議論になってたりしました。

 LazySweepの採用にあたっては、ヒープスロットの構造も変えて、ソートされた配列から、双方向リスト+それぞれのエントリを指すソートされた配列にしたとか。で、Ruby(自身の)開発者への注意として、マークとスイープとで時間差があるのでその間の処理でSEGVを起こさないように、という話もありました。

 これからのCRuby GCについては、保守的GCの問題とか(でも実はそんなに問題なんじゃないか)、停止型のGCの問題(でもライトバリアが面倒)とかが上げられました。GC本の共著者の相川さんが、ライトバリア記述支援の研究をしているそうです。

 そのほか、マーク時間を短縮するために、マーク並列化とか、マークの再帰をやめるとか、シンボルなどをマーク対象から外すとか、Stringなど特定の型を専用のヒープにするとか、Boehm GC採用とかいう話がありました。

 最後に、Rubyプログラマには「"こんなアプリでGCが遅い"という実例を公開してほしい」、研究者には「rejectされたものも含めてGCの論文を公開してほしい」とメッセージが語られました。

Rubyで作るDSLの基礎(Yasuko Ohba)

 プログラムっぽい書き方とDSLっぽい書き方の違い、あるいはDSLの考え方をRubyのコードにどう生かすか、といったあたりを解説しました。DSLの実装方法というより、DSLの設計というか命名規則というか、そういう作法の部分についての話が参考になりました。裏番組を断念して聴いて、得るものがありました。

 いいDSLというのは、読みやすい、記述量が少ない、メンテナンスしやすいと。普通のRubyの式は「レセプタよ、これをせよ/くれ」という記述なのに対して、DSLは「宣言的な記述」「ブロック」「特定概念を表すメソッド」というのが記述の特徴、と。そのへん、発想を変える必要があるとか、実装としてはモジュール向きという話がありました。

 考え方として、「宣言的な記述には形容詞や受動態、三人称単数が似合う」というのは、ハッとさせられました。

 最後に、「DSL的な考えをとりいれて、いいコードを」というメッセージで締めました。

君のクラスの最高の偽物(Shugo Maeda)

 個人的に、初日のいろいろ面白そうなセッションがある中で一番期待のセッションでした。

 Rubyではオープンクラスによって、たとえばStringクラスにどんどんメソッドを追加できます。アプリならまだいいのですが、ライブラリを作るときにそれをやっちゃうとグローバル汚染によりライブラリを使うアプリにも影響しちゃいます。それを、限定した静的スコープの中でだけ使えるようにするclassboxを実装したという話です。

 といいつつ、最初に、レシーバーにメソッドを追加しなくてもいいんじゃないかとか、is-a(継承)やhas-a(移譲)ではだめなのかと釘を刺していましたが。でもそれでも必要なときはある(to_xmlとか)、と。

 考え方としては、特定の静的スコープのみ作用するModuleのinclude。具体的には、「Kernel#overlay_module(klass, module)」というのをCで実装したそうです。そのために、crefのレキシカルな構造にメソッド探索を追加したり、メソッキャッシュをクリアする工夫をしたり、superの探索順序がどうなるかとか、iseq->klassとかいう話でした。

 あと、複数のクラスをまとめて拡張するために、overlay_moduleを使ってclassbox.rbを作ったという話も。なんとRubyのコード47行で実現しているそうです。

 そのほか、evalの第2引数のバインディングに使えるbinding(1)(1レベル外のバインディング)とか、最適化されたメソッドを置きかえるためにいちど:method_addedで置きかえて戻すとか、defの中のdefのレシーバーとかいう議論がなされていました。

 ちなみに、tesst-allが通ったけどたまにSEGVする(GCがらみ)とのことでした。

再帰も外部データ構造も使わずにtree traversalする

 2進木などのツリー構造の巡回(tree traversal)を、再帰も、スタックなどの外部データ構造も使わずに実現する方法について考えてみます。

再帰を使う場合

 ツリーを巡回するのに、いちばんシンプルな方法は、再帰的に辿る方法です。Scheme(Gauche)で素直に書くとこんな(↓)感じ。Schemeよくわかってませんが。

 実行。

$ gosh traverse-tree1.scm
3
5
7
11

データ構造を使ってループにする場合

 大きなツリー構造だったり、再帰の深さ制限がキツ目の言語(処理系)だったりすると、再帰を使わずに済ませたいところです。

 そういうときには、外部データ構造を使ってループにするのが定石かと思います。深さ優先ならスタック、幅優先ならキューとか。

 外部データ構造ではありませんが、ツリーのノードに項目を追加できるなら、親ノードや次の行き先などを持たせておく方法などもよく使われます。

コンスセルのツリーをループで巡回する場合

 再帰を使わず、外部データ構造も使わず、ノードはLispのコンスセル、という条件でtree traversalする方法もあります。ちょっと乱暴な方法ですが。

 考え方は「ノードを通るたびに、そのノードを120度回転する」というものです。

  1. コンスセルのcarを元のcdrの値に、コンスセルのcdrを参照元に変更
  2. 元のcarの値がコンスセルならそのコンスセルに移って1.に、コンスセルでなければそのまま1.に

 ツリーを破壊しまくって巡回しますが、一巡すると元の構造に戻ります。

 Scheme(Gauche)で書くとこんな(↓)感じ。名前付きletの末尾再帰はループということで。最後にtreeを表示しているのは、元に戻っていることを確認するためです。

 実行。

$ gosh traverse-tree2.scm
3
5
7
11
((3 (5 (7)) 11))

 めでたしめでたし。

ネタ元は?

 この方法は、大昔に雑誌のインタビューだかエッセイだかで見たアイデアを思い出して、実装してみたものです。大学のコンピュータサイエンスの授業とかで普通に話してそうですが、受けたことないのでわかりません。ネタ元をご存知の方、よろしければ教えてください。

DevQuizに回答してみた

 Google Developers Day 2010に参加するためのDevQuizの提出期限が終了しました。ちらほら自分の回答を晒している人がいるので、調子に乗って私も。

 ちなみに、結果は惨敗でした(汗)

Siritori

 DevQuizに参加するような方々は傾向を見付けて攻略していたようですね。私は頭を使わず、WWW::MechanaizeとHTML::TreeBuilder::XPathでスクレイピングしてランダムに答えるだけのボットをぶん回してました。図書館なら怒られるレベル。

 で、寝て起きたら勝ってたと。

PAC-MAN

 これも頭を使わず、分岐点に来たらそれぞれ世界を枝分れさせて全件探索するというだけのプログラムを作りました。ちょっとだけ工夫したのは、深さ優先探索を再帰もスタックも使わずにやった点ぐらい。

 で、22日の夜に仕掛けて23日の朝に起きたら…Lv.1しか回答候補が出力されてませんでした。しょぼーん。

gitのhttpなリポジトリをApacheの仮想ホストに立てるメモ

 OSアカウントを作らずHTTPのBASIC認証でアクセスできるgitリポジトリをDebianで作る手順。なんか毎回やるたびに手元のメモやネットを調べ直してるので、ブログにメモを残しておく。

追記2011.12.15:最近では以下で説明しているWebDAVではなく、gitに付いてくるgit-http-bakcendというCGIプログラムを使うほうがよいらしいので注意。

 以下、http://hoge.example.com/fuga.gitというURLでgit cloneできるようにする場合。

 あらかじめgitとApache 2.2をインストールしておく。Apacheはまず止めておく。

$ sudo apt-get install git-core
$ sudo apt-get install apache2
$ sudo /etc/init.d/apache2 stop

 ApacheのWebDAVモジュールを有効に。

$ sudo a2enmod dav
$ sudo a2enmod dav_fs

 仮想ホストの設定ファイル/etc/apache2/sites-available/hoge.example.comを作成。

<VirtualHost *:80>
        ServerName hoge.example.com
        ServerAdmin webmaster@example.com

        DocumentRoot /path/to/documentroot
        <Directory /path/to/docuentroot>
                AllowOverride None
        </Directory>
        <Directory /path/to/documentroot/fuga.git>
                DAV On
                AuthType Basic
                AuthName "Access control"
                AuthUserFile /path/to/htpasswd
                Require valid-user
        </Directory>

        CustomLog /var/log/apache2/access_hoge.example.com.log combined
        ErrorLog /var/log/apache2/error_hoge.example.com.log
</VirtualHost>

 仮想ホストを有効に。

$ sudo a2ensite hoge.example.com

 BASIC認証のIDとパスワードを設定。

$ sudo -u www-data htpasswd -c /path/to/htpasswd oreore
(パスワードを入力)

 gitリポジトリを作成。これでいいかはちょっと不安。

$ cd /path/to/documentroot
$ sudo -u www-data mkdir fuga.git
$ cd fuga.git
$ sudo -u www-data git init --bare --shared=true
$ sudo -u www-data git --bare update-server-info

 Apacheを起動。

$ sudo /etc/init.d/apache2 start

参考:

「Emacsテクニックバイブル」

Emacsテクニックバイブル ~作業効率をカイゼンする200の技~
るびきち
技術評論社
売り上げランキング: 525

 テキストエディタEmacsユーザーが文章やコードを書く作業効率を上げるためのライブラリやコマンドを紹介している本。通読して、万年Emacs初級者の自分には参考になったので、さっそく以下の設定やコマンドを使ってみた。でもanything.el導入までのレベルアップはまだ遠い…

  • gc-cons-threshold
  • wdired.el
  • find-function-setup-keys
  • hideif.el
  • imenu
  • which-func-mode

 本書の作りで驚いたのが、説明図(ハコ図)もアスキーアートで描かれていること。場所によっては、ハコ図のアスキーアートを表示したEmacsの画面キャプチャーを掲載しているところまであって、説明のメタレベルを理解するのに1分ぐらいかかった。

Debian sidのrubygems1.9.1がエラーになるのを一時回避

(注意:この件は現在のDebian sidでは対応されています。追記参照)

 メモ。

 これを書いている現在(2010.8.8)、Debian sidでruby1.9.1パッケージとrubygems1.9.1パッケージをインストールして、gemコマンドを実行すると、エラーになる。

$ gem list
/usr/lib/ruby/1.9.1/rubygems/source_index.rb:68:in `installed_spec_directories': undefined method `path' for Gem:Module (NoMethodError)
	from /usr/lib/ruby/1.9.1/rubygems/source_index.rb:58:in `from_installed_gems'
	from /usr/lib/ruby/1.9.1/rubygems.rb:883:in `source_index'
	from /usr/lib/ruby/1.9.1/rubygems/gem_path_searcher.rb:81:in `init_gemspecs'
	from /usr/lib/ruby/1.9.1/rubygems/gem_path_searcher.rb:13:in `initialize'
	from /usr/lib/ruby/1.9.1/rubygems.rb:841:in `new'
	from /usr/lib/ruby/1.9.1/rubygems.rb:841:in `block in searcher'
	from <internal:prelude>:10:in `synchronize'
	from /usr/lib/ruby/1.9.1/rubygems.rb:840:in `searcher'
	from /usr/lib/ruby/1.9.1/rubygems.rb:479:in `find_files'
	from /usr/lib/ruby/1.9.1/rubygems.rb:983:in `load_plugins'
	from /usr/lib/ruby/1.9.1/rubygems.rb:1139:in `<top (required)>'
	from <internal:lib/rubygems/custom_require>:29:in `require'
	from <internal:lib/rubygems/custom_require>:29:in `require'
	from /usr/bin/gem:8:in `<main>'

 ほか、require 'rubygems'しているプログラムも同様。この件は、7月上旬にDebianのBTSに報告されているので、そのうち直るはず。

 とりあえずの回避策は、rubyに--disable-gemsオプションを付けること。

$ ruby1.9.1 --disable-gems /usr/bin/gem list

*** LOCAL GEMS ***

 原因はこんな(↓)感じか。

  • ruby1.9.1パッケージの中身はRuby 1.9.2(開発版)
  • ruby1.9.1パッケージでは、rubygemsまわりのライブラリを削除してパッケージ化している
  • librubygems1.9.1パッケージは、Ruby 1.9.2に含まれるrubygemsではなく、rubygems.orgで配布しているものを元にしている
  • ruby1.9.1パッケージでも、実はpreludeでRuby 1.9.2のrubygemsの一部を組み込んでいる
  • 混ぜると危険

追記(2010.10.13)

 その後、9月上旬に対応がなされました。rubygems1.9.1パッケージを廃止し、ruby1.9.1にrubygemsを統合(Ruby 1.9の元の配布形態)しています。

RubyでLoggerのブロック引数の効率を試す

 Rubyで標準のLoggerを使ってlogger.debug(expr)とかやると、デバッグレベルじゃないときにもexprは評価されてしまいます。ruby-profで見てみて、思ったより実行していることを知って、ちょっとオーバーヘッドが気になったり。観測問題ってやつですか。

 Rubyはマクロないからなぁと誤解してましたが、ドキュメントを読んだらLogger#debugとかではブロック引数で指定できると書いてあるじゃないですか。RTFM >おいら

 ただ、ブロック引数のオーバーヘッドは気になります。そこで、ベンチマークを取ってみました。結論は、実行時間(かかる順)ベースで以下のとおり。

Hashの式展開 >> ブロック引数 > Fixnumの式展開 > Stringの式展開

$ cat bench.rb
require 'benchmark'
require 'logger'

logger = Logger.new(STDOUT)
logger.level = Logger::ERROR

puts Benchmark::CAPTION
puts Benchmark.measure { 100000.times { logger.debug "#{'3'}" } }
puts Benchmark.measure { 100000.times { logger.debug "#{3}" } }
puts Benchmark.measure { 100000.times { logger.debug "#{{:a => 3}}" } }
puts Benchmark.measure { 100000.times { logger.debug { "#{{:a => 3}}" } } }
$ ruby --version
ruby 1.9.1p429 (2010-07-02 revision 28523) [i686-linux]
$ ruby ./bench.rb
      user     system      total        real
  0.130000   0.000000   0.130000 (  0.127763)
  0.170000   0.010000   0.180000 (  0.182469)
  0.630000   0.010000   0.640000 (  0.620975)
  0.180000   0.010000   0.190000 (  0.198962)

Mac→Linuxのsshログインでロケールを伝える

 メモ。

 Linux→Linuxでsshログインする場合、クライアントのロケールがサーバーに伝わって日本語ロケールになる。でも、Mac→Linuxではそうならない。そこで、Mac側のsshクライアントの設定に1行追加。

SendEnv LANG LC_*

「風雲児たち」17巻

風雲児たち 幕末編 17 (SPコミックス)
みなもと 太郎
リイド社 (2010-07-28)

 西郷隆盛と月照の逃避行、吉田松陰の決起、天狗党、関鉄之介の遊説、調印の勅許、辞令落飾、入江兄弟、竹島開拓論、村岡の局、戊午の密勅返納。

「極道めし」6巻

極道めし(6) (アクションコミックス)
土山 しげる
双葉社 (2010-07-28)

 ネタ尽きないなー。今度はさらに舞台と勝負方法を変えて女囚編に。

 | HOME | 

Categories

Recent Entries

Recent Comments

Recent Trackbacks

Appendix

emasaka

emasaka

フリーター。
連絡先はこのへん

Monthly


FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。