Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals

range.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   range.c -
00004 
00005   $Author: nobu $
00006   $Date: 2005/08/14 15:39:39 $
00007   created at: Thu Aug 19 17:46:47 JST 1993
00008 
00009   Copyright (C) 1993-2003 Yukihiro Matsumoto
00010 
00011 **********************************************************************/
00012 
00013 #include "ruby.h"
00014 
00015 VALUE rb_cRange;
00016 static ID id_cmp, id_succ, id_beg, id_end, id_excl;
00017 
00018 #define EXCL(r) RTEST(rb_ivar_get((r), id_excl))
00019 #define SET_EXCL(r,v) rb_ivar_set((r), id_excl, (v) ? Qtrue : Qfalse)
00020 
00021 static VALUE
00022 range_failed()
00023 {
00024     rb_raise(rb_eArgError, "bad value for range");
00025     return Qnil;                /* dummy */
00026 }
00027 
00028 static VALUE
00029 range_check(args)
00030     VALUE *args;
00031 {
00032     return rb_funcall(args[0], id_cmp, 1, args[1]);
00033 }
00034 
00035 static void
00036 range_init(range, beg, end, exclude_end)
00037     VALUE range, beg, end;
00038     int exclude_end;
00039 {
00040     VALUE args[2];
00041 
00042     args[0] = beg;
00043     args[1] = end;
00044     
00045     if (!FIXNUM_P(beg) || !FIXNUM_P(end)) {
00046         VALUE v;
00047 
00048         v = rb_rescue(range_check, (VALUE)args, range_failed, 0);
00049         if (NIL_P(v)) range_failed();
00050     }
00051 
00052     SET_EXCL(range, exclude_end);
00053     rb_ivar_set(range, id_beg, beg);
00054     rb_ivar_set(range, id_end, end);
00055 }
00056 
00057 VALUE
00058 rb_range_new(beg, end, exclude_end)
00059     VALUE beg, end;
00060     int exclude_end;
00061 {
00062     VALUE range = rb_obj_alloc(rb_cRange);
00063 
00064     range_init(range, beg, end, exclude_end);
00065     return range;
00066 }
00067 
00068 /*
00069  *  call-seq:
00070  *     Range.new(start, end, exclusive=false)    => range
00071  *  
00072  *  Constructs a range using the given <i>start</i> and <i>end</i>. If the third
00073  *  parameter is omitted or is <code>false</code>, the <i>range</i> will include
00074  *  the end object; otherwise, it will be excluded.
00075  */
00076 
00077 static VALUE
00078 range_initialize(argc, argv, range)
00079     int argc;
00080     VALUE *argv;
00081     VALUE range;
00082 {
00083     VALUE beg, end, flags;
00084     
00085     rb_scan_args(argc, argv, "21", &beg, &end, &flags);
00086     /* Ranges are immutable, so that they should be initialized only once. */
00087     if (rb_ivar_defined(range, id_beg)) {
00088         rb_name_error(rb_intern("initialize"), "`initialize' called twice");
00089     }
00090     range_init(range, beg, end, RTEST(flags));
00091     return Qnil;
00092 }
00093 
00094 
00095 /*
00096  *  call-seq:
00097  *     rng.exclude_end?    => true or false
00098  *  
00099  *  Returns <code>true</code> if <i>rng</i> excludes its end value.
00100  */
00101 
00102 static VALUE
00103 range_exclude_end_p(range)
00104     VALUE range;
00105 {
00106     return EXCL(range) ? Qtrue : Qfalse;
00107 }
00108 
00109 
00110 /*
00111  *  call-seq:
00112  *     rng == obj    => true or false
00113  *  
00114  *  Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
00115  *  beginning and end items (by comparing them with <code>==</code>), and has
00116  *  the same #exclude_end? setting as <i>rng</t>.
00117  *     
00118  *    (0..2) == (0..2)            #=> true
00119  *    (0..2) == Range.new(0,2)    #=> true
00120  *    (0..2) == (0...2)           #=> false
00121  *     
00122  */
00123 
00124 static VALUE
00125 range_eq(range, obj)
00126     VALUE range, obj;
00127 {
00128     if (range == obj) return Qtrue;
00129     if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
00130         return Qfalse;
00131 
00132     if (!rb_equal(rb_ivar_get(range, id_beg), rb_ivar_get(obj, id_beg)))
00133         return Qfalse;
00134     if (!rb_equal(rb_ivar_get(range, id_end), rb_ivar_get(obj, id_end)))
00135         return Qfalse;
00136 
00137     if (EXCL(range) != EXCL(obj)) return Qfalse;
00138 
00139     return Qtrue;
00140 }
00141 
00142 static int
00143 r_lt(a, b)
00144     VALUE a, b;
00145 {
00146     VALUE r = rb_funcall(a, id_cmp, 1, b);
00147 
00148     if (NIL_P(r)) return Qfalse;
00149     if (rb_cmpint(r, a, b) < 0) return Qtrue;
00150     return Qfalse;
00151 }
00152 
00153 static int
00154 r_le(a, b)
00155     VALUE a, b;
00156 {
00157     int c;
00158     VALUE r = rb_funcall(a, id_cmp, 1, b);
00159 
00160     if (NIL_P(r)) return Qfalse;
00161     c = rb_cmpint(r, a, b);
00162     if (c == 0) return INT2FIX(0);
00163     if (c < 0) return Qtrue;
00164     return Qfalse;
00165 }
00166 
00167 
00168 /*
00169  *  call-seq:
00170  *     rng.eql?(obj)    => true or false
00171  *  
00172  *  Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
00173  *  beginning and end items (by comparing them with #eql?), and has the same
00174  *  #exclude_end? setting as <i>rng</i>.
00175  *     
00176  *    (0..2) == (0..2)            #=> true
00177  *    (0..2) == Range.new(0,2)    #=> true
00178  *    (0..2) == (0...2)           #=> false
00179  *     
00180  */
00181 
00182 static VALUE
00183 range_eql(range, obj)
00184     VALUE range, obj;
00185 {
00186     if (range == obj) return Qtrue;
00187     if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
00188         return Qfalse;
00189 
00190     if (!rb_eql(rb_ivar_get(range, id_beg), rb_ivar_get(obj, id_beg)))
00191         return Qfalse;
00192     if (!rb_eql(rb_ivar_get(range, id_end), rb_ivar_get(obj, id_end)))
00193         return Qfalse;
00194 
00195     if (EXCL(range) != EXCL(obj)) return Qfalse;
00196 
00197     return Qtrue;
00198 }
00199 
00200 /*
00201  * call-seq:
00202  *   rng.hash    => fixnum
00203  *
00204  * Generate a hash value such that two ranges with the same start and
00205  * end points, and the same value for the "exclude end" flag, generate
00206  * the same hash value.
00207  */
00208 
00209 static VALUE
00210 range_hash(range)
00211     VALUE range;
00212 {
00213     long hash = EXCL(range);
00214     VALUE v;
00215 
00216     v = rb_hash(rb_ivar_get(range, id_beg));
00217     hash ^= v << 1;
00218     v = rb_hash(rb_ivar_get(range, id_end));
00219     hash ^= v << 9;
00220     hash ^= EXCL(range) << 24;
00221 
00222     return LONG2FIX(hash);
00223 }
00224 
00225 static VALUE
00226 str_step(args)
00227     VALUE *args;
00228 {
00229     return rb_str_upto(args[0], args[1], EXCL(args[2]));
00230 }
00231 
00232 static void
00233 range_each_func(range, func, v, e, arg)
00234     VALUE range;
00235     void (*func) (VALUE, void*);
00236     VALUE v, e;
00237     void *arg;
00238 {
00239     int c;
00240 
00241     if (EXCL(range)) {
00242         while (r_lt(v, e)) {
00243             (*func)(v, arg);
00244             v = rb_funcall(v, id_succ, 0, 0);
00245         }
00246     }
00247     else {
00248         while (RTEST(c = r_le(v, e))) {
00249             (*func)(v, arg);
00250             if (c == INT2FIX(0)) break;
00251             v = rb_funcall(v, id_succ, 0, 0);
00252         }
00253     }
00254 }
00255 
00256 static VALUE
00257 step_i(i, iter)
00258     VALUE i;
00259     long *iter;
00260 {
00261     iter[0]--;
00262     if (iter[0] == 0) {
00263         rb_yield(i);
00264         iter[0] = iter[1];
00265     }
00266     return Qnil;
00267 }
00268 
00269 /*
00270  *  call-seq:
00271  *     rng.step(n=1) {| obj | block }    => rng
00272  *  
00273  *  Iterates over <i>rng</i>, passing each <i>n</i>th element to the block. If
00274  *  the range contains numbers or strings, natural ordering is used.  Otherwise
00275  *  <code>step</code> invokes <code>succ</code> to iterate through range
00276  *  elements. The following code uses class <code>Xs</code>, which is defined
00277  *  in the class-level documentation.
00278  *     
00279  *     range = Xs.new(1)..Xs.new(10)
00280  *     range.step(2) {|x| puts x}
00281  *     range.step(3) {|x| puts x}
00282  *     
00283  *  <em>produces:</em>
00284  *     
00285  *      1 x
00286  *      3 xxx
00287  *      5 xxxxx
00288  *      7 xxxxxxx
00289  *      9 xxxxxxxxx
00290  *      1 x
00291  *      4 xxxx
00292  *      7 xxxxxxx
00293  *     10 xxxxxxxxxx
00294  */
00295 
00296 
00297 static VALUE
00298 range_step(argc, argv, range)
00299     int argc;
00300     VALUE *argv;
00301     VALUE range;
00302 {
00303     VALUE b, e, step;
00304     long unit;
00305 
00306     b = rb_ivar_get(range, id_beg);
00307     e = rb_ivar_get(range, id_end);
00308     if (rb_scan_args(argc, argv, "01", &step) == 0) {
00309         step = INT2FIX(1);
00310     }
00311 
00312     unit = NUM2LONG(step);
00313     if (unit < 0) {
00314         rb_raise(rb_eArgError, "step can't be negative");
00315     } 
00316     if (FIXNUM_P(b) && FIXNUM_P(e)) { /* fixnums are special */
00317         long end = FIX2LONG(e);
00318         long i;
00319 
00320         if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
00321         if (!EXCL(range)) end += 1;
00322         for (i=FIX2LONG(b); i<end; i+=unit) {
00323             rb_yield(LONG2NUM(i));
00324         }
00325     }
00326     else {
00327         VALUE tmp = rb_check_string_type(b);
00328 
00329         if (!NIL_P(tmp)) {
00330             VALUE args[5];
00331             long iter[2];
00332 
00333             b = tmp;
00334             if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
00335             args[0] = b; args[1] = e; args[2] = range;
00336             iter[0] = 1; iter[1] = unit;
00337             rb_iterate((VALUE(*)(VALUE))str_step, (VALUE)args, step_i,
00338                         (VALUE)iter);
00339         }
00340         else if (rb_obj_is_kind_of(b, rb_cNumeric)) {
00341             ID c = rb_intern(EXCL(range) ? "<" : "<=");
00342 
00343             if (rb_equal(step, INT2FIX(0))) rb_raise(rb_eArgError, "step can't be 0");
00344             while (RTEST(rb_funcall(b, c, 1, e))) {
00345                 rb_yield(b);
00346                 b = rb_funcall(b, '+', 1, step);
00347             }
00348         }
00349         else {
00350             long args[2];
00351 
00352             if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
00353             if (!rb_respond_to(b, id_succ)) {
00354                 rb_raise(rb_eTypeError, "can't iterate from %s",
00355                          rb_obj_classname(b));
00356             }
00357         
00358             args[0] = 1;
00359             args[1] = unit;
00360             range_each_func(range, step_i, b, e, args);
00361         }
00362     }
00363     return range;
00364 }
00365 
00366 static void
00367 each_i(v, arg)
00368     VALUE v;
00369     void *arg;
00370 {
00371     rb_yield(v);
00372 }
00373 
00374 /*
00375  *  call-seq:
00376  *     rng.each {| i | block } => rng
00377  *  
00378  *  Iterates over the elements <i>rng</i>, passing each in turn to the
00379  *  block. You can only iterate if the start object of the range
00380  *  supports the +succ+ method (which means that you can't iterate over
00381  *  ranges of +Float+ objects).
00382  *     
00383  *     (10..15).each do |n|
00384  *        print n, ' '
00385  *     end
00386  *     
00387  *  <em>produces:</em>
00388  *     
00389  *     10 11 12 13 14 15
00390  */
00391 
00392 static VALUE
00393 range_each(range)
00394     VALUE range;
00395 {
00396     VALUE beg, end;
00397 
00398     beg = rb_ivar_get(range, id_beg);
00399     end = rb_ivar_get(range, id_end);
00400 
00401     if (!rb_respond_to(beg, id_succ)) {
00402         rb_raise(rb_eTypeError, "can't iterate from %s",
00403                  rb_obj_classname(beg));
00404     }
00405     if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */
00406         long lim = FIX2LONG(end);
00407         long i;
00408 
00409         if (!EXCL(range)) lim += 1;
00410         for (i=FIX2LONG(beg); i<lim; i++) {
00411             rb_yield(LONG2NUM(i));
00412         }
00413     }
00414     else if (TYPE(beg) == T_STRING) {
00415         VALUE args[5];
00416         long iter[2];
00417 
00418         args[0] = beg; args[1] = end; args[2] = range;
00419         iter[0] = 1; iter[1] = 1;
00420         rb_iterate((VALUE(*)(VALUE))str_step, (VALUE)args, step_i,
00421                    (VALUE)iter);
00422     }
00423     else {
00424         range_each_func(range, each_i, beg, end, NULL);
00425     }
00426     return range;
00427 }
00428 
00429 /*
00430  *  call-seq:
00431  *     rng.first    => obj
00432  *     rng.begin    => obj
00433  *  
00434  *  Returns the first object in <i>rng</i>.
00435  */
00436 
00437 static VALUE
00438 range_first(range)
00439     VALUE range;
00440 {
00441     return rb_ivar_get(range, id_beg);
00442 }
00443 
00444 
00445 /*
00446  *  call-seq:
00447  *     rng.end    => obj
00448  *     rng.last   => obj
00449  *  
00450  *  Returns the object that defines the end of <i>rng</i>.
00451  *     
00452  *     (1..10).end    #=> 10
00453  *     (1...10).end   #=> 10
00454  */
00455 
00456 
00457 static VALUE
00458 range_last(range)
00459     VALUE range;
00460 {
00461     return rb_ivar_get(range, id_end);
00462 }
00463 
00464 VALUE
00465 rb_range_beg_len(range, begp, lenp, len, err)
00466     VALUE range;
00467     long *begp, *lenp;
00468     long len;
00469     int err;
00470 {
00471     long beg, end, b, e;
00472 
00473     if (!rb_obj_is_kind_of(range, rb_cRange)) return Qfalse;
00474 
00475     beg = b = NUM2LONG(rb_ivar_get(range, id_beg));
00476     end = e = NUM2LONG(rb_ivar_get(range, id_end));
00477 
00478     if (beg < 0) {
00479         beg += len;
00480         if (beg < 0) goto out_of_range;
00481     }
00482     if (err == 0 || err == 2) {
00483         if (beg > len) goto out_of_range;
00484         if (end > len) end = len;
00485     }
00486     if (end < 0) end += len;
00487     if (!EXCL(range)) end++;    /* include end point */
00488     len = end - beg;
00489     if (len < 0) len = 0;
00490 
00491     *begp = beg;
00492     *lenp = len;
00493     return Qtrue;
00494 
00495   out_of_range:
00496     if (err) {
00497         rb_raise(rb_eRangeError, "%ld..%s%ld out of range",
00498                  b, EXCL(range)? "." : "", e);
00499     }
00500     return Qnil;
00501 }
00502 
00503 /*
00504  * call-seq:
00505  *   rng.to_s   => string
00506  *
00507  * Convert this range object to a printable form.
00508  */
00509 
00510 static VALUE
00511 range_to_s(range)
00512     VALUE range;
00513 {
00514     VALUE str, str2;
00515 
00516     str = rb_obj_as_string(rb_ivar_get(range, id_beg));
00517     str2 = rb_obj_as_string(rb_ivar_get(range, id_end));
00518     str = rb_str_dup(str);
00519     rb_str_cat(str, "...", EXCL(range)?3:2);
00520     rb_str_append(str, str2);
00521     OBJ_INFECT(str, str2);
00522 
00523     return str;
00524 }
00525 
00526 /*
00527  * call-seq:
00528  *   rng.inspect  => string
00529  *
00530  * Convert this range object to a printable form (using 
00531  * <code>inspect</code> to convert the start and end
00532  * objects).
00533  */
00534 
00535 
00536 static VALUE
00537 range_inspect(range)
00538     VALUE range;
00539 {
00540     VALUE str, str2;
00541 
00542     str = rb_inspect(rb_ivar_get(range, id_beg));
00543     str2 = rb_inspect(rb_ivar_get(range, id_end));
00544     str = rb_str_dup(str);
00545     rb_str_cat(str, "...", EXCL(range)?3:2);
00546     rb_str_append(str, str2);
00547     OBJ_INFECT(str, str2);
00548 
00549     return str;
00550 }
00551 
00552 /*
00553  *  call-seq:
00554  *     rng === obj       =>  true or false
00555  *     rng.member?(val)  =>  true or false
00556  *     rng.include?(val) =>  true or false
00557  *  
00558  *  Returns <code>true</code> if <i>obj</i> is an element of
00559  *  <i>rng</i>, <code>false</code> otherwise. Conveniently,
00560  *  <code>===</code> is the comparison operator used by
00561  *  <code>case</code> statements.
00562  *     
00563  *     case 79
00564  *     when 1..50   then   print "low\n"
00565  *     when 51..75  then   print "medium\n"
00566  *     when 76..100 then   print "high\n"
00567  *     end
00568  *     
00569  *  <em>produces:</em>
00570  *     
00571  *     high
00572  */
00573 
00574 static VALUE
00575 range_include(range, val)
00576     VALUE range, val;
00577 {
00578     VALUE beg, end;
00579 
00580     beg = rb_ivar_get(range, id_beg);
00581     end = rb_ivar_get(range, id_end);
00582     if (r_le(beg, val)) {
00583         if (EXCL(range)) {
00584             if (r_lt(val, end)) return Qtrue;
00585         }
00586         else {
00587             if (r_le(val, end)) return Qtrue;
00588         }
00589     }
00590     return Qfalse;
00591 }
00592 
00593 
00594 /*  A <code>Range</code> represents an interval---a set of values with a
00595  *  start and an end. Ranges may be constructed using the
00596  *  <em>s</em><code>..</code><em>e</em> and
00597  *  <em>s</em><code>...</code><em>e</em> literals, or with
00598  *  <code>Range::new</code>. Ranges constructed using <code>..</code>
00599  *  run from the start to the end inclusively. Those created using
00600  *  <code>...</code> exclude the end value. When used as an iterator,
00601  *  ranges return each value in the sequence.
00602  *     
00603  *     (-1..-5).to_a      #=> []
00604  *     (-5..-1).to_a      #=> [-5, -4, -3, -2, -1]
00605  *     ('a'..'e').to_a    #=> ["a", "b", "c", "d", "e"]
00606  *     ('a'...'e').to_a   #=> ["a", "b", "c", "d"]
00607  *     
00608  *  Ranges can be constructed using objects of any type, as long as the
00609  *  objects can be compared using their <code><=></code> operator and
00610  *  they support the <code>succ</code> method to return the next object
00611  *  in sequence.
00612  *     
00613  *     class Xs                # represent a string of 'x's
00614  *       include Comparable
00615  *       attr :length
00616  *       def initialize(n)
00617  *         @length = n
00618  *       end
00619  *       def succ
00620  *         Xs.new(@length + 1)
00621  *       end
00622  *       def <=>(other)
00623  *         @length <=> other.length
00624  *       end
00625  *       def to_s
00626  *         sprintf "%2d #{inspect}", @length
00627  *       end
00628  *       def inspect
00629  *         'x' * @length
00630  *       end
00631  *     end
00632  *     
00633  *     r = Xs.new(3)..Xs.new(6)   #=> xxx..xxxxxx
00634  *     r.to_a                     #=> [xxx, xxxx, xxxxx, xxxxxx]
00635  *     r.member?(Xs.new(5))       #=> true
00636  *     
00637  *  In the previous code example, class <code>Xs</code> includes the
00638  *  <code>Comparable</code> module. This is because
00639  *  <code>Enumerable#member?</code> checks for equality using
00640  *  <code>==</code>. Including <code>Comparable</code> ensures that the
00641  *  <code>==</code> method is defined in terms of the <code><=></code>
00642  *  method implemented in <code>Xs</code>.
00643  *     
00644  */
00645 
00646 void
00647 Init_Range()
00648 {
00649     rb_cRange = rb_define_class("Range", rb_cObject);
00650     rb_include_module(rb_cRange, rb_mEnumerable);
00651     rb_define_method(rb_cRange, "initialize", range_initialize, -1);
00652     rb_define_method(rb_cRange, "==", range_eq, 1);
00653     rb_define_method(rb_cRange, "===", range_include, 1);
00654     rb_define_method(rb_cRange, "eql?", range_eql, 1);
00655     rb_define_method(rb_cRange, "hash", range_hash, 0);
00656     rb_define_method(rb_cRange, "each", range_each, 0);
00657     rb_define_method(rb_cRange, "step", range_step, -1);
00658     rb_define_method(rb_cRange, "first", range_first, 0);
00659     rb_define_method(rb_cRange, "last", range_last, 0);
00660     rb_define_method(rb_cRange, "begin", range_first, 0);
00661     rb_define_method(rb_cRange, "end", range_last, 0);
00662     rb_define_method(rb_cRange, "to_s", range_to_s, 0);
00663     rb_define_method(rb_cRange, "inspect", range_inspect, 0);
00664 
00665     rb_define_method(rb_cRange, "exclude_end?", range_exclude_end_p, 0);
00666 
00667     rb_define_method(rb_cRange, "member?", range_include, 1);
00668     rb_define_method(rb_cRange, "include?", range_include, 1);
00669 
00670     id_cmp = rb_intern("<=>");
00671     id_succ = rb_intern("succ");
00672     id_beg = rb_intern("begin");
00673     id_end = rb_intern("end");
00674     id_excl = rb_intern("excl");
00675 }
00676 

Generated on Wed Jan 18 23:32:06 2006 for Ruby by doxygen 1.3.5