トップ 追記

ohai日誌

2003|12|
2004|01|02|03|04|05|06|07|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|06|08|10|11|
2008|01|02|03|04|05|07|09|
2009|01|02|07|09|12|
2010|01|02|03|04|05|07|08|09|10|
2011|05|06|07|08|09|11|12|
2012|02|03|04|

2012-04-08

_ 近況

出張ラッシュも終わって延期していた用事などを片付け始めています。


2012-03-28

_ 近況

数学会年会に参加するため東京都心に来ています。 2,3月は非常に忙しかったのですが、これが終われば落ち着く予定。


2012-02-25

_ 最近

左手首を捻挫したのでコードとか長文が書けない。完治するにはあと2週間とからしい。


2011-12-11

_ boost::numeric::interval

指数関数とか中身が実装されていないのな(枠組みだけある)。

いや面倒なのはわかるけどこれじゃあまり役に立たないと思う。


2011-12-01

_ 東大生研

11月の中頃に行ってきたのだが、非常に迷いそうな構造をしていた。 デザインした人は京都駅と同じらしい。


2011-11-21

_ るりまとbitclust

11/13の関西闇Ruby会議での発表資料 <URL:http://www.kmc.gr.jp/~ohai/20111113/p.pdf>


2011-11-16

_ Gnomeの下でhgsubversionを使っていて認証関連のエラーが出た場合の対処

subversion のパスワードが Gnome keyring の管理下にある場合に、 hgsubversion を使おうとすると、

Could not authenticate to server: rejected Digest challenge

などといったエラーがでる場合があります。

これは hgsubversion に問題があるのか subversion の python インターフェース に問題があるのかはわかりませんが、Gnome keyring管理下にある パスワードにアクセスする権限がない場合に発生します。 そのため権限を付加してやればうまく動作するようになります。

具体的には Gnome control center の「パスワードと暗号鍵」 から「パスワード: login」という項を右クリックし、「ロックの解除」を 選び、ロック解除用のパスワードを入力するとうまくいくようになります。


2011-09-15

_ hgsubversion の clone と rebuildmeta

hgsubversion で subversion のリポジトリを mercurial のリポジトリとして clone したあと、 その mercurial リポジトリを再び別のリポジトリに clone すると、 hgsubversion のメタデータは複製されません。

しかし、hgsubversion はそこをうまくなんとかする方法があります。 やりかたは、.hg/hgrc を

[paths]
default = /home/foo/bar

となっている(/home/foo/bar は複製元)のを、

[paths]
default = svn+http://example.com/bar/

などのように svn のリポジトリの URL に変更し、

hg svn rebuildmeta

とすると、メタデータを復元します。

svn リポジトリのすべてのチェンジセットを clone などで複製するのは かなり時間がかかるため、他人が複製したものをこれで clone するなどすると かなり高速に svn リポジトリを取り込むことができます。


2011-08-25

_ 「Rubyのバグを探せ」問題

移動中の暇潰しに解いてみた。10分ちょいで解けたので あまり暇潰しにならなかった。


2011-08-11

_ with_indexの仕掛け

Ruby 1.9には、Enumerable#collectのようなブロックを取るメソッドに ブロックを渡さなかった場合、collectを実現するような繰り返しをする Enumerator オブジェクトを返します。これは、

[3, 4, 8].collect.with_index{|n,idx| n+idx} # => [3, 5, 10]

というような使い道があります。また Enumerator#with_object というのも あります。これは便利なのですが、どういう仕掛けになっているのか ちょっと不思議なところがあります。ここではそれについて解説します。 Rubyのソースコードを手元に置いて解説を呼んでください。

まずは、Enumerable#collectでブロックを与えた場合について見ていきます。 実装の本体は enum.c:enum_collect で

enum.c:
#define enum_yield rb_yield_values2

static VALUE
collect_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
    rb_ary_push(ary, enum_yield(argc, argv));

    return Qnil;
}

static VALUE
enum_collect(VALUE obj)
{
    VALUE ary;

    RETURN_ENUMERATOR(obj, 0, 0);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, collect_i, ary);

    return ary;
}

の部分です。 RETURN_ENUMERATOR はブロック省略時に Enumerator を返している 部分でしょうから、ここでは置いておきます。するとこのメソッドはおおよそ 次のようなことをしています。

module Enumerable
  def collect
    ary = Array.new
    self.each{|n| ary.push(yield(n)); nil}
    return ary
  end
end

これで each メソッドが定義されたクラスで collect が使えるように なります。

