Ruby  2.5.0dev(2017-10-22revision60238)
compar.c
Go to the documentation of this file.
1 /**********************************************************************
2 
3  compar.c -
4 
5  $Author$
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 #include "id.h"
14 
16 
17 static VALUE
18 rb_cmp(VALUE x, VALUE y)
19 {
20  return rb_funcallv(x, idCmp, 1, &y);
21 }
22 
23 void
25 {
26  VALUE classname;
27 
28  if (SPECIAL_CONST_P(y) || BUILTIN_TYPE(y) == T_FLOAT) {
29  classname = rb_inspect(y);
30  }
31  else {
32  classname = rb_obj_class(y);
33  }
34  rb_raise(rb_eArgError, "comparison of %"PRIsVALUE" with %"PRIsVALUE" failed",
35  rb_obj_class(x), classname);
36 }
37 
38 static VALUE
39 invcmp_recursive(VALUE x, VALUE y, int recursive)
40 {
41  if (recursive) return Qnil;
42  return rb_cmp(y, x);
43 }
44 
45 VALUE
47 {
48  VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y);
49  if (invcmp == Qundef || NIL_P(invcmp)) {
50  return Qnil;
51  }
52  else {
53  int result = -rb_cmpint(invcmp, x, y);
54  return INT2FIX(result);
55  }
56 }
57 
58 static VALUE
59 cmp_eq_recursive(VALUE arg1, VALUE arg2, int recursive)
60 {
61  if (recursive) return Qnil;
62  return rb_cmp(arg1, arg2);
63 }
64 
65 /*
66  * call-seq:
67  * obj == other -> true or false
68  *
69  * Compares two objects based on the receiver's <code><=></code>
70  * method, returning true if it returns 0. Also returns true if
71  * _obj_ and _other_ are the same object.
72  */
73 
74 static VALUE
75 cmp_equal(VALUE x, VALUE y)
76 {
77  VALUE c;
78  if (x == y) return Qtrue;
79 
80  c = rb_exec_recursive_paired_outer(cmp_eq_recursive, x, y, y);
81 
82  if (NIL_P(c)) return Qfalse;
83  if (rb_cmpint(c, x, y) == 0) return Qtrue;
84  return Qfalse;
85 }
86 
87 static int
88 cmpint(VALUE x, VALUE y)
89 {
90  return rb_cmpint(rb_cmp(x, y), x, y);
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  if (cmpint(x, y) > 0) return Qtrue;
105  return Qfalse;
106 }
107 
108 /*
109  * call-seq:
110  * obj >= other -> true or false
111  *
112  * Compares two objects based on the receiver's <code><=></code>
113  * method, returning true if it returns 0 or 1.
114  */
115 
116 static VALUE
117 cmp_ge(VALUE x, VALUE y)
118 {
119  if (cmpint(x, y) >= 0) return Qtrue;
120  return Qfalse;
121 }
122 
123 /*
124  * call-seq:
125  * obj < other -> true or false
126  *
127  * Compares two objects based on the receiver's <code><=></code>
128  * method, returning true if it returns -1.
129  */
130 
131 static VALUE
132 cmp_lt(VALUE x, VALUE y)
133 {
134  if (cmpint(x, y) < 0) return Qtrue;
135  return Qfalse;
136 }
137 
138 /*
139  * call-seq:
140  * obj <= other -> true or false
141  *
142  * Compares two objects based on the receiver's <code><=></code>
143  * method, returning true if it returns -1 or 0.
144  */
145 
146 static VALUE
147 cmp_le(VALUE x, VALUE y)
148 {
149  if (cmpint(x, y) <= 0) return Qtrue;
150  return Qfalse;
151 }
152 
153 /*
154  * call-seq:
155  * obj.between?(min, max) -> true or false
156  *
157  * Returns <code>false</code> if <i>obj</i> <code><=></code>
158  * <i>min</i> is less than zero or if <i>anObject</i> <code><=></code>
159  * <i>max</i> is greater than zero, <code>true</code> otherwise.
160  *
161  * 3.between?(1, 5) #=> true
162  * 6.between?(1, 5) #=> false
163  * 'cat'.between?('ant', 'dog') #=> true
164  * 'gnu'.between?('ant', 'dog') #=> false
165  *
166  */
167 
168 static VALUE
169 cmp_between(VALUE x, VALUE min, VALUE max)
170 {
171  if (cmpint(x, min) < 0) return Qfalse;
172  if (cmpint(x, max) > 0) return Qfalse;
173  return Qtrue;
174 }
175 
176 /*
177  * call-seq:
178  * obj.clamp(min, max) -> obj
179  *
180  * Returns <i>min</i> if <i>obj</i> <code><=></code> <i>min</i> is less
181  * than zero, <i>max</i> if <i>obj</i> <code><=></code> <i>max</i> is
182  * greater than zero and <i>obj</i> otherwise.
183  *
184  * 12.clamp(0, 100) #=> 12
185  * 523.clamp(0, 100) #=> 100
186  * -3.123.clamp(0, 100) #=> 0
187  *
188  * 'd'.clamp('a', 'f') #=> 'd'
189  * 'z'.clamp('a', 'f') #=> 'f'
190  */
191 
192 static VALUE
193 cmp_clamp(VALUE x, VALUE min, VALUE max)
194 {
195  int c;
196 
197  if (cmpint(min, max) > 0) {
198  rb_raise(rb_eArgError, "min argument must be smaller than max argument");
199  }
200 
201  c = cmpint(x, min);
202  if (c == 0) return x;
203  if (c < 0) return min;
204  c = cmpint(x, max);
205  if (c > 0) return max;
206  return x;
207 }
208 
209 /*
210  * The <code>Comparable</code> mixin is used by classes whose objects
211  * may be ordered. The class must define the <code><=></code> operator,
212  * which compares the receiver against another object, returning -1, 0,
213  * or +1 depending on whether the receiver is less than, equal to, or
214  * greater than the other object. If the other object is not comparable
215  * then the <code><=></code> operator should return nil.
216  * <code>Comparable</code> uses
217  * <code><=></code> to implement the conventional comparison operators
218  * (<code><</code>, <code><=</code>, <code>==</code>, <code>>=</code>,
219  * and <code>></code>) and the method <code>between?</code>.
220  *
221  * class SizeMatters
222  * include Comparable
223  * attr :str
224  * def <=>(other)
225  * str.size <=> other.str.size
226  * end
227  * def initialize(str)
228  * @str = str
229  * end
230  * def inspect
231  * @str
232  * end
233  * end
234  *
235  * s1 = SizeMatters.new("Z")
236  * s2 = SizeMatters.new("YY")
237  * s3 = SizeMatters.new("XXX")
238  * s4 = SizeMatters.new("WWWW")
239  * s5 = SizeMatters.new("VVVVV")
240  *
241  * s1 < s2 #=> true
242  * s4.between?(s1, s3) #=> false
243  * s4.between?(s3, s5) #=> true
244  * [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
245  *
246  */
247 
248 void
250 {
251 #undef rb_intern
252 #define rb_intern(str) rb_intern_const(str)
253 
254  rb_mComparable = rb_define_module("Comparable");
255  rb_define_method(rb_mComparable, "==", cmp_equal, 1);
256  rb_define_method(rb_mComparable, ">", cmp_gt, 1);
257  rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
258  rb_define_method(rb_mComparable, "<", cmp_lt, 1);
259  rb_define_method(rb_mComparable, "<=", cmp_le, 1);
260  rb_define_method(rb_mComparable, "between?", cmp_between, 2);
261  rb_define_method(rb_mComparable, "clamp", cmp_clamp, 2);
262 }
void Init_Comparable(void)
Definition: compar.c:249
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2284
#define Qtrue
Definition: ruby.h:437
VALUE rb_exec_recursive(VALUE(*)(VALUE, VALUE, int), VALUE, VALUE)
Definition: thread.c:4709
VALUE rb_inspect(VALUE)
Convenient wrapper of Object::inspect.
Definition: object.c:656
VALUE rb_eArgError
Definition: error.c:802
VALUE rb_obj_class(VALUE)
call-seq: obj.class -> class
Definition: object.c:277
VALUE rb_mComparable
Definition: compar.c:15
#define NIL_P(v)
Definition: ruby.h:451
#define T_FLOAT
Definition: ruby.h:495
#define Qfalse
Definition: ruby.h:436
Definition: id.h:82
#define PRIsVALUE
Definition: ruby.h:135
#define Qnil
Definition: ruby.h:438
#define BUILTIN_TYPE(x)
Definition: ruby.h:518
unsigned long VALUE
Definition: ruby.h:85
#define rb_cmpint(cmp, a, b)
#define rb_funcallv
Definition: console.c:21
#define INT2FIX(i)
Definition: ruby.h:232
VALUE rb_exec_recursive_paired_outer(VALUE(*)(VALUE, VALUE, int), VALUE, VALUE, VALUE)
Definition: thread.c:4744
#define SPECIAL_CONST_P(x)
Definition: ruby.h:1242
VALUE rb_define_module(const char *name)
Definition: class.c:768
#define Qundef
Definition: ruby.h:439
VALUE rb_invcmp(VALUE x, VALUE y)
Definition: compar.c:46
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
void rb_cmperr(VALUE x, VALUE y)
Definition: compar.c:24