From: "NARUSE, Yui" Date: 2007-06-18T20:09:39+09:00 Subject: [ruby-dev:31019] Re: ("a".."f").step(2) {|x| p x} 成瀬です。 SASADA Koichi wrote: >  ささだです。 > >  成瀬さん、ソースを読んでくれてありがとうございました。ここで > 大事なのは rb_iterate に渡す関数から rb_call0 が呼ばれていない > ということです。 rb_call0 は 1.9 では存在しませんが、th_call0 ですよね。なるほど、本来は rb_funcall 経由で th_call0 が呼ばれるはずだったのですか、通りでソース 追ってもたどり着かないわけだ・・・。 >  rb_iterate が何をするのか、その定義はよくわからなかったので > すが、リファレンスマニュアルをみると次のようになっています。 > >> まず call_proc(data1) を実行します。そしてその関数かその直下のメソッドで yield が発生すると以下が実行されます。 > >  (1)「その関数か」(2)「その直下のメソッド」という定義を、私は > (2) だけ、というように実装したということになります。これは、 > NODE_ITER の使い方、および enum.c での利用法を見てそのように判 > 断しました(てっきり、(2) の使い方だけかと思っていた)。(1) に > 依存したプログラムってどれくらいあるでしょうか。直すのは結構大 > 変そうです。 > >  なので、現在の rb_iterate() の仕様による解決方法は > str_step() で rb_funcall() で何らかのメソッドを呼べばよいとい > うことになります。 これって、一度 Ruby の層まで上らない bl_proc が呼ばれないということです よね。it_proc ほどではないにしても、bl_proc のように何度も呼ばれる部分が 必ず Ruby 層経由というのは少し気になります。 >  「ひとつのスタックフレームには 1 つのブロック渡ししか対応し > ていません」の意味ですが、まず ('a' .. 'b').step(2){ (A) } で > は、Range#step に、1つのブロック渡しをされています。その時点 > で、このスタックフレームには1つのブロックが登録されたことにな > ります。 > >  rb_iterate() を実行すると、「次のメソッド呼び出し時に C で記 > 述したブロックを渡す」という意味となります(同じスタックフレー > ムに複数のブロックは積めない)。なので、Range#step 処理中(同 > じスタックフレーム)で yield したときの挙動は (A) を呼び出す、 > ということになります。たとえば、Range#step が String#step を呼 > び出せば、正しく動きます(exclを渡す方法がないんだけど)。 rb_define_method(rb_cString, "upto_excl", rb_str_upto, 2); とでも定義して、str_step から return rb_funcall(args[0], rb_intern("upto_excl"), 2, args[1], EXCL(args[2])); と呼べばいいのですかね。 なんか冗長な気はしますが。 -- NARUSE, Yui DBDB A476 FDBD 9450 02CD 0EFC BCE3 C388 472E C1EA