では次に Enumerator を返すところを見ましょう。

include/ruby/intern.h:
#define RETURN_ENUMERATOR(obj, argc, argv) do {                         \
        if (!rb_block_given_p())                                        \
            return rb_enumeratorize((obj), ID2SYM(rb_frame_this_func()),\
                                    (argc), (argv));                    \
    } while (0)

Rubyのコード片に直すと

if !block_given?
  return rb_enumeratorize(self, :collect)
end

といった所でしょうか。マクロなのでこのコード片が enum_collect の所に 展開されるということに注意しましょう。 rb_frame_this_func は Ruby 側で呼びだしたメソッドの 名前を返しているので、ここでは collect になります。 argc, argv は collect では使っていない(0, 0になっている)ので無視します。 まあつまりブロックが渡されていない時に rb_enumeratorize の 返り値を返しているだけです。 次に rb_enumeratorize を見ます。

enumerator.c:
VALUE
rb_enumeratorize(VALUE obj, VALUE meth, int argc, VALUE *argv)
{
  return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv);
}

名前から予想するに、 enumerator_allocate は Enumerator 用のメモリを確保して、 Enumerator_init で初期化しているのでしょう。 次は enumerator_init を見ましょう。

enumerator.c:
static VALUE
enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
{
    struct enumerator *ptr;

    TypedData_Get_Struct(enum_obj, struct enumerator, &enumerator_data_type, ptr);

    if (!ptr) {
        rb_raise(rb_eArgError, "unallocated enumerator");
    }

    ptr->obj  = obj;
    ptr->meth = rb_to_id(meth);
    if (argc) ptr->args = rb_ary_new4(argc, argv);
    ptr->fib = 0;
    ptr->dst = Qnil;
    ptr->lookahead = Qundef;
    ptr->feedvalue = Qundef;
    ptr->stop_exc = Qfalse;

    return enum_obj;
}

ptr は先程確保されたメモリ領域を指しているので、これはおおよそ次の ようなことをしています。argc, argv は無視します。

def init(obj, meth)
  @obj = obj
  @meth = meth
  @fib = 0
    :
end

ここまでをまとめ、Rubyコードに直すと、以下のようになるでしょう

class Enumerator
  def initialize(obj, meth)
    @obj = obj
    @meth = meth
  end
    :
end

def rb_enumeratorize(obj, meth)
  return Enumerator.new(obj, meth)
end

module Enumerable
  def collect
    if !block_given?
      return rb_enumeratorize(self, :collect)
    end

    ary = Array.new
    self.each{|n| ary.push(yield(n)); nil }
    return ary
  end
end

最後に Enumerator#with_index が何をしているのかを見ましょう。

enumerator.c:
static VALUE
enumerator_block_call(VALUE obj, rb_block_call_func *func, VALUE arg)
{
    int argc = 0;
    VALUE *argv = 0;
    const struct enumerator *e = enumerator_ptr(obj);
    ID meth = e->meth;

    if (e->args) {
        argc = RARRAY_LENINT(e->args);
        argv = RARRAY_PTR(e->args);
    }
    return rb_block_call(e->obj, meth, argc, argv, func, arg);
}

enumerator.c:
static VALUE
enumerator_with_index_i(VALUE val, VALUE m, int argc, VALUE *argv)
{
    VALUE idx;
    VALUE *memo = (VALUE *)m;

    idx = INT2FIX(*memo);
    ++*memo;

    if (argc <= 1)
        return rb_yield_values(2, val, idx);

    return rb_yield_values(2, rb_ary_new4(argc, argv), idx);
}

enumerator.c:
static VALUE
enumerator_with_index(int argc, VALUE *argv, VALUE obj)
{
    VALUE memo;

    rb_scan_args(argc, argv, "01", &memo);
    RETURN_ENUMERATOR(obj, argc, argv);
    memo = NIL_P(memo) ? 0 : (VALUE)NUM2LONG(memo);
    return enumerator_block_call(obj, enumerator_with_index_i, (VALUE)&memo);
} 

RETURN_ENUMERATORはとりあえずここでは用はないので無視、 memo は index がどこまで進んだかを表します。

return enumerator_block_call(obj, enumerator_with_index_i, &memo);

という意味になります。obj は先程生成した Enumerator オブジェクトです。 enumerator_block_call は e->obj と e->meth を取りだして、 rb_block_call に func を いっしょに渡し、呼びだしています。rb_block_callばブロック付き呼出を 実現するようなものですから、これは

