From: Josh Cheek Date: 2010-05-15T11:01:16+09:00 Subject: Re: Range on strings. --00163683351ec00dd00486985db3 Content-Type: text/plain; charset=ISO-8859-1 On Fri, May 14, 2010 at 4:40 AM, Vikrant Chaudhary wrote: > Hi, > If I do - > > ('A'..'Z').include?('AA') > > It returns "true", while > > ('A'..'Z').to_a.include?('AA') > > (of course) returns "false". Is it intentional or possibly a bug? > I'm using ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux] on > Ubuntu 10.04 x64 > > I think the problem is that on 1.8 it checks include by seeing if the value is greater than or equal to the beginning value, and less then or equal to the end value, since string comparison would have "A" < "AA" < "Z", it returns true. (http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8/range.c) /* * call-seq: * rng === obj => true or false * rng.member?(val) => true or false * rng.include?(val) => true or false * * Returns true if obj is an element of * rng, false otherwise. Conveniently, * === is the comparison operator used by * case statements. * * case 79 * when 1..50 then print "low\n" * when 51..75 then print "medium\n" * when 76..100 then print "high\n" * end * * produces: * * high */ static VALUE range_include(range, val) VALUE range, val; { VALUE beg, end; beg = rb_ivar_get(range, id_beg); end = rb_ivar_get(range, id_end); if (r_le(beg, val)) { if (EXCL(range)) { if (r_lt(val, end)) return Qtrue; } else { if (r_le(val, end)) return Qtrue; } } return Qfalse; } However, when it converts to an array, that is a method from Enumerable. I'm not initiated enough to figure out how it's C code works, but my understanding is that it uses #succ (I got that from this thread http://www.ruby-forum.com/topic/192758#new, but please correct me if I misunderstood) to iterate from the beginning element to the end element, to create the array. Here is it's code, if you understand it, feel free to explain. (http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8/enum.c) /* * call-seq: * enum.to_a => array * enum.entries => array * * Returns an array containing the items in enum. * * (1..7).to_a #=> [1, 2, 3, 4, 5, 6, 7] * { 'a'=>1, 'b'=>2, 'c'=>3 }.to_a #=> [["a", 1], ["b", 2], ["c", 3]] */ static VALUE enum_to_a(argc, argv, obj) int argc; VALUE *argv; VALUE obj; { VALUE ary = rb_ary_new(); rb_block_call(obj, id_each, argc, argv, collect_all, ary); return ary; } Then Array#include iterates through each of it's elements to check if it contains the desired element (http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8/array.c) /* * call-seq: * array.include?(obj) -> true or false * * Returns true if the given object is present in * self (that is, if any object == anObject), * false otherwise. * * a = [ "a", "b", "c" ] * a.include?("b") #=> true * a.include?("z") #=> false */ VALUE rb_ary_includes(ary, item) VALUE ary; VALUE item; { long i; for (i=0; ilen; i++) { if (rb_equal(RARRAY(ary)->ptr[i], item)) { return Qtrue; } } return Qfalse; } The elements the array contains are $ ruby -e "p ('A'..'Z').to_a" ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] So "AA" is not found, and thus it returns false. I honestly don't know about 1.9, I tried looking at it's code, but I don't understand it. (http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_9_1/range.c) --00163683351ec00dd00486985db3--