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

Generated on Sat Jan 22 14:29:25 2005 for Ruby by doxygen1.2.18