Ruby 2.0.0p247(2013-06-27revision41674)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

compar.c
Go to the documentation of this file.
1 /**********************************************************************
2
3 compar.c -
4
5 $Author: drbrain $
6 created at: Thu Aug 26 14:39:48 JST 1993
7
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9
10 **********************************************************************/
11
12 #include "ruby/ruby.h"
13
14 VALUE rb_mComparable;
15
16 static ID cmp;
17
18 void
19 rb_cmperr(VALUE x, VALUE y)
20 {
21 const char *classname;
22
23 if (SPECIAL_CONST_P(y)) {
24 y = rb_inspect(y);
25 classname = StringValuePtr(y);
26 }
27 else {
28 classname = rb_obj_classname(y);
29 }
30 rb_raise(rb_eArgError, "comparison of %s with %s failed",
31 rb_obj_classname(x), classname);
32 }
33
34 static VALUE
35 invcmp_recursive(VALUE x, VALUE y, int recursive)
36 {
37 if (recursive) return Qnil;
38 return rb_check_funcall(y, cmp, 1, &x);
39 }
40
41 VALUE
42 rb_invcmp(VALUE x, VALUE y)
43 {
44 VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y);
45 if (invcmp == Qundef || NIL_P(invcmp)) {
46 return Qnil;
47 }
48 else {
49 int result = -rb_cmpint(invcmp, x, y);
50 return INT2FIX(result);
51 }
52 }
53
54 static VALUE
55 cmp_eq(VALUE *a)
56 {
57 VALUE c = rb_funcall(a[0], cmp, 1, a[1]);
58
59 if (NIL_P(c)) return Qfalse;
60 if (rb_cmpint(c, a[0], a[1]) == 0) return Qtrue;
61 return Qfalse;
62 }
63
64 static VALUE
65 cmp_failed(void)
66 {
67 return Qfalse;
68 }
69
70 /*
71 * call-seq:
72 * obj == other -> true or false
73 *
74 * Compares two objects based on the receiver's <code><=></code>
75 * method, returning true if it returns 0. Also returns true if
76 * _obj_ and _other_ are the same object.
77 *
78 * Even if _obj_ <=> _other_ raised an exception, the exception
79 * is ignoread and returns false.
80 */
81
82 static VALUE
83 cmp_equal(VALUE x, VALUE y)
84 {
85 VALUE a[2];
86
87 if (x == y) return Qtrue;
88
89 a[0] = x; a[1] = y;
90 return rb_rescue(cmp_eq, (VALUE)a, cmp_failed, 0);
91 }
92
93 /*
94 * call-seq:
95 * obj > other -> true or false
96 *
97 * Compares two objects based on the receiver's <code><=></code>
98 * method, returning true if it returns 1.
99 */
100
101 static VALUE
102 cmp_gt(VALUE x, VALUE y)
103 {
104 VALUE c = rb_funcall(x, cmp, 1, y);
105
106 if (rb_cmpint(c, x, y) > 0) return Qtrue;
107 return Qfalse;
108 }
109
110 /*
111 * call-seq:
112 * obj >= other -> true or false
113 *
114 * Compares two objects based on the receiver's <code><=></code>
115 * method, returning true if it returns 0 or 1.
116 */
117
118 static VALUE
119 cmp_ge(VALUE x, VALUE y)
120 {
121 VALUE c = rb_funcall(x, cmp, 1, y);
122
123 if (rb_cmpint(c, x, y) >= 0) return Qtrue;
124 return Qfalse;
125 }
126
127 /*
128 * call-seq:
129 * obj < other -> true or false
130 *
131 * Compares two objects based on the receiver's <code><=></code>
132 * method, returning true if it returns -1.
133 */
134
135 static VALUE
136 cmp_lt(VALUE x, VALUE y)
137 {
138 VALUE c = rb_funcall(x, cmp, 1, y);
139
140 if (rb_cmpint(c, x, y) < 0) return Qtrue;
141 return Qfalse;
142 }
143
144 /*
145 * call-seq:
146 * obj <= other -> true or false
147 *
148 * Compares two objects based on the receiver's <code><=></code>
149 * method, returning true if it returns -1 or 0.
150 */
151
152 static VALUE
153 cmp_le(VALUE x, VALUE y)
154 {
155 VALUE c = rb_funcall(x, cmp, 1, y);
156
157 if (rb_cmpint(c, x, y) <= 0) return Qtrue;
158 return Qfalse;
159 }
160
161 /*
162 * call-seq:
163 * obj.between?(min, max) -> true or false
164 *
165 * Returns <code>false</code> if <i>obj</i> <code><=></code>
166 * <i>min</i> is less than zero or if <i>anObject</i> <code><=></code>
167 * <i>max</i> is greater than zero, <code>true</code> otherwise.
168 *
169 * 3.between?(1, 5) #=> true
170 * 6.between?(1, 5) #=> false
171 * 'cat'.between?('ant', 'dog') #=> true
172 * 'gnu'.between?('ant', 'dog') #=> false
173 *
174 */
175
176 static VALUE
177 cmp_between(VALUE x, VALUE min, VALUE max)
178 {
179 if (RTEST(cmp_lt(x, min))) return Qfalse;
180 if (RTEST(cmp_gt(x, max))) return Qfalse;
181 return Qtrue;
182 }
183
184 /*
185 * The <code>Comparable</code> mixin is used by classes whose objects
186 * may be ordered. The class must define the <code><=></code> operator,
187 * which compares the receiver against another object, returning -1, 0,
188 * or +1 depending on whether the receiver is less than, equal to, or
189 * greater than the other object. If the other object is not comparable
190 * then the <code><=></code> operator should return nil.
191 * <code>Comparable</code> uses
192 * <code><=></code> to implement the conventional comparison operators
193 * (<code><</code>, <code><=</code>, <code>==</code>, <code>>=</code>,
194 * and <code>></code>) and the method <code>between?</code>.
195 *
196 * class SizeMatters
197 * include Comparable
198 * attr :str
199 * def <=>(anOther)
200 * str.size <=> anOther.str.size
201 * end
202 * def initialize(str)
203 * @str = str
204 * end
205 * def inspect
206 * @str
207 * end
208 * end
209 *
210 * s1 = SizeMatters.new("Z")
211 * s2 = SizeMatters.new("YY")
212 * s3 = SizeMatters.new("XXX")
213 * s4 = SizeMatters.new("WWWW")
214 * s5 = SizeMatters.new("VVVVV")
215 *
216 * s1 < s2 #=> true
217 * s4.between?(s1, s3) #=> false
218 * s4.between?(s3, s5) #=> true
219 * [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
220 *
221 */
222
223 void
224 Init_Comparable(void)
225 {
226 #undef rb_intern
227 #define rb_intern(str) rb_intern_const(str)
228
229 rb_mComparable = rb_define_module("Comparable");
230 rb_define_method(rb_mComparable, "==", cmp_equal, 1);
231 rb_define_method(rb_mComparable, ">", cmp_gt, 1);
232 rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
233 rb_define_method(rb_mComparable, "<", cmp_lt, 1);
234 rb_define_method(rb_mComparable, "<=", cmp_le, 1);
235 rb_define_method(rb_mComparable, "between?", cmp_between, 2);
236
237 cmp = rb_intern("<=>");
238 }
239