On Aug 4, 2009, at 3:45 PM, Matthew K. Williams wrote:
“11” like this loop does:
- It then uses #succ to try to expand the range, but since “100” has
more characters than “11”, it stops…
Hope I’ve not muddied it too much…
Matt
Well, the Range#to_a is actually Enumerable#to_a and uses Range#each
defined in range.c
After checking that the beginning of the range responds to :succ and
if it is a Fixnum (which are special), it finds that the Range.begin
is a String:
else if (TYPE(beg) == T_STRING) {
VALUE args[5];
long iter[2];
args[0] = beg;
args[1] = end;
args[2] = range;
iter[0] = 1;
iter[1] = 1;
rb_iterate(str_step, (VALUE)args, step_i, (VALUE)iter);
}
str_step calls rb_str_upto defined in string.c
VALUE
rb_str_upto(VALUE beg, VALUE end, int excl)
{
VALUE current, after_end;
ID succ = rb_intern(“succ”);
int n;
StringValue(end);
n = rb_str_cmp(beg, end);
if (n > 0 || (excl && n == 0)) return beg;
after_end = rb_funcall(end, succ, 0, 0);
current = beg;
while (!rb_str_equal(current, after_end)) {
rb_yield(current);
if (!excl && rb_str_equal(current, end)) break;
current = rb_funcall(current, succ, 0, 0);
StringValue(current);
if (excl && rb_str_equal(current, end)) break;
StringValue(current);
if (RSTRING_LEN(current) > RSTRING_LEN(end) || RSTRING_LEN(current)
== 0)
break;
}
return beg;
}
Now, not having read a lot of Ruby’s C code, I’m not sure what some
bits are for (like calling StringValue(current) so much), but it does
ultimately behave almost like Matt said. The difference being that
the rb_yield(current) has already happened once before the length
check (RSTRING_LEN(current) > RSTRING_LEN(end)). I think the
RSTRING_LEN(current)==0 is there to catch “”.succ == “”, but that just
means that (“”…any).to_a is [“”] and yet (“”…“”).to_a is [] (because
after_end will be “” and the loop is never entered).
So it’s the odd situation that String is given some special treatment
and has the unusual property that there are strings a,b such that:
a < b && a.length > b.length
Knowing this, here’s an even more bizzare-looking example:
irb> “19”.succ
=> “20”
irb> (“2”…“19”).to_a
=> []
irb> (“2”…“20”).to_a
=> [“2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “10”, “11”, “12”, “13”,
“14”, “15”, “16”, “17”, “18”, “19”]
-Rob
Rob B. http://agileconsultingllc.com
[email protected]