/ 最近 .rdf 追記 設定 本棚

脳log[Ruby: 2008-04-17~]



2008年04月17日 (木) JavaScriptと HTMLを初めてさわったのは Win98の「フォルダのカスタマイズ」。WMPを埋め込んで試聴できるようにしたり。

[tDiary][Ruby] CGI.escape と ERB::Util.u の違い

http://tdiary.cvs.sourceforge.net/tdiary/plugin/category.rb?revision=1.45.2.1&view=markup&sortby=date&pathrev=Stable-2_2

  • 「 」が「%20」になる。(ERB::Util.u)
  • 「 」が「+」になる。」(CGI.escape)

気付いたのは日記を書くときに、カテゴリ名入力支援機能(クリックすると本文にカテゴリが挿入される*)のカテゴリリストに目当てのカテゴリがなかったから。

脱線。何かのソースを見たときに思ったのだけど ERB::Util.u も CGI.escape もエンコードしすぎだと感じてる人がいるみたい。(一部の記号をわざわざ復号していた。たしかに %XX が URLに現れるのは美しくない)

閑休。存在するはずのカテゴリファイルがなくてエラーを出していたのは、ここ(20071208p01)で自分が書いた tdiary/categorizedio.rb だったので誰にでも起こる不具合なのか確証がなかったり。

http://tdiary-users.sourceforge.jp/cgi-bin/wforum/wforum.cgi?mode=allread&no=5718&page=0

tDiary標準のカテゴリモードがどのようになるのかは未確認だったり。

複数のポストを日付で括ってしまう tDiary(<日記だから)はどうしても <title>タグの中身が味気なくなってしまって、ボットにも人間にもアピールが弱いな、とか全然関係ないけど、いま思った。(BlogKitでは解決してそう)

ぼそり。(category.rbは @conf.data_pathと 'category'を連結するときにパスセパレータを二重化してる。問題はレンタルサーバ(FreeBSD)でもローカル(Windows)でも起きていないが、そういうのが気持ちわるい&気にしたくないので自分は File.joinや Scripting.FileSystemObject.BuildPathを必ず使う)

* 本文の末尾でなくカーソル位置にカテゴリを挿入するための変更はこちら > http://www.kde.ics.tut.ac.jp/%7Efrsh/tdiary/?date=20071220#p01

 Firefox3はデコードした URLをロケーションバーに表示して、クリップボードにはエンコードした URLをコピーするので、二重にエンコードされた部分が含まれる、Amazonやブックマークサービスの URLでしか %XX を見ることはなくなりそう。


2008年04月04日 (金) *s と s[] は sizeof の結果も違っていたはず。仮引数に s[]とも書けるのはデメリットでしかない。実際はポインタなのだから。

最終更新: 2012-01-26T13:29+0900

[Ruby] 最近は Cのポインタが流行りらしい

Rubyに来たときは意識しなかったが、最近 Ruby側から C++を眺めたときにあまりの違いに恐れおののいたこと(限りあるスタックの使いすぎに注意しようね)。

 C++の変数はごつい、あるは近い (蛇足。もちろん全てがそうとは言わない)

# Ruby
myobject = MyClass.new();
// C++
MyClass myobject;
MyClass *pmyobject = &myobject;

Rubyでは MyClass.new()で MyClassのインスタンスを作り、それを後から参照するために myobjectという名札を貼り付けている。C++では MyClass myobject; だけで既にインスタンスが存在していて、名札を貼り付ける行為は二行目が相当する。Rubyには myobjectのようにオブジェクトそのものを表す変数は存在しない*

書いてて思った。ポインタ(に近いもの。参照。C++の参照とは違うもの)はスクリプト言語では普通に使われている*4。Rubyでは C++の myobjectに相当するものがなく、全てのオブジェクトが一段遠くにあるから、C++でやるように (*pmyobject).hoge() や pmyobject->hoge() というようにポインタをデリファレンスする手間を省いて myobject.hoge() という書き方でメソッドを呼べるが、Rubyの myobjectは C++の、myobjectよりむしろポインタの pmyobjectに一番近い。

* Fixnumは例外。

 これが Rubyに ++、--が存在しない理由かとも思ったが、C++で単項インクリメント/デクリメントをメンバ関数でオーバーロードできることを考えると Rubyでやっていけない理由がわからない。Rubyでも ++相当のメソッドを定義できるということだし、全てがオブジェクト(内部状態を持てる)であることを謳っている Rubyだからこそ制約なしにやっても構わないんじゃ。http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/5323。

 いやいやいや、仮に ++/--というメッセージが追加されても Numericはイミュータブルだという理由で使えなければ 99%の利用場面が奪われる。これをなんとかすると ++が +=のシンタックスシュガーになったり、Fixnumがオブジェクトでない(=変数がオブジェクトへの参照でない)ことが露呈したり、a=b=1; ++b; で a が 2 になったりする。これは違う。

