Class: RangeExtd::Infinity
- Includes:
- Comparable
- Defined in:
- lib/range_extd/infinity/infinity.rb,
lib/range_extd/infinity/infinity.rb
Overview
Class RangeExtd::Infinity
- Authors
-
Masa Sakano
- License
-
MIT
Summary
Class to hold just two constants:
-
RangeExtd::Infinity::NEGATIVE
-
RangeExtd::Infinity::POSITIVE
and two more:
-
FLOAT_INFINITY (OBSOLETE; workaround for Ruby 1.8 to represent Float::INFINITY)
-
CLASSES_ACCEPTABLE (see below)
There is no other object in this class (you can not create a new one).
This class includes Comparable module.
Description
Both the two constant are an abstract value which is always smaller/larger, respectively, than any other Comparable objects (1 or -1 by i#<=>(obj)) except for infinities with the same polarity, that is, positive or negative, in which case 0 is returned. See the document of the method #== for the definition of “infinity”. Also, #succ is defined, which just returns self.
There is a note of caution. The method #<=> is defined in this class as mentioned above. However any operator is, by Ruby’s definition, not commutative, unless both the classes define so.
There are only three built-in classes that are Comparable: String, Time and Numeric (except for Complex). Note Date and DateTime objects are so, too, however practically they need “require”, hence are (and must be) treated, the same as any other classes. For String and Time class objects, the [#<=>] operator work as expected in the commutative way.
?z <=> RangeExtd::Infinity::POSITIVE # => nil
RangeExtd::Infinity::POSITIVE <=> ?z # => 1.
For Numeric, it does not.
50 <=> RangeExtd::Infinity::POSITIVE # => nil
RangeExtd::Infinity::POSITIVE <=> 50 # => 1.
For that reason, for example,
( 50 .. RangeExtd::Infinity::POSITIVE)
raises an exception, because the Numeric instance 50 does not know how to compare itself with a RangeExtd::Infinity instance, and Range class does not allow such a case.
For Numeric, this is deliberately so. Please use Float::INFINITY instead in principle; it will be a lot faster in run-time, though it is perfectly possible for you to implement the feature in Numeric sub-classes, if need be.
Any other Comparable classes are defined by users by definition, whether you or authors of libraries. The comparison with Infinity instances are implemented in Object#<=> in this library. Hence, as long as the method [#<=>] in the classes is written sensibly, that is, if it respects the method of the super-class when it does not know how to deal with an unknown object, there is no need for modification. Any object in your class (say, YourComparable) is immediately comparable with the Infinity instances,
YourComparable.new <=> RangeExtd::Infinity::POSITIVE # => -1
RangeExtd::Infinity::POSITIVE <=> YourComparable.new # => 1
except for the infinity inscances in YourComparable (see #==).
See the document in Object#<=> in this code/package for detail.
However, some existing Comparable classes, perhaps written by some one else may not be so polite, and has disabled comparison with any object but those intended. Unlucky you! For example, the classes like Date and DateTime are one of them.
For that sort of circumstances, the class method Infinity.overwrite_compare provides a convenient way to overcome this problem to make the operator [#<=>] commutative for a given Comparable class.
Note Infinity.overwrite_compare does nothing for the classes registered in the Class constant Array CLASSES_ACCEPTABLE. So, if you want to avoid such modification of the method [#<=>], perhaps by some other end users, you can register the class in that array.
Only the methods defined in this class are #===, #==, #<=>, #succ, #to_s, #inspect, #infinity?, #positive? and #negative?, and in addition, since Version 1.1, two unary operators #@+ and #@- to unchange/swap the parity are defined (#< and #> are modified, too, to deal with Integer and Float; I do not know whether the default behaviour of these classes have changed in the recent versions of Ruby, hence resulting in the neccesity of this change).
Comparison operators
POSITIVE and InfN NEGATIVE are always comparable with any comparable objects except for Float::INFINITY, in which case
(RangeExtd::Infinity::POSITIVE <=> Float::INFINITY) # => nil
(RangeExtd::Infinity::POSITIVE < Float::INFINITY) # => ArgumentError
(RangeExtd::Infinity::POSITIVE > Float::INFINITY) # => ArgumentError
(RangeExtd::Infinity::POSITIVE == Float::INFINITY) # => false
which is what happens for the comparison operators for Float::INFINITY.
Basically, the concept of POSITIVE is a generalised concept of Float::INFINITY. Therefore they are really not equal. On the other hand, POSITIVE is greater than any normal comparable objects (except those that are infinite). Therefore, all the following are true (Object#<=> and some methods in some classes are modified)
(5 < RangeExtd::Infinity::POSITIVE)
(5 > RangeExtd::Infinity::NEGATIVE)
("a" < RangeExtd::Infinity::POSITIVE)
("a" > RangeExtd::Infinity::NEGATIVE)
whereas
(RangeExtd::Infinity::POSITIVE < Object.new) # => ArgumentError
raises ArgumentError.
Constant Summary collapse
- CLASSES_ACCEPTABLE =
Class that accept to be compared with Infinity instances.
[self, Float, Integer, Rational, Numeric, String]
Class Method Summary collapse
-
.infinite?(obj) ⇒ Boolean
True if obj is either Float::INFINITY or Infinity type.
-
.infinity?(obj) ⇒ Boolean
True if obj is a kind of Infinity like this class.
-
.overwrite_compare(obj) ⇒ Boolean?
Overwrite [#<=>] method of the given class, if necessary, to make its instances be comparable with RangeExtd::Infinity objects (constants).
Instance Method Summary collapse
-
#+@ ⇒ Object
Unary Operator: Plus.
-
#-@ ⇒ Object
Unary Operator: Minus.
-
#<(c) ⇒ Object
Special case for Float::INFINITY.
-
#<=>(c) ⇒ Integer?
Always -1 or 1 except for itself and the corresponding infinities (== 0).
-
#==(c) ⇒ Object
Always false except for itself and the corresponding Float::INFINITY and those that have methods of #infinity? and #positive? with the corresponding true/false values, in which case this returns true.
-
#===(c) ⇒ Object
Equivalent to #==.
-
#>(c) ⇒ Object
Special case for Float::INFINITY.
-
#cmp_before_rangeextd_infinity? ⇒ Object
No overwriting.
-
#greater_than_before_rangeextd_infinity? ⇒ Object
No overwriting.
- #infinity? ⇒ Boolean
- #inspect ⇒ String (also: #to_s)
-
#less_than_before_rangeextd_infinity? ⇒ Object
No overwriting.
- #negative? ⇒ Boolean
-
#positive? ⇒ Boolean
alias_method :infinite?, :infinity? if !self.method_defined? :infinite? # Common with Float::INFINITY If the alias for :infinite? is defined as above, the following would raise NoMethodError: undefined method ‘>’ for true:TrueClass in the operation Float::INFINITY <=> RangeExtd::Infinity::POSITIVE.
-
#succ ⇒ Infinity
Self.
Class Method Details
.infinite?(obj) ⇒ Boolean
True if obj is either Float::INFINITY or Infinity type.
Note Float#infinite? is defined - how to memorise this method name.
367 368 369 370 |
# File 'lib/range_extd/infinity/infinity.rb', line 367 def self.infinite?(obj) kl = obj.class (kl.method_defined?(:infinite?) && obj.infinite?) || (kl.method_defined?(:infinity?) && obj.infinity?) end |
.infinity?(obj) ⇒ Boolean
True if obj is a kind of Infinity like this class
This is similar to the following, but is in a duck-typing way:
RangeExtd::Infinity === obj
Note that this returns false for Float::INFINITY. If you want true for Float::INFINITY, use infinite? instead.
357 358 359 360 |
# File 'lib/range_extd/infinity/infinity.rb', line 357 def self.infinity?(obj) kl = obj.class kl.method_defined?(:infinity?) && kl.method_defined?(:positive?) && kl.method_defined?(:negative?) end |
.overwrite_compare(obj) ⇒ Boolean?
Overwrite [#<=>] method of the given class, if necessary, to make its instances be comparable with RangeExtd::Infinity objects (constants). For example,
RangeExtd::Infinity::NEGATIVE.<=>(any_comparable)
always gives back -1 (except for same infinities). However the other way around,
SomeClass.new.<=>(RangeExtd::Infinity::NEGATIVE)
usually returns nil, which is not handy. Therefore, this function (Class method) provides a convenient way to overcome it, that is, if the given class (or the class of the given object) is Comparable, its [#<=>] method is modified (and true is returned), unless it has been already done so, or some classes as listed below, such as Numeric and String, in which case nil is returned. If it is not Comparable, false is returned. The judgement whether it is Comparable or not is based whether the class has an instance method ThatClass#<=
In processing, this method first looks up at an Array CLASSES_ACCEPTABLE, and if the given class is registered in it, it does nothing. If not, and if all the othe conditions are met, it overwrites its <=> method and register the class in the array.
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/range_extd/infinity/infinity.rb', line 284 def self.overwrite_compare(obj) if defined? obj.instance_methods klass = obj else klass = obj.class begin _ = 1.0 + obj # Use "rescue ArgumentError" if using "1.0<obj" return nil # No change for Numeric rescue TypeError end end # if defined? obj.instance_methods # [Numeric, Fixnum, Bignum, Float, Rational, String, Complex].each do |i| # , BigFloat (self::CLASSES_ACCEPTABLE+[self]).each do |i| # , BigFloat # The class itself (RangeExtd::Infinity) must be rejected! # Otherwise the rewrites itself, and may cause an infinite loop. # In fact it is pre-defined in RangeExtd::Infinity, so the above addition is a duplication - just to make sure. return nil if i == klass # No change for Numeric etc # Built-in String, Numeric etc try to flip over "<=>" if it doesn't know the object! end self::CLASSES_ACCEPTABLE.push(klass) # The class is registered, so it would not come here again for the class. a = klass.instance_methods if !a.include?( :<= ) # NOT Comparable return false elsif a.include?(:compare_before_infinity) return nil else # Overwrite the definition of "<=>" so that it is fliped over for Infinity. code = "alias_method :compare_before_infinity, :<=> if ! self.method_defined?(:compare_before_infinity)\ndef <=>(c)\n if defined?(self.<=) && RangeExtd::Infinity === c\n if defined?(self.infinity?) && defined?(self.positive?)\n if (self.positive? ^! c.positive?)\n 0\n elsif self.positive?\n 1\n else\n -1\n end\n else\n if c.positive?\n -1\n else\n 1\n end\n end\n else\n compare_before_infinity(c)\n end\nend\n" #<<__EOF__ # for Emacs hilit. klass.class_eval(code) true end end |
Instance Method Details
#+@ ⇒ Object
Unary Operator: Plus
158 159 160 |
# File 'lib/range_extd/infinity/infinity.rb', line 158 def +@ self end |
#-@ ⇒ Object
Unary Operator: Minus
163 164 165 |
# File 'lib/range_extd/infinity/infinity.rb', line 163 def -@ positive? ? NEGATIVE : POSITIVE end |
#<(c) ⇒ Object
Special case for Float::INFINITY
(Float::INFINITY > RangeExtd::Infinity::POSITIVE)
raises ArgumentError and so does this method.
215 216 217 |
# File 'lib/range_extd/infinity/infinity.rb', line 215 def <(c) ((c.abs rescue c) == Float::INFINITY) ? raise(ArgumentError, "RangeExtd::Infinity object not comparable with '#{__method__}' with Float::INFINITY") : less_than_before_rangeextd_infinity?(c) end |
#<=>(c) ⇒ Integer?
Always -1 or 1 except for itself and the corresponding infinities (== 0). See #==. Or, nil (as defined by Object), if the argument is not Comparable, such as, nil and IO.
191 192 193 194 195 196 197 198 199 |
# File 'lib/range_extd/infinity/infinity.rb', line 191 def <=>(c) if c.nil? || !c.class.method_defined?(:<=) # Not Comparable? nil elsif c == Float::INFINITY nil # Special case. else (self == c) ? 0 : (@positive ? 1 : -1) end end |
#==(c) ⇒ Object
Always false except for itself and the corresponding Float::INFINITY and those that have methods of #infinity? and #positive? with the corresponding true/false values, in which case this returns true.
222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/range_extd/infinity/infinity.rb', line 222 def ==(c) if (Infinity === c) (@positive ^! c.positive?) # It should be OK to compare object_id? #elsif c == FLOAT_INFINITY && @positive # true #elsif c == -FLOAT_INFINITY && !@positive # true elsif defined?(c.infinity?) && defined?(c.positive?) (c.infinity? && (@positive ^! c.positive?)) else false end end |
#===(c) ⇒ Object
Equivalent to #==
237 238 239 |
# File 'lib/range_extd/infinity/infinity.rb', line 237 def ===(c) self == c end |
#>(c) ⇒ Object
Special case for Float::INFINITY
(Float::INFINITY > RangeExtd::Infinity::POSITIVE)
raises ArgumentError and so does this method.
206 207 208 |
# File 'lib/range_extd/infinity/infinity.rb', line 206 def >(c) ((c.abs rescue c) == Float::INFINITY) ? raise(ArgumentError, "RangeExtd::Infinity object not comparable with '#{__method__}' with Float::INFINITY") : greater_than_before_rangeextd_infinity?(c) end |
#cmp_before_rangeextd_infinity? ⇒ Object
No overwriting.
185 |
# File 'lib/range_extd/infinity/infinity.rb', line 185 alias_method :cmp_before_rangeextd_infinity?, :== |
#greater_than_before_rangeextd_infinity? ⇒ Object
No overwriting.
201 |
# File 'lib/range_extd/infinity/infinity.rb', line 201 alias_method :greater_than_before_rangeextd_infinity?, :> |
#infinity? ⇒ Boolean
167 168 169 |
# File 'lib/range_extd/infinity/infinity.rb', line 167 def infinity? true end |
#inspect ⇒ String Also known as: to_s
247 248 249 250 251 252 253 |
# File 'lib/range_extd/infinity/infinity.rb', line 247 def inspect if @positive "INFINITY" else "-INFINITY" end end |
#less_than_before_rangeextd_infinity? ⇒ Object
No overwriting.
210 |
# File 'lib/range_extd/infinity/infinity.rb', line 210 alias_method :less_than_before_rangeextd_infinity?, :< |
#negative? ⇒ Boolean
181 182 183 |
# File 'lib/range_extd/infinity/infinity.rb', line 181 def negative? !@positive end |
#positive? ⇒ Boolean
alias_method :infinite?, :infinity? if !self.method_defined? :infinite? # Common with Float::INFINITY If the alias for :infinite? is defined as above, the following would raise
NoMethodError: undefined method `>' for true:TrueClass
in the operation
Float::INFINITY <=> RangeExtd::Infinity::POSITIVE
177 178 179 |
# File 'lib/range_extd/infinity/infinity.rb', line 177 def positive? @positive end |
#succ ⇒ Infinity
Returns self.
242 243 244 |
# File 'lib/range_extd/infinity/infinity.rb', line 242 def succ self end |