From: Nobuhiro IMAI Date: 2010-12-23T14:29:13+09:00 Subject: [ruby-list:47706] Re: Array#values_at behavior いまいです。 遅くなってすいません。 From: Kouya Date: Wed, 15 Dec 2010 23:21:22 +0900 > kouya です。 > > array.c の value_at の実装をなんとなくみる限りでは、 > Rangeオブジェクトが渡された場合にのみ、 > 範囲チェック関数rb_range_beg_lenを呼び出して、 > begin が範囲外の場合はQnilを返し、次のセレクタの処理に移るといったように見えました。 > ので、意図した動作(仕様?)なのだと思います。 > > // argc は selector の数 > for (i=0; i   // argv[i] は i番目のselector > if (FIXNUM_P(argv[i])) { > rb_ary_push(result, (*func)(obj, FIX2LONG(argv[i]))); >    // selector がfixnum の場合は一つずつ要素を配列につっこむ。 > continue; > } > /* check if idx is Range */ > switch (rb_range_beg_len(argv[i], &beg, &len, olen, 0)) { > // rb_range_beg_len は beginが範囲内にないと Qnil をかえす。たぶん。 > case Qfalse: > break; > case Qnil: > continue; > default: > for (j=0; j rb_ary_push(result, (*func)(obj, j+beg)); > } > continue; > } >   // continue するので、ここには到達しない。begin が範囲外の場合は無視されている。 > rb_ary_push(result, (*func)(obj, NUM2LONG(argv[i]))); > } ありがとうございます。Qfalse/Qnil の辺りの理解があやふやですが、おおむ ね理解出来ました。 > なお、ソースコードの使用例にもセレクターに二つ以上のRangeオブジェクトが指定できると書いてありました。 > > $ ruby-19-trunk -ve 'p [1,2,3].values_at(-4..3, 1..2, 0..10)' > ruby 1.9.3dev (2010-11-07 trunk 29711) [x86_64-darwin10.4.3] > [2, 3, 1, 2, 3, nil] * -4..3 #=> begin が範囲外なので無視 * 1..2 #=> 前半の [2, 3] * 0..10 #=> 後半の [1, 2, 3, nil] という対応ですね。 > ですので、説明を書くならば、 > 「セレクターにRange オブジェクトを渡した場合、begin が範囲外の場合はその範囲は無視されます。 > end が範囲外ならば、nil が代入され、それ以降の範囲は無視します。」 > といったかんじでしょうか。 2文目は、 「Range の途中で配列の範囲外になると nil を代入?して、それ以降の範囲は 無視します。」 でしょうか。上の例で言うと、0..10 という Range のうち、0、1、2、までは 配列の範囲内、3の時点で範囲外になるのでnil、4から10までは無視、という 感じです。 # rb_range_beg_len は、その辺を考慮した「長さ」をlenにセットするんです # ね。たぶん。 -- Nobuhiro IMAI Key fingerprint = E57F 2482 4074 13BC 3B9A 165B C689 5B16 A620 4657