*4 C++の &のような、明示的にアドレスを取得する方法がないので、ポインタのポインタは存在しない。


2008年03月17日 (月) Google(jp)経由でこの日記を初めて見つけた (SHJSで検索。30件表示してると 1ページ目(笑)。検索しても見つけられないレアなサイトだったのに……。ちょっと客観視してみると、えらく解りにくく読みにくい文章を書いていた。

[Ruby] RUBY_INSTALL_NAMEを ruby以外に変更してもインストールできるようになってる (Ruby1.9)

win32/configure.batが生成する Makefileを編集して* ruby19.exeを作るようにしていると ruby-dev:34000 と同じところで止まっていたのでした。

MLのスレッドを読んで、RUBY_SUFFIXだけでなく RUBY_INSTALL_NAMEも反映して欲しいと思ったら、そのように変更されてコミットされていた。それは手元の Makefile.subと同じです。

* configure.batの引数として与える方が人手を介さない分、良さそう。1.8のときに prefixオプションが効かなくて Makefileの直編集に切り替えたのだった気がするが、1.9.0-1では効いていたと思う。(最初、いつも通りにしたら C:/Program Files (x86)/ruby/Program Files (x86)/rubyにインストールされたのは、prefix=と PREFIX=が両方有効だったからだと思う。)


2008年03月07日 (金) 紅玉 = 赤い宝石 = Ruby だったのねん。紅玉制覇編の意味が今わかった。最近、名前に意味がある、意味を込めようとする人が多い、ということに気付いてきている。(でもこれは単なる語彙の問題)

[Ruby] Ruby-1.9.0-1: NMAKE : fatal error U1045: spawn failed : No error Stop.

miniruby.exeを作るところと、ruby.exe、rubyw.exeを作るところで止まる。何度でも止まる。

nmake の /N オプションで実行されるコマンドを表示して、自分でタイプ*、実行すると進むから不思議。

* 嘘。手打ちじゃない。


2008年03月04日 (火) 現在 9F。火トカゲを倒す必要がないことにしばらく気付かなかったよ。

最終更新: 2010-03-22T05:44+0900

[][][Ruby][単行本] arton【Rubyを256倍使うための本 邪道編】 アスキー

著者は ActiveScriptRubyの arton氏。

この本が出た当時、Mysterious SyndromeWindows Scripting Host Laboratoryの管理人むたぐち氏が、言語は Rubyであるが、COMや WSHの話題は VBScriptや JScriptと共通だし、他にこんなに突っ込んだ類書もないので、と(言葉と内容は全然違うが)自身の掲示板でおすすめされていたのをずっと覚えている。

タイミングを逸していただけなのだ。

Rubyの名前を初めて目にしたのもこの時だったかもしれない。(「何か」(当時)の名前を目にしたのもこの掲示板が……。美耳いいよね)

あくまで目にしていただけで、初めての CGIプログラムはサンプルの多い Perlで書いたし、すぐに嫌気がさして代わりの言語を探したときに Rubyを"再"発見している。そのときに、WSHに親しんでいたことと ActiveScriptRubyの存在が Ruby採用のきっかけになった、というのは多分にありそう。

思い出深い一冊ということ。この本を通して、気付かないまま Rubyとすれ違っていたのだから。


2008年02月13日 (水) ファイルを編集していつものように Subversionにコミットしたら http越しに http://hikidoc.rubyforge.org にコミットしにいっていた。拒否されて本当に良かった。こういうときは SVKか Mercurialですか?

[Hiki][Ruby] HikiDoc(revision 90)のリストアイテムに段落を。

リストを使うと途端にそこから文章に構造がなくなってしまうのが困りもので、できるだけ !!(h4)や !!!(h5)を使うようにしていたのだけど、とうとうリストアイテム内で段落を書けるようにいじってしまった。(少し前の変更で取り除かれた \ エスケープが復活している……)

 hikidoc.rb.extend-listitem.diff (2.4KiB)

HikiDoc(r90)のリストアイテムに文章構造(段落)を持ち込むための変更

 HikiDocフォーマット

*li1-p1\
\
li1-p2A\
li1-p2B
*li2

 HTML

  • li1-p1

    li1-p2A li1-p2B

  • li2

 HTMLソース

<ul>
<li><p>li1-p1</p>
<p>li1-p2A
li1-p2B</p>
</li>
<li>li2</li>
</ul>
  • 影響を最小限にするために \を使って行をまたいだときだけ <p>タグが挿入される。
  • 段落以外の、実はすべてのマークアップが有効になっているが、どういう表示になるかは未確認。
  • 思いつきのやっつけです。

行末の \ は見落としやすいので *-、**-、***- を、前行のリストアイテムを継続する、という意味にするのはどうだろう。

 hikidoc.rb.extend-listitem2.diff (1.6KiB)

HikiDoc(r90)のリストアイテムに文章構造(段落)を持ち込むための変更2 (以前の変更からの差分なので順番に適用する必要がある)

 HikiDocフォーマット

* li1-p1
*-
*-li1-p2A
*-li1-p2B
*li2

 HTML

  • li1-p1

    li1-p2A li1-p2B

  • li2

 HTMLソース

<ul>
<li><p>li1-p1</p>
<p>li1-p2A
li1-p2B</p>
</li>
<li>li2</li>
</ul>

なぜ *+ でなく *- にしたんだろう……。

[Ruby] Ruby-1.9の文字コード関連

面倒くさいという印象しか持っていない。

文字コードに unawareなスクリプトが文字コードがらみで余計なことをされたあげく、動かなくなるというのだけはやめてほしい。

必要なときだけエンコーディング情報を付加するし、そのときだけエンコーディング情報を利用したメリットを受け取りたい

実際のところはどうなってるのでしょう。


2008年01月11日 (金) 文字クラス内で後方参照は使えない。/(")[^\1]*\1/ のようなものは [^1]とも [^"]のときとも違う結果になった。(Ruby1.8, JScript, JavaScript(Fx3rc1)で実験)

最終更新: 2016-11-12T11:41+0900

[正規表現][Ruby] 鬼車すごい。正規表現で再帰ができる。

Rubyの、括弧を使った %記法だって。

irb19> re = /%[Qq]?(?<brace>\{[^\{}]*(?:\g<brace>[^\{}]*)*})/
irb19> strings = %w(%{z}a %{a{b}z}c %{a{b}c{d{e}f}z}g %{{{{}}}z}a %{a{b}c %{z}a}b)
irb19> strings.each{|str| p str[re] }
"%{z}"
"%{a{b}z}"
"%{a{b}c{d{e}f}z}"
"%{{{{}}}z}"
nil
"%{z}"
=> ["%{z}a", "%{a{b}z}c", "%{a{b}c{d{e}f}z}g", "%{{{{}}}z}a", "%{a{b}c", "%{z}a}b"]

どの例も正しい範囲( %{ から z} まで)を切り取っているのがわかる。

  1. この機能が使える鬼車がのってる Rubyは 1.9.0。
  2. PCRE(ver 4.x)の (?P<name>...)と(?P>name)が同じものにあたるらしい。へー、そんなものが。javascript(JScript)の正規表現も新しくならんかな
  3. .NETの (?<open>...)と(?<close-open>...)も同じことができるらしいが、正直わからん*
  4. http://www.kt.rim.or.jp/~kbk/regex/regex.html < 正規表現の各種実装の違いがよくわかる。

 追記@2008-05-09: 対応する括弧にマッチする正規表現のヴァリエイション(<くどい表記だな)

/%[Qq]?(?<brace>\{(?:[^\{}]++|\g<brace>)*})/

若干速い。同じパターン( [^\{}] )の繰り返しも存在しない。http://fleur.hio.jp/perldoc/perl/5.10.0/pod/perl5100delta.mix.html#Regular_expressions を参考にした。

/%[Qq]?(?<brace>\{(?:[^\{}]+|\g<brace>)*})/

上のものの + が一つ落ちたもの。開き括弧が余分にある文字列を食わせると待てども待てども返ってこない。 http://mlog.euqset.org/archives/ruby-list/42232.html で既に書かれている。それに対する返答が http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/42233

* http://msdn2.microsoft.com/ja-jp/library/bs2twtah(VS.80).aspx#BalancingGroupDefinitionExample

 \1 (後方参照)が、単純にすでに出現したのと同じ文字列にマッチするパターンなのに対して、任意のパターンを指定できるものだと想像してみる。

 追記@2008-05-09: それでは括弧のネストに対応できない。それは条件分岐の一形態。.NETのものはカウンタの増減を指示する構文。

最終更新: 2016-11-12T11:41+0900

[SakuraEditor][Ruby] SakuraEditorの正規表現キーワードファイル(Ruby用)を作っていた

テキストエディタは、メモ帳、TeraPadときて、現在は SakuraEditorを、もう五年以上使っている。xyzzy(正式な読み方はないらしい。ジッズィと読んでいる。saxyunの読みがサッキュンなのは詐欺だと思います)や Meadowに移る気は無し。

構文ハイライトには興味がなくて、どこかからダウンロードしてきた強調キーワードファイルや正規表現キーワードファイルを使っていた。間違いがあっても正規表現を直したりはせず、オフにするだけ。邪魔にならなければいい程度の扱いだった。

ところが最近 SHJSをいじってたこともあって中途半端なハイライトが許せなくなってしまった。

 SHJS+ブラウザと同じ表示を SakuraEditorでも!

そのためには鬼車が必要。BREGEXP.dllでは %Q<a<b<c>d<e>f>g> のように入れ子になった括弧の対応を調べられないので。

 前提条件
 Rubyキーワードファイル(ruby_keywords.zip)をダウンロードしてエディタでインポート。

キーワードファイルはこの日記の SHJS(with カスタマイズ版 sh_ruby.js)を使ったハイライトとコンパチ。ただし完全移植ではない。サクラエディタの正規表現キーワードは正規表現一発で、マッチした全体を特定の配色にするだけだけど、SHJSはパターン中のキャプチャグループごとに配色を分けることができる。サクラエディタの制限で、正規表現が行をまたいでマッチできないことも影響がある。正規表現マッチが行をまたげないのは SHJSも同じなのだが、SHJSは正規表現キーワードから利用できるスタックを用意していて、行を超えて色指定を継続できる。


2008年01月01日 (火) Application Dataなんてフォルダを掘って一人で中に収まってる Operaは恥を知れ (Vistaでの話)

[SHJS][Ruby][tDiary] SHJSの Rubyルールを修正。(sh_ruby.js, sh_ruby.min.js)

以下、変更点のリスト。(\bの使い方が適当なのでスペースの少ないソースで問題が出る可能性あり。\bの使いどころが全然わかってないせい)

    { // part of Kernel methods.
      'regex': /\b(?:defined\?|Array|Floar|Integer|String|abort|callcc|exec|exit!?|fork|proc|lambda|set_trace_func|spawn|syscall|system|trace_var|trap|untrace_var|warn)\b/g,
      'style': 'sh_preproc'
    },

なくてもいいかな、と思うけど defined?と Kernelモジュールのメソッドの一部を sh_preprocとして追加。Rubyで sh_preprocなのは requireだけなので sh_preprocの配色を流用した。選んだのは abort、callcc、exit、fork、systemなど比較的重要そうなもの。(loopなど一部の他のメソッドは sh_keywordとして既に分類されている)

    {
      'next': 4,
      'regex': /<(?=[\w\/])/g,
      'style': 'sh_string'
    },

正規表現を /</g から変更。<<メソッドやヒアドキュメント(<<HOGE)にマッチしないように。

    { // Symbol
      'regex': /:(?:(?:@@|@|\$)?\w+[\?!]?|\+=?|!=?|~|\*\*=?|-=?|\*=?|\/=?|%=?|<<=?|>>=?|&=?|\|=?|^=?|>=?|<=?|<=>|===?|=~|!~|&&=?|\|\|=?|\.\.|\.\.\.|=)(?=\s|$)/g,
      'style': 'sh_string'
    },

新ルール。シンボル(:hoge)を sh_stringとして色付け。

    { // %!string!
      'regex': /%[Qq]?([!-'*-\/:;=?^]).*?\1/g,
      'style': 'sh_string'
    },

新ルール。%!string!、%Q!string!、%q!string!を sh_stringとして色付け。残念ながら %Q[]のように括弧を使ったものは入れ子になった括弧を数えられないので非対応。対応した。詳しくは下の方。

    {
      'regex': /(?:\b(?:alias|begin|BEGIN|at_exit|break|case|do|else|elsif|end|END|ensure|for|if|in|include|loop|next|raise|redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield|and|not|or|def|class|module|catch|fail|load|throw)\b|&&|\|\|)/g,
      'style': 'sh_keyword'
    },

ここにはプログラムの流れや定義に関するキーワードや Kernelメソッドが集められているようなので、既に登録されている ENDと同じ働きの at_exitを追加し、definedを削除(上で sh_preprocとして defined?を登録済み)、false、nil、self、true、__FILE__、__LINE__を削除し、あとで定数として定義。&& と || を and、orに対応するものとして追加。

    { // global variables
      'regex': /\$(?:[_&~`'\+\?!@=\/\\,;\.<>\*\$:"]|-?[A-Za-z0-9_]+)/g,
      'style': 'sh_type'
    },

グローバル変数の定義を追加。sh_typeはインスタンス変数やクラス変数のクラス名として使用されているもの。

    { // Constants
      'regex': /\b[A-Z]\w+[!\?]?(?=\b|$)/g,
      'style': 'sh_function'
    },
    { // Constants
      'regex': /\b(?:false|nil(?!\?)|true|self|__FILE__|__LINE__)(?=\b|$)/g,
      'style': 'sh_function'
    },

定数のルールを追加。sh_functionは Rubyでは使われていないクラス。

    {
      'regex': /[a-z0-9_]+(?:\?|!)/g,
      'style': 'sh_normal'
    },

正規表現を /[A-Za-z0-9_]+(?:\?|!)/g から変更。定数は区別したいじゃない。

 余談

    {
      'exit': true,
      'regex': /$/g
    },

文字列リテラルの終了条件に上のは必要ない、むしろこれがあることで複数行にまたがったリテラルを正しく認識できない、のだけど強力すぎる正規表現は誤認識があったときにソースを最後まで一色に染めてしまう危険性があるのでそのままにしている。ヒアドキュメントに対応しないのも同じ理由。

 追記@2008-01-02:括弧を使ったリテラルにも対応した

    { // %r(regexp)
      'next': 6,
      'regex': /%r[\(<\[\{]/g,
      'style': 'sh_regexp'
    },
    { // %x(command), %w(array)
      'next': 7,
      'regex': /%[xWw][\(<\[\{]/g,
      'style': 'sh_normal'
    },
    { // %(string)
      'next': 8,
      'regex': /%[Qq]?[\(<\[\{]/g,
      'style': 'sh_string'
    },
  [
    {
      'exit': true,
      'regex': /$/g
    },
    {
      'next': 6,
      'regex': /[\(<\[\{]/g,
      'style': 'sh_regexp'
    },
    {
      'exit': true,
      'regex': /[)>\]}]/g
    }
  ],
  [
    {
      'exit': true,
      'regex': /$/g
    },
    {
      'next': 7,
      'regex': /[\(<\[\{]/g,
      'style': 'sh_normal'
    },
    {
      'exit': true,
      'regex': /[)>\]}]/g
    }
  ],
  [
    {
      'exit': true,
      'regex': /$/g
    },
    {
      'next': 8,
      'regex': /[\(<\[\{]/g,
      'style': 'sh_string'
    },
    {
      'exit': true,
      'regex': /[)>\]}]/g
    }
  ],

括弧の対応をチェックすることはするけどカッコの種類を区別しないので

%(foo{bar)baz}

こんなのも通る。でも現実的には区別する必要ないよね。HTML断片を組み立てるときに問題がありそう。そしてそういうときにこそダブルクォーテーションを使わずに %[]を使うんだよね。試してみる。

html << %[<option value="#{h hoge}"] << (selected? ? ' selected="selected">' : '>') << h(hoge) << "</option>\n";

やっぱりダメだ〜。

 追記@2008-01-02:括弧を使ったリテラルに正式に対応した

上で出した「こんなのも通る」と「やっぱりダメだ〜」の例が、言葉とは裏腹に「通ってない」と「ちゃんとできてる」状態になってると思う。だとしたら成功。

変更点は20080102p01で。

 追記@2008-01-05:#コメントと #{interpolation}の順番を入れ替え

# for variable interpolation, #{ is not a comment

というコメントを付けて #{}のハイライトルールを定義しているにも関わらず、それが #コメントルール よりも後ろにあるために機能していなかった。#コメントルールを後ろに持ってきて解決。

続きは20080105p01で。


2007年12月31日 (月)

[tDiary][Ruby] evalは最終手段*。module ::TDiaryを使うんだ。

 plugin/counter.rb

 plugin/disp_referer.rb

 plugin/makelirs.rb

 plugin/makerss.rb

 plugin/navi_user.rb

 plugin/pb-show.rb

 plugin/recent_list.rb

 plugin/tb-show.rb

 plugin/title_tag.rb

eval(<<__END_OF_TOPLEVEL__,TOPLEVEL_BINDING)
module TDiary
end
__END_OF_TOPLEVEL__

に類する evalは

module ::TDiary
end

でいいじゃない。

 tdiary/defaultio.rb

(行間に、構造に関係しないコードが省略されています)

module TDiary
	class DefaultIO < IOBase
	private
		def restore( fh, diaries )
						diary = eval( "#{style( style_name )}::new( headers['Date'], headers['Title'], body, Time::at( headers['Last-Modified'].to_i ) )" )

最後の行はこれ↓で。

						diary = style( style_name )::new( headers['Date'], headers['Title'], body, Time::at( headers['Last-Modified'].to_i ) )

 tdiary.rb

 plugin\pingback\pb.rb

anchor_str = @plugin.instance_eval( %Q[anchor "#{@diary.date.strftime('%Y%m%d')}"].untaint )

anchor_str = @plugin.anchor( @diary.date.strftime('%Y%m%d' ) ).untaint

で OK。


あえて寝た子を起こすまねをして新たなエラーを引き起こすこともないとは思うけど……。とはいえ、

@plugin_files.grep(/\/category.rb$/).empty?

のコピペの連鎖のようなものは断ち切りたい。emptyかどうかを知りたいのならマッチする全ての要素を集めてくる必要はなくて

not @plugin_files.any?{|pi| /\/category.rb\z/ =~ pi }
@plugin_files.find{|pi| /\/category.rb\z/ =~ pi }.nil?

のどちらかで十分です。速度的なもの(なんという婉曲さw)は計ってないけど、category.rbは cで始まるから (有効になっているのなら) @plugin_filesの最初の方にあって、すぐに結果がでるもののはず。

 計ってみたよ

irb(main):044:0> plugin_files = Dir.glob('./*.rb')
=> ["./a.rb", "./akismet.rb", "./amazon.rb", "./append-css.rb", "./bq.rb", "./calendar2.rb", "./calendar3.rb", "./category.rb", "./comment_mail-qmail.rb", "./comment_mail-sendmail.rb", "./comment_mail-smtp.rb", "./comment_rank.rb", "./counter.rb", "./daily_theme.rb", "./disp_referrer.rb", "./doctype-html401tr.rb", "./dropdown_calendar.rb", "./edit_today.rb", "./footnote.rb", "./gradation.rb", "./gradient.rb", "./hide-mail-field.rb", "./highlight.rb", "./html_anchor.rb", "./image.rb", "./kw.rb", "./list.rb", "./makelirs.rb", "./makerss.rb", "./my-ex.rb", "./my-sequel.rb", "./navi_user.rb", "./number_anchor.rb", "./pb-show.rb", "./ping.rb", "./pingback.rb", "./random_google.rb", "./recent_comment.rb", "./recent_comment3.rb", "./recent_list.rb", "./recent_namazu.rb", "./recent_rss.rb", "./recent_trackback3.rb", "./referer-antibot.rb", "./referer-utf8.rb", "./referer_scheme.rb", "./search_control.rb", "./search_form.rb", "./sn.rb", "./speed_comment.rb", "./squeeze.rb", "./src.rb", "./tb-send.rb", "./tb-show.rb", "./title_list.rb", "./title_tag.rb", "./tlink.rb", "./todo.rb", "./weather.rb", "./whatsnew.rb", "./xmlrpc.rb"]
irb(main):045:0> Benchmark.bmbm{|j|
irb(main):046:1* j.report('grep'){ 10000.times{ plugin_files.grep(/\/category.rb$/).empty? } }
irb(main):047:1> j.report('grep2'){ 10000.times{ plugin_files.grep(/\/category.rb\z/).empty? } }
irb(main):048:1> j.report('any?'){ 10000.times{ not plugin_files.any?{|pi| /\/category.rb\z/ =~ pi } } }
irb(main):049:1> j.report('find'){ 10000.times{ plugin_files.find{|pi| /\/category.rb\z/ =~ pi }.nil? } }
irb(main):050:1> }
Rehearsal -----------------------------------------
grep    0.359000   0.000000   0.359000 (  0.361000)
grep2   0.297000   0.000000   0.297000 (  0.275000)
any?    0.109000   0.000000   0.109000 (  0.104000)
find    0.094000   0.000000   0.094000 (  0.105000)
-------------------------------- total: 0.859000sec

            user     system      total        real
grep    0.297000   0.000000   0.297000 (  0.270000)
grep2   0.281000   0.000000   0.281000 (  0.275000)
any?    0.094000   0.000000   0.094000 (  0.100000)
find    0.140000   0.000000   0.140000 (  0.101000)

最新のプラグイン集のプラグインを全て有効にしたのと同じ状態では any? と findの勝ち。これでは全ての要素を調べる grepがあまりに不利。(ちなみに category.rbが見つからなくて grep同様に配列の最後まで調べた場合、any?と findは grepの二倍強の時間がかかっていた。yieldがあるからなあ)

plugin_filesの要素数を半分の 30にしても any?、findの勝ちだったので、有効なプラグイン数15(category.rbを含む)の状態でもう一度。

irb(main):058:0> plugin_files = plugin_files[0,15]
=> ["./a.rb", "./akismet.rb", "./amazon.rb", "./append-css.rb", "./bq.rb", "./calendar2.rb", "./calendar3.rb", "./category.rb", "./comment_mail-qmail.rb", "./comment_mail-sendmail.rb", "./comment_mail-smtp.rb", "./comment_rank.rb", "./counter.rb", "./daily_theme.rb", "./disp_referrer.rb"]
irb(main):059:0> Benchmark.bmbm{|j|
irb(main):060:1* j.report('grep'){ 10000.times{ plugin_files.grep(/\/category.rb$/).empty? } }
irb(main):061:1> j.report('grep2'){ 10000.times{ plugin_files.grep(/\/category.rb\z/).empty? } }
irb(main):062:1> j.report('any?'){ 10000.times{ not plugin_files.any?{|pi| /\/category.rb\z/ =~ pi } } }
irb(main):063:1> j.report('find'){ 10000.times{ plugin_files.find{|pi| /\/category.rb\z/ =~ pi }.nil? } }
irb(main):064:1> }
Rehearsal -----------------------------------------
grep    0.188000   0.000000   0.188000 (  0.175000)
grep2   0.109000   0.000000   0.109000 (  0.099000)
any?    0.110000   0.000000   0.110000 (  0.106000)
find    0.109000   0.000000   0.109000 (  0.107000)
-------------------------------- total: 0.516000sec

            user     system      total        real
grep    0.125000   0.000000   0.125000 (  0.096000)
grep2   0.094000   0.000000   0.094000 (  0.094000)
any?    0.110000   0.000000   0.110000 (  0.101000)
find    0.125000   0.000000   0.125000 (  0.105000)

category.rbを含めて 15のプラグインが有効の場合、僅差で any?、findの負け。category.rbを使ってない場合、any?、findはさらに不利になるわけだけど……。(みんな使ってるよね?) この日記では category.rbを含めて 23(+必ず有効な4つ)のプラグインが有効だから any?、findがもう少し有利になって、結論は「どっちでもいい」。けど、それなら findを使う。プラグインは増やすことはできても減らすと過去の日記でエラーが出たりするから。

 追記@2008-01-06:grepは名前勝ち?

なじみがあって処理内容が明確だから使いやすいのかも。同じことを find_allでやろうとするよりも(any?や findの結果から考えて)倍近く高速だろうことも想像ができるので、使いどころが正しければ優秀なメソッド。

 追記@2008-01-06:ブロック付き Enumerable#any?(all?)は Ruby 1.8 feature

2.2.0までは Ruby 1.6もサポートしていたので any?の使用は考えられないのだった。でも findは Ruby 1.6からあるようなのでここまで書いたことが全否定されたというわけでもない。よかった。

* ほんとうに?なんで?


2007年12月25日 (火) なんでもかんでも「相性」で片付けて原因究明を怠る(一部の)風潮が嫌い

[Ruby][tDiary] PStore, category.rb: 日記に変更があるたびにカテゴリキャッシュファイルが一斉に更新されるのをなんとかする。

日記を更新すると、TDiary::Config#data_path/category/ 以下の、カテゴリごとに作られるキャッシュファイルがずいぶんたくさん更新される。全部ではないが半分近い 21のファイルが更新されていた。日記の内容はというと一つのカテゴリしか使っていない。

どこのコードが無駄にファイルを更新しているのかと絞っていくと、category.rbの中の Category::Cache#replace_sectionsだとわかった。ではこの replace_sectionsが悪いのかというとそうではない。replace_sectionsの中の

PStore.new(cache_file(c)).transaction do |db|
end

に囲まれた部分をすべてコメントアウトしても 21のキャッシュファイルが一斉に更新されたのだから。一部の PStoreファイルは開いて閉じるだけで常に更新されるのだとしか考えられない。

PStoreは transactionの前後で Marshal::dump の戻り値のサイズと MD5が変化したかどうかを見て、変更があったかどうかを判断し、ファイルに書き込みをするかしないかを決めている。Marshal::load/dump が対称ではないのだろか。(そもそも Hashを Marshal::dumpした結果が一定だと仮定してよいのだろうか*)

原因が何であるにせよサーバーの pstore.rbを書き換えるわけにもいかないので、tDiaryの category.rbに対策を施した。

 PStore#transaction(true)を使う

読み出し専用であることが予めわかっている PStore#transactionはすべて trueを引数にして(readonly=trueの意)呼び出す。実はそういう transactionはすべて PStore#abortで終わっているのでこの対策は必要ないのだが、ファイルを排他ロックすることと MD5を計算する手間が省けるので一応。

 PStore#abortを使う

肝心の Category::Cache#replace_sectionsは transactionの開始前に変更があるのかどうかがわからないので PStore#transaction(true)は使えない。日付の削除が空振りに終わったときにだけ PStore#abortを呼ぶことにする。これで必要最低限のキャッシュファイルだけが更新されるようになった。

	#
	# cache each section of diary
	# used in update_proc
	#
	def replace_sections(diary)
		return if diary.nil? or !diary.categorizable?

		categorized = categorize_diary(diary)
		categories = restore_categories
		deleted = []
		ymd = diary.date.strftime('%Y%m%d')

		categories.each do |c|
			PStore.new(cache_file(c)).transaction do |db|
				db['category'] = {} unless db.root?('category')
				if categorized[c] and diary.visible?
					db['category'].update(categorized[c])
				else
					# diary is invisible or sections of this category is deleted
					db.abort unless db['category'].delete(ymd)
					deleted << c if db['category'].empty?
				end
			end
		end

		if !deleted.empty?
			deleted.each do |c|
				File.unlink(cache_file(c))
			end
			replace_categories(categories - deleted)
		end
	end

* 追記:最近の Ruby(1.8.7だか 1.9.0)は Hashの keyの順序を保存しているみたいだけど。


2007年12月20日 (木)

[Ruby] るびまゴルフ 【第 2 回】

 問題

以下のコードと同じ出力をより短いコードで得よ。(パーは 27バイト)

-2000.step(-10000,-10) do |v|
  puts v
end

 答案

n=1990;801.times{p -n+=10}

-1 (26バイト)だからバーディーかな。アルバトロス(-3)が上限とは限らないけど。数値リテラルが冗長な気がするけど他の表現が思いつかない。文字コード(?X)を使おうと思ったけどできなかった。

 やってみたこと

  • 省ける空白を省く
  • do〜end を {〜} に
  • puts を pに
  • 三カ所の -(マイナス)を一カ所に

 失敗したこと

  • ブロック変数名を $\(レコードセパレータ)にして、printを無引数で呼ぶ。(.step{|$\\|print}。$\に数字を代入できなくて失敗。なら文字列に対して uptoを呼べばいいんだけど文字列リテラルにするために引用符で +2バイト使っちゃ無駄が多いし、そもそもお題を満たせないし、泥縄)
  • (irb限定で) 変数_を使ったインチキ。

 できなかったこと (他人の答えを見て)

  • 三カ所の 0(十)を一カ所に (思いつかなかった)
  • Shift_JISの文字コードを利用 (できなかった)

2007年12月17日 (月)

[Ruby] Rubyの 定数の見える範囲 の理解はあきらめております

irb(main):115:0> require :benchmark.to_s
irb(main):119:0> Benchmark.bmbm{|j| j.report }
NameError: uninitialized constant Benchmark::Job::ArgmentError
        from C:/Program Files (x86)/ruby/lib/ruby/1.8/benchmark.rb:333:in `report'
        from (irb):119
        from C:/Program Files (x86)/ruby/lib/ruby/1.8/benchmark.rb:250:in `bmbm'
        from (irb):119
        from :0
irb(main):120:0>

 追記@2008-02-20:実は可視範囲の問題ではなくタイポだった

ArgmentError -> ArgumentError

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/44601


2007年01月29日 (月)

[Ruby] 文字列リテラル連結

irb(main):001:0> RUBY_RELEASE_DATE
=> "2006-12-25"
irb(main):002:0> "aaa"\
irb(main):003:0* "bbb"
=> "aaabbb"
irb(main):004:0> "aaa"\
irb(main):005:0* %[bbb]
NameError: undefined local variable or method `bbb' for main:Object
        from (irb):5

こういうもんなの? "bbb" と %!bbb! は同じものだと思っていたが。

 追記:2007-12-12

'%04d-%02d-%02d'%[2007,12,12]
=> "2007-12-12"

String#% が呼ばれてるんだよ。