class Enumerator
  def enumerator_block_call(func)
    @obj.__send__(@meth, &func)
  end
end

に相当します。ここで @obj は 元は Enumerator#collect における self、 すなわち配列であり、@meth は :collect、funcは enumerator_with_index_i です。 enumerator_with_index_iですが、これは Ruby のコードに解釈すると

{|n| idx = memo; memo += 1; yield(n,i)}

というブロックを意味します。これを考えあわせて ruby のコードに直してみると、

class Enumerator
  def enumerator_block_call(&func)
    @obj.__send__(@meth, &func)
  end

  def with_index(memo=0)
    enumerator_block_call{|n| idx = memo; memo += 1; yield(n,i)}
  end
end

となります。結果として、

[3, 4, 8].collect.with_index{|n,idx| n+idx}

というコードは、

memo = 0
[3, 4, 8].collect{|n| idx=memo; memo+=1; n+idx}

と同等の動作をすることになります。

Enumerator#each はもう少し簡単で、

enumerator.c:
static VALUE
enumerator_each(VALUE obj)
{
    if (!rb_block_given_p()) return obj;
    return enumerator_block_call(obj, 0, obj);
}

やはり Ruby のコードで相当品を書くと、

class Enumerator
  def each(&b)
    return self if !block_given?
    enumerator_block_call(&b)
  end
end

ここで、rb_block_callのfuncの所にNULLを渡すとメソッドに 渡されたブロックを使うという事実によってこのような解釈ができます。 これは渡されたブロックを@obj.__send__(@meth)にたらい回ししているだけです。

最後に

ary = [1,2,3,4,5]
p ary.collect!.collect{|x| x%2 == 0}
p ary

というコードについて考えてみてください。

  • これがどのような結果になるか実行する前に予想する
  • 実際に動かしてみてどうなるか確認し、予想と比較する
  • なぜそのような結果になったか考えてみる

などすると、より理解が深まるでしょう。


2011-07-24

_ Ruby会議2011

忘れないうちに感想を。

Next version of Ruby 1.8 and 1.9 We will release Ruby: 2.0に 対するイメージははコアチームの中でも大きな差異があるようだ、と感じました。

Profiler: 発表では非常に完成度が高いように見えた。ruby-profとの性能差には何か 工夫があるのだろうか。

図書館 & クリティカルミッション: 実システムの話は聞いてておもしろい。

闇: 基調講演。

padrino良さそう。Webアプリケーションを作る予定はまったくないのですが。

Drip: 追記型アーキテクチャとtuple space的アイデアの融合、というのが とてもすばらしい。いや、tuple spaceからはかなり離れてしまっているんだけど。 なんか使ってみたいことです。

Mac Bookは外見が一様に見える(多様性がない)のが威圧感を与えるのではないか説。

lockの話: あのような話をあれだけわかりやすく話すのはすごい。何を話して 何を話さないかの選択が良いのかな。

rurima: るりまプロジェクトの一員として数言。1.リファレンスマニュアルで重要なことは 一通りすべての要素にドキュメントがあること。 まあTKとかやってられませんが。2. 基本的には時間と手をかければそれだけ進みます。 逆にそうしなければ進まないんですが。3.るりまプロジェクトが遅れているというのは まあそういう話です。

rubima: 長続きしているよなあ。インタビューは毎回楽しみにしております。

testing frameworkの作りかた: 判断基準を一貫することが重要という話。

O/R mapper: 「なるほど」というより「そうそうその通り」という。


2011-07-18

_ Ruby会議2011

参加してきました。すばらしい会議でした。 すべての関係者に感謝を。

LT の発表スライドを公開します。 <URL:http://www.kmc.gr.jp/~ohai/rubykaigi2011/p.pdf>

時間配分がうまくいかなかったので最後までいけませんでした。 この場合コードの所を(説明はできなかったかもしれませんが) 時間を取るようにしたほうがよかったかもしれません。

このネタは渾身の一作なんですが、あまりうけが良くなかった気がします。 面白がってくれる人の範囲が狭すぎたかもしれません。 5ページ目の「良く知られている」ことをよく知っている人でないと通じない 気がします。

何かどうにかしたい所です。

おまけ2の部分は発表後に思いついたものを追加してあります。


2003|12|
2004|01|02|03|04|05|06|07|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|06|08|10|11|
2008|01|02|03|04|05|07|09|
2009|01|02|07|09|12|
2010|01|02|03|04|05|07|08|09|10|
2011|05|06|07|08|09|11|12|
2012|02|03|04|
トップ 追記
RDF