Ruby
2.0.0p247(2013-06-27revision41674)
|
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