Ruby  1.9.3p537(2014-02-19revision0)
compar.c
Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   compar.c -
00004 
00005   $Author$
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 cmp_eq_recursive(VALUE arg1, VALUE arg2, int recursive)
00036 {
00037     if (recursive) return Qfalse;
00038     return rb_funcall2(arg1, cmp, 1, &arg2);
00039 }
00040 
00041 static VALUE
00042 cmp_eq(VALUE *a)
00043 {
00044     VALUE c = rb_exec_recursive_paired_outer(cmp_eq_recursive, a[0], a[1], a[1]);
00045 
00046     if (NIL_P(c)) return Qfalse;
00047     if (rb_cmpint(c, a[0], a[1]) == 0) return Qtrue;
00048     return Qfalse;
00049 }
00050 
00051 static VALUE
00052 cmp_failed(void)
00053 {
00054     return Qfalse;
00055 }
00056 
00057 /*
00058  *  call-seq:
00059  *     obj == other    -> true or false
00060  *
00061  *  Compares two objects based on the receiver's <code><=></code>
00062  *  method, returning true if it returns 0. Also returns true if
00063  *  _obj_ and _other_ are the same object.
00064  */
00065 
00066 static VALUE
00067 cmp_equal(VALUE x, VALUE y)
00068 {
00069     VALUE a[2];
00070 
00071     if (x == y) return Qtrue;
00072 
00073     a[0] = x; a[1] = y;
00074     return rb_rescue(cmp_eq, (VALUE)a, cmp_failed, 0);
00075 }
00076 
00077 /*
00078  *  call-seq:
00079  *     obj > other    -> true or false
00080  *
00081  *  Compares two objects based on the receiver's <code><=></code>
00082  *  method, returning true if it returns 1.
00083  */
00084 
00085 static VALUE
00086 cmp_gt(VALUE x, VALUE y)
00087 {
00088     VALUE c = rb_funcall(x, cmp, 1, y);
00089 
00090     if (rb_cmpint(c, x, y) > 0) return Qtrue;
00091     return Qfalse;
00092 }
00093 
00094 /*
00095  *  call-seq:
00096  *     obj >= other    -> true or false
00097  *
00098  *  Compares two objects based on the receiver's <code><=></code>
00099  *  method, returning true if it returns 0 or 1.
00100  */
00101 
00102 static VALUE
00103 cmp_ge(VALUE x, VALUE y)
00104 {
00105     VALUE c = rb_funcall(x, cmp, 1, y);
00106 
00107     if (rb_cmpint(c, x, y) >= 0) return Qtrue;
00108     return Qfalse;
00109 }
00110 
00111 /*
00112  *  call-seq:
00113  *     obj < other    -> true or false
00114  *
00115  *  Compares two objects based on the receiver's <code><=></code>
00116  *  method, returning true if it returns -1.
00117  */
00118 
00119 static VALUE
00120 cmp_lt(VALUE x, VALUE y)
00121 {
00122     VALUE c = rb_funcall(x, cmp, 1, y);
00123 
00124     if (rb_cmpint(c, x, y) < 0) return Qtrue;
00125     return Qfalse;
00126 }
00127 
00128 /*
00129  *  call-seq:
00130  *     obj <= other    -> true or false
00131  *
00132  *  Compares two objects based on the receiver's <code><=></code>
00133  *  method, returning true if it returns -1 or 0.
00134  */
00135 
00136 static VALUE
00137 cmp_le(VALUE x, VALUE y)
00138 {
00139     VALUE c = rb_funcall(x, cmp, 1, y);
00140 
00141     if (rb_cmpint(c, x, y) <= 0) return Qtrue;
00142     return Qfalse;
00143 }
00144 
00145 /*
00146  *  call-seq:
00147  *     obj.between?(min, max)    -> true or false
00148  *
00149  *  Returns <code>false</code> if <i>obj</i> <code><=></code>
00150  *  <i>min</i> is less than zero or if <i>anObject</i> <code><=></code>
00151  *  <i>max</i> is greater than zero, <code>true</code> otherwise.
00152  *
00153  *     3.between?(1, 5)               #=> true
00154  *     6.between?(1, 5)               #=> false
00155  *     'cat'.between?('ant', 'dog')   #=> true
00156  *     'gnu'.between?('ant', 'dog')   #=> false
00157  *
00158  */
00159 
00160 static VALUE
00161 cmp_between(VALUE x, VALUE min, VALUE max)
00162 {
00163     if (RTEST(cmp_lt(x, min))) return Qfalse;
00164     if (RTEST(cmp_gt(x, max))) return Qfalse;
00165     return Qtrue;
00166 }
00167 
00168 /*
00169  *  The <code>Comparable</code> mixin is used by classes whose objects
00170  *  may be ordered. The class must define the <code><=></code> operator,
00171  *  which compares the receiver against another object, returning -1, 0,
00172  *  or +1 depending on whether the receiver is less than, equal to, or
00173  *  greater than the other object. If the other object is not comparable
00174  *  then the <code><=></code> operator should return nil.
00175  *  <code>Comparable</code> uses
00176  *  <code><=></code> to implement the conventional comparison operators
00177  *  (<code><</code>, <code><=</code>, <code>==</code>, <code>>=</code>,
00178  *  and <code>></code>) and the method <code>between?</code>.
00179  *
00180  *     class SizeMatters
00181  *       include Comparable
00182  *       attr :str
00183  *       def <=>(anOther)
00184  *         str.size <=> anOther.str.size
00185  *       end
00186  *       def initialize(str)
00187  *         @str = str
00188  *       end
00189  *       def inspect
00190  *         @str
00191  *       end
00192  *     end
00193  *
00194  *     s1 = SizeMatters.new("Z")
00195  *     s2 = SizeMatters.new("YY")
00196  *     s3 = SizeMatters.new("XXX")
00197  *     s4 = SizeMatters.new("WWWW")
00198  *     s5 = SizeMatters.new("VVVVV")
00199  *
00200  *     s1 < s2                       #=> true
00201  *     s4.between?(s1, s3)           #=> false
00202  *     s4.between?(s3, s5)           #=> true
00203  *     [ s3, s2, s5, s4, s1 ].sort   #=> [Z, YY, XXX, WWWW, VVVVV]
00204  *
00205  */
00206 
00207 void
00208 Init_Comparable(void)
00209 {
00210 #undef rb_intern
00211 #define rb_intern(str) rb_intern_const(str)
00212 
00213     rb_mComparable = rb_define_module("Comparable");
00214     rb_define_method(rb_mComparable, "==", cmp_equal, 1);
00215     rb_define_method(rb_mComparable, ">", cmp_gt, 1);
00216     rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
00217     rb_define_method(rb_mComparable, "<", cmp_lt, 1);
00218     rb_define_method(rb_mComparable, "<=", cmp_le, 1);
00219     rb_define_method(rb_mComparable, "between?", cmp_between, 2);
00220 
00221     cmp = rb_intern("<=>");
00222 }
00223