Class: RangeExtd
- Defined in:
- lib/range_extd/range_extd.rb,
lib/range_extd/range_extd.rb,
lib/range_extd/infinity/infinity.rb
Overview
This file is required from range_open/range_open.rb
Defined Under Namespace
Classes: Infinity
Constant Summary collapse
- NONE =
Two constants
RangeExtd.new(nil...nil, true, true)
- ALL =
RangeExtd.new(Infinity::NEGATIVE..Infinity::POSITIVE, false, false)
- @@middle_strings =
[]
Class Method Summary collapse
-
.middle_strings ⇒ Array<String>
See RangExtd.middle_strings=() for detail.
- .middle_strings=(ary) ⇒ Array, Symbol
-
.valid?(*inar) ⇒ Boolean
Returns true if the range to be constructed (or given) is valid, as a range, accepted in RangeExtd.
Instance Method Summary collapse
-
#==(r) ⇒ Boolean
Like Range, returns true only if both of them are Range (or its subclasses), and in addition if both #exclude_begin? and #exclude_end? match (==) between the two objects.
- #===(obj) ⇒ Boolean (also: #include?, #member?)
- #begin ⇒ Object
-
#bsearch(*rest, &bloc) ⇒ Object
bsearch is internally implemented by converting a float into 64-bit integer.
- #cover?(i) ⇒ Boolean
- #each(*rest, &bloc) ⇒ RangeExtd, Enumerator
- #end ⇒ Object
-
#eql?(r) ⇒ Boolean
The same as #== but it uses eql?() as each comparison.
-
#equiv?(other) ⇒ Boolean
Return true if self and the other are equivalent; if [#to_a] is defined, it is similar to (self.to_a == other.to_a) (though the ends are checked more rigorously), and if not, equivalent to (self == other).
-
#exclude_begin? ⇒ Boolean
Returns true if the “begin” boundary is excluded, or false otherwise.
-
#exclude_end? ⇒ Boolean
Returns true if the “end” boundary is excluded, or false otherwise.
-
#first(*rest) ⇒ Object, Array
Like Range#last, if no argument is given, it behaves like #begin(), that is, it returns the initial value, regardless of #exclude_begin?.
-
#hash(*rest) ⇒ Object
When #exclude_begin? is true, the returned value is not strictly guaranteed to be unique, though in pracrtice it is most likely to be so.
-
#initialize(*inar, **hsopt) ⇒ RangeExtd
constructor
Note if you use the third form with “string_form” with the user-defined string (via RangeExtd.middle_strings=()), make 100 per cent sure of what you are doing.
-
#inspect ⇒ String
Return eg., ‘(“a”<…“c”)’, ‘(“a”<..“c”)’, if #exclude_begin? is true, or else, identical to those for Range.
- #is_all? ⇒ Boolean
-
#is_none? ⇒ Boolean
true if self is identical to NONE.
-
#last(*rest) ⇒ Object, Array
See Range#last.
-
#max(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
-
#max_by(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
-
#min(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
-
#min_by(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
-
#minmax(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
-
#minmax_by(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
-
#size(*rest) ⇒ Integer, ...
Implementation of Range#size to this class.
-
#step(*rest, &bloc) ⇒ RangeExtd, Enumerator
See #each.
-
#to_s ⇒ String
Return eg., “(a<…c)”, “(a<..c)”, if #exclude_begin? is true, or else, identical to those for Range.
Methods inherited from Range
#empty?, #eql_prerangeextd?, #equal_prerangeextd?, #null?, #valid?
Constructor Details
#new(range, [exclude_begin = false, [exclude_end=false]], opts) ⇒ RangeExtd #new(obj_begin, obj_end, [exclude_begin = false, [exclude_end=false]], opts) ⇒ RangeExtd #new(obj_begin, string_form, obj_end, [exclude_begin = false, [exclude_end=false]], opts) ⇒ RangeExtd
The flag of exclude_begin|end can be given in the arguments in a couple of ways. If there is any duplication, those specified in the optional hash have the highest priority. Then the two descrete Boolean parameters have the second. If not, the values embeded in the Range or RangeExtd object or the String form in the parameter are used. In default, both of them are false.
Note if you use the third form with “string_form” with the user-defined string (via middle_strings=()), make 100 per cent sure of what you are doing. If the string is ambiguous, the result may differ from what you thought you would get! See middle_strings=() for detail.
35 36 37 38 39 |
# File 'lib/range_extd/range_extd.rb', line 35 def initialize(rangepart, ex_begin, ex_end) @rangepart = rangepart @exclude_begin = ex_begin @exclude_end = ex_end end |
Class Method Details
.middle_strings ⇒ Array<String>
See RangExtd.middle_strings=() for detail.
1115 1116 1117 |
# File 'lib/range_extd/range_extd.rb', line 1115 def self.middle_strings() @@middle_strings end |
.middle_strings=(ary) ⇒ Array, Symbol
Set the class variable to be used in #to_s and #inspect to configure the format of their returned values.
The parameters should be given as an Array with 7 elements of string in principle, which gives the characters for each index:
-
prefix
-
begin-inclusive
-
begin-exclusive
-
middle-string to bridge both ends
-
end-exclusive
-
end-inclusive
-
postfix
If the elements [1] and [2], or [4] and [5] are equal, a warning is issued as some of RangeExtd in display will be indistinguishable. Note even if no warning is issued, that does not mean all the forms will be not ambiguous. For example, if you specify
['(', '', '.', '..', '.', '', ')']
a string (3…7) can mean either exclusive #begin or #end. It is user’s responsibility to make it right.
The two most popular forms can be given as a Symbol instead of Array, that is,
:default ( ['', '', '<', '..', '.', '', ''] )
:math ( ['', '<=', '<', 'x', '<', '<=', ''] )
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 |
# File 'lib/range_extd/range_extd.rb', line 1089 def self.middle_strings=(ary) case ary when :default @@middle_strings = ['', '', '<', '..', '.', '', ''] when :math @@middle_strings = ['', '<=', '<', 'x', '<', '<=', ''] else begin if ary.size == 7 _dummy = 'a' + ary[6] @@middle_strings = ary if (ary[1] == ary[2]) || (ary[4] == ary[5]) warn "warning: some middle_strings are indistinguishable." end else raise end rescue raise ArgumentError, "invalid argument" end end end |
.new(range, [exclude_begin = false, [exclude_end=false]]) ⇒ Boolean .new(obj_begin, obj_end, [exclude_begin = false, [exclude_end=false]]) ⇒ Boolean
The flag of exclude_begin|end can be given in the arguments in a couple of ways. If there is any duplication, those specified in the optional hash have the highest priority. Then the two descrete Boolean parameters have the second. If not, the values embeded in the Range or RangeExtd object in the parameter are used. In default, both of them are false.
Returns true if the range to be constructed (or given) is valid, as a range, accepted in RangeExtd.
This routine is also impremented as a method in Range, and accordingly its sub-classes.
This routine is called from new, hence for any instance of RangeExtd class, its Range#valid? returns true.
What is valid is defined as follows:
-
Both #begin and #end elements must be Comparable to each other, and the comparison results must be consistent betwen the two. The sole exception is NONE, which is valid. For example, (nil..nil) is NOT valid (nb., it raised Exception in Ruby 1.8).
-
#begin must be smaller than or equal to #end, that is, (#begin <=> #end) must be either -1 or 0.
-
If #begin is equal to #end, namely, (#begin <=> #end) == 0, the exclude status of the both ends must agree. That is, if the #begin is excluded, #end must be also excluded, and vice versa. For example, (1…1) is NOT valid for that reason, because any built-in Range object has the exclude status of false (namely, inclusive) for #begin.
Note the last example may change in the future release.
Note ([2]..) is NOT valid, because Array does not include Comparable for some reason, as of Ruby 2.1.1, even though it has the redefined and working [#<=>]. You can make those valid, by including Comparable in Array class, should you wish.
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 |
# File 'lib/range_extd/range_extd.rb', line 1006 def self.valid?(*inar) (vbeg, vend, exc_beg, exc_end) = _get_init_args(*inar) if defined?(inar[0].is_none?) && inar[0].is_none? && exc_beg && exc_end return true end begin t = (vbeg <=> vend) begin return false if t != -1*(vend <=> vbeg) # false if not commutative, or possibly exception (such as -1*nil). rescue NoMethodError, TypeError if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) || (Float === vbeg && defined?(vend.infinity?) && vend.infinity?) warn "Float::INFINITY is not comparable with other Infinity." end return false # return end rescue # NoMethodError false # return else case t when -1 true when 0 if defined?(vbeg.<=) && defined?(vend.<=) # Comparable? ((true && exc_beg) ^! exc_end) # True if single value or empty, false if eg, (1...1) else false # Not Comparable end when 1 false else if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) || (Float === vbeg && defined?(vend.infinity?) && vend.infinity?) warn "Float::INFINITY is not comparable with other Infinity." end false # Not Comparable. end # case t # All statements of return above. end end |
Instance Method Details
#==(r) ⇒ Boolean
Like Range, returns true only if both of them are Range (or its subclasses), and in addition if both #exclude_begin? and #exclude_end? match (==) between the two objects. For the empty ranges they are somewhat different. In short, when both of them are empty and they belong to the same Class or have common ancestors (apart from Object and BasicObject, excluding all the included modules), this returns true, regardless of their boundary values. And any empty range is equal to RangeExtd::Infinity::NONE.
Note the last example will return false for #eql? – see #eql?
See #eql?
224 225 226 |
# File 'lib/range_extd/range_extd.rb', line 224 def ==(r) re_equal_core(r, :==) end |
#===(obj) ⇒ Boolean Also known as: include?, member?
If the object is open-ended to the negative (Infinity), this returns nil in default, unless the given object is Numeric (and comparable of Real), in which case this calls #cover?, or if self is ALL and the object is Comparable.
In the standard Range, this checks whether the given object is a member, hence,
(?D..?z) === ?c # => true
(?a..?z) === "cc" # => false
In the case of the former, after finite trials of [#succ] from ?c, it reaches the end (?z). In the latter, after finit trials of [#succ] from the begin ?a, it reaches the end (?z). Therefore it is theoretically possible to prove it (n.b., the actual algorithm of built-in Range#include? is different and cheating! See below.).
However, in the case of
(?D..Infinity) === ?c
it can never prove ?c is a member after infinite trials of [#succ], whether it starts the trials from the begin (?D) or the object (?c).
For anything but Numeric, use #cover? instead.
Note
(?B..?z) === 'dd' # => false
as Ruby’s Range knows the algorithm of String#succ and String#<=> and specifically checks with it, before using Enumerable#include?. https://github.com/ruby/ruby/blob/trunk/range.c
Therefore, even if you change the definition of String#succ so that ‘B’.succ => ‘dd’, ‘dd’.succ => ‘z’, as follows,
class String
alias :succ_orig :succ
def succ
if self == 'B'
'dd'
elsif self == 'dd'
'z'
else
:succ_orig
end
end
end
the resutl of Range#=== will unchange;
(?B..?z) === 'dd' # => false
(?B..?z).to_a # => ["B", "dd", "z"]
Similarly Range treats String differently;
(?X..?z).each do |i| print i;end # => "XYZ[\]^_`abcdefghijklmnopqrstuvwxyz"
?Z.succ # => 'AA'
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 |
# File 'lib/range_extd/range_extd.rb', line 297 def ===(obj) # ("a".."z")===("cc") # => false return false if is_none? # No need of null?(), supposedly! begin 1.0+(obj) # OK if Numeric. rescue TypeError # obj is not Numeric, hence runs brute-force check. beg = self.begin() if defined?(beg.infinity?) && beg.infinity? || beg == -Infinity::FLOAT_INFINITY return nil # raise TypeError "can't iterate from -Infinity" end each do |ei| if ei == obj return true end end false else cover?(obj) end end |
#begin ⇒ Object
401 402 403 |
# File 'lib/range_extd/range_extd.rb', line 401 def begin() @rangepart.begin() end |
#bsearch(*rest, &bloc) ⇒ Object
bsearch is internally implemented by converting a float into 64-bit integer. The following examples demonstrate what is going on.
ary = [0, 4, 7, 10, 12]
(3...4).bsearch{ |i| ary[i] >= 11} # => nil
(3...5).bsearch{ |i| ary[i] >= 11} # => 4 (Integer)
(3..5.1).bsearch{ |i| ary[i] >= 11} # => 4.0 (Float)
(3.6..4).bsearch{ |i| ary[i] >= 11} # => 4.0 (Float)
(3.6...4).bsearch{ |i| ary[i] >= 11} # => nil
(3.6...4.1).bsearch{|i| ary[i] >= 11} # => 4.0 (Float)
class Special
def [](f)
(f>3.5 && f<4) ? true : false
end
end
sp = Special.new
(3..4).bsearch{ |i| sp[i]} # => nil
(3...4).bsearch{ |i| sp[i]} # => nil
(3.0...4).bsearch{|i| sp[i]} # => 3.5000000000000004
(3...4.0).bsearch{|i| sp[i]} # => 3.5000000000000004
(3.3..4).bsearch{ |i| sp[i]} # => 3.5000000000000004
(Rational(36,10)..5).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Rational (Ruby 2.1)
(3..Rational(61,10)).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Fixnum (Ruby 2.1)
In short, bsearch works only with Integer and/or Float (as in Ruby 2.1). If either of begin and end is an Float, the search is conducted in Float and the returned value will be Float, unless nil. If Float, it searches on the binary plane. If Integer, the search is conducted on the descrete Integer points only, and no search will be made in between the adjascent integers.
Given that, #bsearch follows basically the same, even when exclude_begin? is true. If either end is Float, it searches between begin*(1+Float::EPSILON) and end. If both are Integer, it searches from begin+1. When #exclude_begin? is false, #bsearch is identical to Range#bsearch.
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 |
# File 'lib/range_extd/range_extd.rb', line 447 def bsearch(*rest, &bloc) if is_none? # No need of null?(), supposedly! raise TypeError, "can't do binary search for NONE range" end if @exclude_begin if ((Float === self.begin()) || (Integer === self.begin()) && (Float === self.end())) #NOTE: Range#bsearch accepts Infinity, whether it makes sense or not. # if Infinity::FLOAT_INFINITY == self.begin() # raise TypeError, "can't do binary search from -Infinity" # else Range.new(self.begin()*(Float::EPSILON+1.0), self.end, exclude_end?).send(__method__, *rest, &bloc) # @note Technically, if begin is Rational, there is no strong reason it should not work. # However Range#bsearch does not accept Rational (at Ruby 2.1), hence this code. # Users should give a RangeExtd with begin being Rational.to_f in that case. # end elsif (defined? self.begin().succ) # Both non-Float Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest, &bloc) # In practice it will not raise an Exception, only when both are Integer. else @rangepart.send(__method__, *rest, &bloc) # It will raise an exception anyway! Such as, (Rational..Rational) end else @rangepart.send(__method__, *rest, &bloc) end end |
#cover?(i) ⇒ Boolean
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/range_extd/range_extd.rb', line 476 def cover?(i) # ("a".."z").cover?("cc") # => true # (?B..?z).cover?('dd') # => true (though 'dd'.succ would never reach ?z) return false if is_none? # No need of null?(), supposedly! if @exclude_begin if self.begin == i false else @rangepart.send(__method__, i) end else @rangepart.send(__method__, i) end end |
#each(*rest, &bloc) ⇒ RangeExtd, Enumerator
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 |
# File 'lib/range_extd/range_extd.rb', line 498 def each(*rest, &bloc) # (1...3.5).each{|i|print i} # => '123' to STDOUT # (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each> # (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float # Note: If the block is not given and if @exclude_begin is true, the self in the returned Enumerator is not the same as self here. if @exclude_begin # including RangeExtd::NONE if defined? self.begin.succ ret = Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc) if block_given? self else ret end elsif is_none? raise TypeError, "can't iterate for NONE range" else raise TypeError, "can't iterate from "+self.begin.class.name end else @rangepart.send(__method__, *rest, &bloc) end end |
#end ⇒ Object
406 407 408 |
# File 'lib/range_extd/range_extd.rb', line 406 def end() @rangepart.end() end |
#eql?(r) ⇒ Boolean
241 242 243 |
# File 'lib/range_extd/range_extd.rb', line 241 def eql?(r) re_equal_core(r, :eql?) end |
#equiv?(other) ⇒ Boolean
Return true if self and the other are equivalent; if [#to_a] is defined, it is similar to
(self.to_a == other.to_a)
(though the ends are checked more rigorously), and if not, equivalent to
(self == other)
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/range_extd/range_extd.rb', line 343 def equiv?(other) # This routine is very similar to Range#equiv? except # exclude_begin? in this object is always defined, hence # a more thorough check is needed. t_or_f = (defined?(self.begin.succ) && defined?(other.begin.succ) && defined?(other.end) && defined?(other.exclude_end?)) if ! t_or_f return(self == other) # succ() for begin is not defined. else # Checking the begins. if defined?(other.exclude_begin?) other_excl_beg = other.exclude_begin? else other_excl_beg = false end if (self.begin == other.begin) if (exclude_begin? ^! other_excl_beg) # Pass else return false end else if (exclude_begin? ^! other_excl_beg) return false elsif (exclude_begin? && (self.begin.succ == other.begin)) || (other_excl_beg && (self.begin == other.begin.succ)) # Pass else return false end end # if (self.begin == other.begin) # else # Now, the begins agreed. Checking the ends. if (self.end == other.end) if (exclude_end? ^! other.exclude_end?) return true else return false end else # if (self.end == other.end) if (exclude_end? ^! other.exclude_end?) return false # elsif defined?(other.last) && (self.last(1) == other.last(1)) # Invalid for Ruby 1.8 or earlier # This is not good - eg., in this case, (1..5.5).equiv?(1..5.4) would return true. # return true elsif ( exclude_end? && defined?(other.end.succ) && (self.end == other.end.succ)) || (other.exclude_end? && defined?( self.end.succ) && (self.end.succ == other.end)) return true else return false end end # if (self.end == other.end) end # if ! t_or_f end |
#exclude_begin? ⇒ Boolean
Returns true if the “begin” boundary is excluded, or false otherwise.
192 193 194 |
# File 'lib/range_extd/range_extd.rb', line 192 def exclude_begin? @exclude_begin end |
#exclude_end? ⇒ Boolean
Returns true if the “end” boundary is excluded, or false otherwise.
197 198 199 |
# File 'lib/range_extd/range_extd.rb', line 197 def exclude_end? @exclude_end end |
#first(*rest) ⇒ Object, Array
Like Range#last, if no argument is given, it behaves like #begin(), that is, it returns the initial value, regardless of #exclude_begin?. However, if an argument is given (nb., acceptable since Ruby 1.9) when #exclude_begin? is true, it returns the array that starts from #begin().succ().
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
# File 'lib/range_extd/range_extd.rb', line 528 def first(*rest) # (1...3.1).last # => 3.1 # (1...3.1).last(1) # => [3] if ! @exclude_begin # hence, not NONE. @rangepart.first(*rest) else case rest.size when 0 self.begin when 1 if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8. raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)." end ## Check the argument. Array.new[ rest[0] ] # Check Type of rest[0] (if invalid, it should raise TypeError) begin if rest[0] < 0 raise ArgumentError, "negative array size (or size too big)" end rescue NoMethodError # Should not happen, but just to play safe. end ## Main if ! defined? self.begin.succ raise TypeError, "can't iterate from "+self.begin.class.name end Range.new(self.begin.succ, self.end, exclude_end?).send(__method__, *rest) else raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)" end end # if ! @exclude_begin end |
#hash(*rest) ⇒ Object
When #exclude_begin? is true, the returned value is not strictly guaranteed to be unique, though in pracrtice it is most likely to be so.
568 569 570 571 572 573 574 |
# File 'lib/range_extd/range_extd.rb', line 568 def hash(*rest) if @exclude_begin @rangepart.send(__method__, *rest) - 1 else @rangepart.send(__method__, *rest) end end |
#inspect ⇒ String
Return eg., ‘(“a”<…“c”)’, ‘(“a”<..“c”)’, if #exclude_begin? is true, or else, identical to those for Range.
580 581 582 |
# File 'lib/range_extd/range_extd.rb', line 580 def inspect re_inspect_core(__method__) end |
#is_all? ⇒ Boolean
186 187 188 |
# File 'lib/range_extd/range_extd.rb', line 186 def is_all? self.begin.object_id == Infinity::NEGATIVE.object_id && self.end.object_id == Infinity::POSITIVE.object_id && !@exclude_begin && !@exclude_end # Direct comparison with object_id should not work for this one!! (because users can create an identical one.) end |
#is_none? ⇒ Boolean
179 180 181 |
# File 'lib/range_extd/range_extd.rb', line 179 def is_none? self.begin.nil? && self.end.nil? && @exclude_begin && @exclude_end # Direct comparison with object_id should be OK? end |
#last(*rest) ⇒ Object, Array
See Range#last. If either (let alone both) side of the edge is Infinity, you can not give an argument in practice, the number of the members of the returned array.
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 |
# File 'lib/range_extd/range_extd.rb', line 599 def last(*rest) return nil if null? nSize = rest.size case nSize when 0 self.end when 1 if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8. raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)." end if defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY raise TypeError, "can't iterate from "+self.begin.to_s elsif defined?(self.end.infinity?) && self.end.infinity? || self.end == Infinity::FLOAT_INFINITY raise TypeError, "can't get elements to "+self.end.to_s elsif ! defined? self.begin.succ raise TypeError, "can't iterate from "+self.begin.class.name else @rangepart.send(__method__, *rest) end else raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)" end end |
#max(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
660 661 662 |
# File 'lib/range_extd/range_extd.rb', line 660 def max(*rest, &bloc) re_min_max_core(__method__, *rest, &bloc) end |
#max_by(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
666 667 668 |
# File 'lib/range_extd/range_extd.rb', line 666 def max_by(*rest, &bloc) re_min_max_core(__method__, *rest, &bloc) end |
#min(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
627 628 629 |
# File 'lib/range_extd/range_extd.rb', line 627 def min(*rest, &bloc) re_min_max_core(__method__, *rest, &bloc) end |
#min_by(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
633 634 635 |
# File 'lib/range_extd/range_extd.rb', line 633 def min_by(*rest, &bloc) re_min_max_core(__method__, *rest, &bloc) end |
#minmax(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
640 641 642 643 644 645 646 |
# File 'lib/range_extd/range_extd.rb', line 640 def minmax(*rest, &bloc) # (0...3.5).minmax # => [0, 3] # (1.3...5).minmax # => TypeError: can't iterate from Float # Note that max() for the same Range raises an exception. # In that sense, it is inconsistent! re_min_max_core(__method__, *rest, &bloc) end |
#minmax_by(*rest, &bloc) ⇒ Object
See #first for the definition when #exclude_begin? is true.
650 651 652 653 654 655 |
# File 'lib/range_extd/range_extd.rb', line 650 def minmax_by(*rest, &bloc) # (0...3.5).minmax # => [0, 3] # Note that max() for the same Range raises an exception. # In that sense, it is inconsistent! re_min_max_core(__method__, *rest, &bloc) end |
#size(*rest) ⇒ Integer, ...
Implementation of Range#size to this class.
It is essentially the same, but the behaviour when #exclude_begin? is true may not always be natural. See #first for the definition when #exclude_begin? is true.
Range#size only works for Numeric ranges. And in Range#size, the value is calculated when the initial value is non-Integer, by stepping by 1.0 from the #begin value, and the returned value is an integer. For example,
(1.4..2.6).size == 2
because both 1.4 and 2.4 (== 1.4+1.0) are included in the Range.
That means you had better be careful with the uncertainty (error) of floating-point. For example, at least in an environment,
4.8 - 4.5 # => 0.2999999999999998
(2.5...4.5000000000000021).size => 2
(2.8...4.8000000000000021).size => 3
(2.8..4.8).size => 3
In #size, the principle is the same. If the #begin value has the method [#succ] defined, the object is regarded to consist of discrete values. If not, it is a range with continuous elements. This dinstinguishment affects the behavious seriously in some cases when #exclude_begin? is true. For example, the following two cases may seem unnatural.
RangeExtd(1..5, true, true) == RangeExtd(Rational(1,1), 5, true, true)
RangeExtd(1..5, true, true).size != RangeExtd(Rational(1,1), 5, true, true).size
Although those two objects are equal by [#==], they are different in nature, as far as Range and RangeExtd are concerned, and that is why they work differently;
RangeExtd(1..5, true, true).eql?(RangeExtd(Rational(1,1), 5, true, true)) # => false
RangeExtd(1..5, true, true).to_a # => [2, 3, 4]
RangeExtd(1..5, true, true).to_a.size # => 3
RangeExtd(Rational(1,1)..5).to_a # => TypeError
Also, the floating-point uncertainties in Float can more often be problematic; for example, in an environment,
4.4 - 2.4 # => 2.0000000000000004
4.8 - 2.8 # => 2.0
RangeExtd(2.4..4.4, true, true).size # => 3
RangeExtd(2.8..4.8, true, true).size # => 2
The last example is what you would naively expect, because both
2.8+a(lim a->0) and 3.8+a(lim a->0) are
in the range whereas 4.8 is not in the range by definition, but not the example right above.
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 |
# File 'lib/range_extd/range_extd.rb', line 725 def size(*rest) # (1..5).size # => 5 # (1...5).size # => 4 # (0.8...5).size # => 5 # Why??? # (1.2...5).size # => 4 # Why??? # (1.2..5).size # => 4 # Why??? # (Rational(3,2)...5).size # => 3 # (1.5...5).size # => 4 # Why not 3?? # (1.5...4.9).size # => 4 # Why not 3?? # (1.5...4.5).size # => 3 # (0...Float::INFINITY).size # => Infinity if is_none? # No need of null?(), supposedly! return 0 ### (Infinity..Infinity) => 0 (as in Ruby 2.1) # elsif self.begin().infinity? || self.end().infinity? # return Infinity::FLOAT_INFINITY # Checking Infinity. # Note (Infinity..Infinity) => 0 (Range as in Ruby 2.1) # however, elsif (defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY) || (defined?(self.end.infinity?) && self.end.infinity? || self.end == Infinity::FLOAT_INFINITY) if self.begin == self.end return 0 else return Infinity::FLOAT_INFINITY end elsif @exclude_begin begin _dummy = 1.0 + self.begin() # _dummy to suppress warning: possibly useless use of + in void context # Numeric if defined? (self.begin().succ) Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest) else size_no_exclude = Range.new(self.begin, self.end).send(__method__, *rest) # exclude_end? == true, ie., Range with both ends inclusinve. diff = self.end - self.begin if diff.to_i == diff # Integer difference return size_no_exclude - 1 # At least exclude_begin?==true (so exclude_end? does not matter) else return size_no_exclude end end rescue TypeError # Non-Numeric if defined? self.begin().succ Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest) # => nil in Ruby 2.1 else nil # See the line above. # raise TypeError, "can't iterate from "+self.begin.class.name end end else @rangepart.send(__method__, *rest) end end |
#step(*rest, &bloc) ⇒ RangeExtd, Enumerator
See #each.
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 |
# File 'lib/range_extd/range_extd.rb', line 794 def step(*rest, &bloc) # (1...3.5).each{|i|print i} # => '123' to STDOUT # (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each> # (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float # Note: If the block is not given and if exclude_begin?() is true, the self in the returned Enumerator is not the same as self here. if @exclude_begin # including RangeExtd::NONE if defined? self.begin.succ ret = Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc) if block_given? self else ret end elsif is_none? # No need of null?(), supposedly! raise TypeError, "can't iterate for NONE range" else raise TypeError, "can't iterate from "+self.begin.class.name end else @rangepart.send(__method__, *rest, &bloc) end end |
#to_s ⇒ String
Return eg., “(a<…c)”, “(a<..c)”, if #exclude_begin? is true, or else, identical to those for Range.
587 588 589 |
# File 'lib/range_extd/range_extd.rb', line 587 def to_s re_inspect_core(__method__) end |