Ruby  2.0.0p247(2013-06-27revision41674)
compar.c
Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   compar.c -
00004 
00005   $Author: drbrain $
00006   created at: Thu Aug 26 14:39:48 JST 1993
00007 
00008   Copyright (C) 1993-2007 Yukihiro Matsumoto
00009 
00010 **********************************************************************/
00011 
00012 #include "ruby/ruby.h"
00013 
00014 VALUE rb_mComparable;
00015 
00016 static ID cmp;
00017 
00018 void
00019 rb_cmperr(VALUE x, VALUE y)
00020 {
00021     const char *classname;
00022 
00023     if (SPECIAL_CONST_P(y)) {
00024         y = rb_inspect(y);
00025         classname = StringValuePtr(y);
00026     }
00027     else {
00028         classname = rb_obj_classname(y);
00029     }
00030     rb_raise(rb_eArgError, "comparison of %s with %s failed",
00031              rb_obj_classname(x), classname);
00032 }
00033 
00034 static VALUE
00035 invcmp_recursive(VALUE x, VALUE y, int recursive)
00036 {
00037     if (recursive) return Qnil;
00038     return rb_check_funcall(y, cmp, 1, &x);
00039 }
00040 
00041 VALUE
00042 rb_invcmp(VALUE x, VALUE y)
00043 {
00044     VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y);
00045     if (invcmp == Qundef || NIL_P(invcmp)) {
00046         return Qnil;
00047     }
00048     else {
00049         int result = -rb_cmpint(invcmp, x, y);
00050         return INT2FIX(result);
00051     }
00052 }
00053 
00054 static VALUE
00055 cmp_eq(VALUE *a)
00056 {
00057     VALUE c = rb_funcall(a[0], cmp, 1, a[1]);
00058 
00059     if (NIL_P(c)) return Qfalse;
00060     if (rb_cmpint(c, a[0], a[1]) == 0) return Qtrue;
00061     return Qfalse;
00062 }
00063 
00064 static VALUE
00065 cmp_failed(void)
00066 {
00067     return Qfalse;
00068 }
00069 
00070 /*
00071  *  call-seq:
00072  *     obj == other    -> true or false
00073  *
00074  *  Compares two objects based on the receiver's <code><=></code>
00075  *  method, returning true if it returns 0. Also returns true if
00076  *  _obj_ and _other_ are the same object.
00077  *
00078  *  Even if _obj_ <=> _other_ raised an exception, the exception
00079  *  is ignoread and returns false.
00080  */
00081 
00082 static VALUE
00083 cmp_equal(VALUE x, VALUE y)
00084 {
00085     VALUE a[2];
00086 
00087     if (x == y) return Qtrue;
00088 
00089     a[0] = x; a[1] = y;
00090     return rb_rescue(cmp_eq, (VALUE)a, cmp_failed, 0);
00091 }
00092 
00093 /*
00094  *  call-seq:
00095  *     obj > other    -> true or false
00096  *
00097  *  Compares two objects based on the receiver's <code><=></code>
00098  *  method, returning true if it returns 1.
00099  */
00100 
00101 static VALUE
00102 cmp_gt(VALUE x, VALUE y)
00103 {
00104     VALUE c = rb_funcall(x, cmp, 1, y);
00105 
00106     if (rb_cmpint(c, x, y) > 0) return Qtrue;
00107     return Qfalse;
00108 }
00109 
00110 /*
00111  *  call-seq:
00112  *     obj >= other    -> true or false
00113  *
00114  *  Compares two objects based on the receiver's <code><=></code>
00115  *  method, returning true if it returns 0 or 1.
00116  */
00117 
00118 static VALUE
00119 cmp_ge(VALUE x, VALUE y)
00120 {
00121     VALUE c = rb_funcall(x, cmp, 1, y);
00122 
00123     if (rb_cmpint(c, x, y) >= 0) return Qtrue;
00124     return Qfalse;
00125 }
00126 
00127 /*
00128  *  call-seq:
00129  *     obj < other    -> true or false
00130  *
00131  *  Compares two objects based on the receiver's <code><=></code>
00132  *  method, returning true if it returns -1.
00133  */
00134 
00135 static VALUE
00136 cmp_lt(VALUE x, VALUE y)
00137 {
00138     VALUE c = rb_funcall(x, cmp, 1, y);
00139 
00140     if (rb_cmpint(c, x, y) < 0) return Qtrue;
00141     return Qfalse;
00142 }
00143 
00144 /*
00145  *  call-seq:
00146  *     obj <= other    -> true or false
00147  *
00148  *  Compares two objects based on the receiver's <code><=></code>
00149  *  method, returning true if it returns -1 or 0.
00150  */
00151 
00152 static VALUE
00153 cmp_le(VALUE x, VALUE y)
00154 {
00155     VALUE c = rb_funcall(x, cmp, 1, y);
00156 
00157     if (rb_cmpint(c, x, y) <= 0) return Qtrue;
00158     return Qfalse;
00159 }
00160 
00161 /*
00162  *  call-seq:
00163  *     obj.between?(min, max)    -> true or false
00164  *
00165  *  Returns <code>false</code> if <i>obj</i> <code><=></code>
00166  *  <i>min</i> is less than zero or if <i>anObject</i> <code><=></code>
00167  *  <i>max</i> is greater than zero, <code>true</code> otherwise.
00168  *
00169  *     3.between?(1, 5)               #=> true
00170  *     6.between?(1, 5)               #=> false
00171  *     'cat'.between?('ant', 'dog')   #=> true
00172  *     'gnu'.between?('ant', 'dog')   #=> false
00173  *
00174  */
00175 
00176 static VALUE
00177 cmp_between(VALUE x, VALUE min, VALUE max)
00178 {
00179     if (RTEST(cmp_lt(x, min))) return Qfalse;
00180     if (RTEST(cmp_gt(x, max))) return Qfalse;
00181     return Qtrue;
00182 }
00183 
00184 /*
00185  *  The <code>Comparable</code> mixin is used by classes whose objects
00186  *  may be ordered. The class must define the <code><=></code> operator,
00187  *  which compares the receiver against another object, returning -1, 0,
00188  *  or +1 depending on whether the receiver is less than, equal to, or
00189  *  greater than the other object. If the other object is not comparable
00190  *  then the <code><=></code> operator should return nil.
00191  *  <code>Comparable</code> uses
00192  *  <code><=></code> to implement the conventional comparison operators
00193  *  (<code><</code>, <code><=</code>, <code>==</code>, <code>>=</code>,
00194  *  and <code>></code>) and the method <code>between?</code>.
00195  *
00196  *     class SizeMatters
00197  *       include Comparable
00198  *       attr :str
00199  *       def <=>(anOther)
00200  *         str.size <=> anOther.str.size
00201  *       end
00202  *       def initialize(str)
00203  *         @str = str
00204  *       end
00205  *       def inspect
00206  *         @str
00207  *       end
00208  *     end
00209  *
00210  *     s1 = SizeMatters.new("Z")
00211  *     s2 = SizeMatters.new("YY")
00212  *     s3 = SizeMatters.new("XXX")
00213  *     s4 = SizeMatters.new("WWWW")
00214  *     s5 = SizeMatters.new("VVVVV")
00215  *
00216  *     s1 < s2                       #=> true
00217  *     s4.between?(s1, s3)           #=> false
00218  *     s4.between?(s3, s5)           #=> true
00219  *     [ s3, s2, s5, s4, s1 ].sort   #=> [Z, YY, XXX, WWWW, VVVVV]
00220  *
00221  */
00222 
00223 void
00224 Init_Comparable(void)
00225 {
00226 #undef rb_intern
00227 #define rb_intern(str) rb_intern_const(str)
00228 
00229     rb_mComparable = rb_define_module("Comparable");
00230     rb_define_method(rb_mComparable, "==", cmp_equal, 1);
00231     rb_define_method(rb_mComparable, ">", cmp_gt, 1);
00232     rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
00233     rb_define_method(rb_mComparable, "<", cmp_lt, 1);
00234     rb_define_method(rb_mComparable, "<=", cmp_le, 1);
00235     rb_define_method(rb_mComparable, "between?", cmp_between, 2);
00236 
00237     cmp = rb_intern("<=>");
00238 }
00239