String#[]
Ruby 初心者スレッド Part 19
http://pc11.2ch.net/test/read.cgi/tech/1208100393/
より。
496 名前:デフォルトの名無しさん[] 投稿日:2008/05/11(日) 11:38:08
str0 = "bar"
の時、マニュアルによると
p str0[3, 1] #=> nil
らしいですが、自分の環境(1.8.6 mswin32)では""が返ってきます
何が悪いのでしょうか497 名前:デフォルトの名無しさん[sage] 投稿日:2008/05/11(日) 11:54:30
'bar'[2, 1] #=> 'r'
'bar'[3, 1] #=> ''
'bar'[4, 1] #=> nil本当だ。なんかバグっぽい挙動だな
5/15追記: 読んだのはruby-1.8.6-p114
void Init_String(){ // 前略 rb_define_method(rb_cString, "[]", rb_str_aref_m, -1); // 後略 }
とのことなので、rb_str_aref_mを見る。
static VALUE rb_str_aref_m(int argc, VALUE *argv, VALUE str){ if (argc == 2) { if (TYPE(argv[0]) == T_REGEXP) { return rb_str_subpat(str, argv[0], NUM2INT(argv[1])); } return rb_str_substr(str, NUM2LONG(argv[0]), NUM2LONG(argv[1])); } if (argc != 1) { rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc); } return rb_str_aref(str, argv[0]); }
String#[begin, length] の形式なのでargc == 2のはず。引数にRegexpオブジェクトも
ないので呼ばれるのはrb_str_substrだ。
RHGによるとNUM2LONG()みたいなマクロの名前で、NUM = Fixnum や Bignumらしいので、
VALUEの引数二つをCレベルのlongに変換して、rb_str_substrに渡す。
rb_str_substrに行き着いた。
VALUE rb_str_substr(VALUE str, long beg, long len){ VALUE str2; if (len < 0) return Qnil; // (A) [4, 1]がnilなのはたぶんココから if (beg > RSTRING(str)->len) return Qnil; if (beg < 0) { beg += RSTRING(str)->len; if (beg < 0) return Qnil; } // (B) lenが長すぎる指定だと、begから終りまでに切り詰められる if (beg + len > RSTRING(str)->len) { len = RSTRING(str)->len - beg; } if (len < 0) { len = 0; } if (len == 0) { str2 = rb_str_new5(str,0,0); } else if (len > sizeof(struct RString)/2 && beg + len == RSTRING(str)->len && !FL_TEST(str, STR_ASSOC)) { str2 = rb_str_new4(str); str2 = str_new3(rb_obj_class(str2), str2); RSTRING(str2)->ptr += RSTRING(str2)->len - len; RSTRING(str2)->len = len; } else { str2 = rb_str_new5(str, RSTRING(str)->ptr+beg, len); } OBJ_INFECT(str2, str); return str2; }
[3, 1]だと(B)でlen == 0にされて、その後空文字列を返しちゃうのか。
(A)が
if (beg >= RSTRING(str)->len) return Qnil;
だといいのかな。それともこの挙動が正しくてマニュアルの間違いなんだろうか。
str[3] が nil だからマニュアルどおりのほうがしっくりくるような気はする。