range_extd 1.1.1 → 2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ChangeLog +16 -0
- data/Makefile +7 -3
- data/News +4 -0
- data/README.en.rdoc +1146 -270
- data/README.ja.rdoc +1146 -270
- data/Rakefile +11 -5
- data/lib/range_extd/infinity.rb +426 -0
- data/lib/range_extd/load_all.rb +19 -0
- data/lib/range_extd/nil_class.rb +41 -0
- data/lib/range_extd/nowhere.rb +135 -0
- data/lib/range_extd/numeric.rb +160 -0
- data/lib/range_extd/object.rb +53 -0
- data/lib/range_extd/range.rb +401 -0
- data/lib/{range_extd/range_extd.rb → range_extd.rb} +387 -633
- data/range_extd.gemspec +50 -0
- data/test/all_required_test.rb +173 -0
- data/test/test_range_extd.rb +482 -148
- data/test/test_range_extd_nowhere.rb +84 -0
- metadata +29 -16
- data/lib/range_extd/infinity/infinity.rb +0 -600
@@ -1,31 +1,22 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
|
-
## Load required files.
|
4
|
-
|
5
|
-
req_files = %w(lib/range_extd/infinity/infinity)
|
3
|
+
## Load required files in this library. (At the end of this file, "range_extd/range" is also required.)
|
4
|
+
req_files = %w(range_extd/infinity range_extd/nowhere range_extd/nil_class)
|
6
5
|
req_files.each do |req_file|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
err1st = errLoad if err1st.nil?
|
12
|
-
if %r@/@ =~ req_file
|
13
|
-
if req_file.sub!(%r@[^/]*/@, '').nil? # Will search for the next directory down.
|
14
|
-
raise
|
15
|
-
end
|
16
|
-
else
|
17
|
-
req_file = ''
|
18
|
-
break
|
19
|
-
end
|
20
|
-
else
|
21
|
-
break
|
22
|
-
end
|
6
|
+
begin
|
7
|
+
require_relative req_file
|
8
|
+
rescue LoadError
|
9
|
+
require req_file
|
23
10
|
end
|
24
|
-
|
25
|
-
raise err1st
|
26
|
-
end
|
27
|
-
end # req_files.each do |req_file|
|
11
|
+
end
|
28
12
|
|
13
|
+
if $DEBUG
|
14
|
+
puts "NOTE: Library full paths:"
|
15
|
+
req_files.each do |elibbase|
|
16
|
+
ar = $LOADED_FEATURES.grep(/(^|\/)#{Regexp.quote(File.basename(elibbase))}(\.rb)?$/).uniq
|
17
|
+
print elibbase+": " if ar.empty?; p ar
|
18
|
+
end
|
19
|
+
end
|
29
20
|
|
30
21
|
# =Class RangeExtd
|
31
22
|
#
|
@@ -36,20 +27,22 @@ end # req_files.each do |req_file|
|
|
36
27
|
#
|
37
28
|
# Extended Range class that features:
|
38
29
|
# 1. includes exclude_begin? (to exclude the "begin" boundary),
|
39
|
-
# 2. allows open-ended range
|
30
|
+
# 2. allows open-ended range to the infinity (very similar to beginless/endless Range),
|
40
31
|
# 3. defines NONE and ALL constants,
|
41
32
|
# 4. the first self-consistent logical structure,
|
42
|
-
# 5. complete
|
33
|
+
# 5. complete compatibility with the built-in Range.
|
43
34
|
#
|
44
35
|
# The instance of this class is immutable, that is, you can not
|
45
36
|
# alter the element once an instance is generated.
|
46
37
|
#
|
38
|
+
# This class has some constants
|
39
|
+
#
|
47
40
|
# What is valid is checked with the class method {RangeExtd.valid?}.
|
48
41
|
# See the document of that method for the definition.
|
49
42
|
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
43
|
+
# This class has two constants:
|
44
|
+
# {RangeExtd::NONE} representing an empty range and
|
45
|
+
# {RangeExtd::ALL} representing the entire range, both in the abstract sense.
|
53
46
|
#
|
54
47
|
# @example An instance of a range of 5 to 8 with both ends being exclusive is created as
|
55
48
|
# r = RangeExtd(5...8, true)
|
@@ -93,7 +86,7 @@ class RangeExtd < Range
|
|
93
86
|
# @option opts [Boolean] :exclude_end If specified, this has the highest priority, or false in default.
|
94
87
|
#
|
95
88
|
# @overload new(obj_begin, obj_end, [exclude_begin=false, [exclude_end=false]], opts)
|
96
|
-
# @param obj_begin [Object] Any object that is
|
89
|
+
# @param obj_begin [Object] Any object that is +Comparable+ with end
|
97
90
|
# @param obj_end [Object] Any object that is Comparable with begin
|
98
91
|
# @param exclude_begin [Boolean] If specified, this has the lower priority, or false in default.
|
99
92
|
# @param exclude_end [Boolean] If specified, this has the lower priority, or false in default.
|
@@ -101,9 +94,9 @@ class RangeExtd < Range
|
|
101
94
|
# @option opts [Boolean] :exclude_end If specified, this has the higher priority, or false in default.
|
102
95
|
#
|
103
96
|
# @overload new(obj_begin, string_form, obj_end, [exclude_begin=false, [exclude_end=false]], opts)
|
104
|
-
# @param obj_begin [Object] Any object that is
|
97
|
+
# @param obj_begin [Object] Any object that is +Comparable+ with end
|
105
98
|
# @param string_form [Object] String form (without pre/postfix) of range expression set by {RangeExtd.middle_strings=}()
|
106
|
-
# @param obj_end [Object] Any object that is Comparable with begin
|
99
|
+
# @param obj_end [Object] Any object that is +Comparable+ with begin
|
107
100
|
# @param exclude_begin [Boolean] If specified, this has the lower priority, or false in default.
|
108
101
|
# @param exclude_end [Boolean] If specified, this has the lower priority, or false in default.
|
109
102
|
# @option opts [Boolean] :exclude_begin If specified, this has the higher priority, or false in default.
|
@@ -126,17 +119,31 @@ class RangeExtd < Range
|
|
126
119
|
# @raise [ArgumentError] particularly if the range to be created is not {#valid?}.
|
127
120
|
def initialize(*inar, **hsopt) # **k expression from Ruby 1.9?
|
128
121
|
|
122
|
+
# This is true only for RangeExtd::NONE,
|
123
|
+
# which is identical to +RangeExtd(nil, nil, true, true)+ without this.
|
124
|
+
@is_none = false
|
125
|
+
|
129
126
|
if inar[4] == :Constant
|
130
|
-
# Special case to create two Constants
|
131
|
-
super(*inar[0..2])
|
127
|
+
# Special case to create two Constants (NONE and ALL)
|
132
128
|
@rangepart = (inar[2] ? (inar[0]...inar[1]) : (inar[0]..inar[1]))
|
133
129
|
@exclude_end, @exclude_begin = inar[2..3]
|
130
|
+
|
131
|
+
# In Ruby-2.7+ and hence RangeExtd Ver.2+, RangeExtd::NONE looks very similar to (nil...nil)
|
132
|
+
# except RangeExtd::NONE.@exclude_begin == true
|
133
|
+
#@is_none = (@rangepart.begin.nil? && @rangepart.end.nil? && @exclude_begin && @exclude_end)
|
134
|
+
@is_none = (@rangepart.begin.respond_to?(:nowhere?) &&
|
135
|
+
@rangepart.begin.nowhere? &&
|
136
|
+
@rangepart.end.respond_to?(:nowhere?) &&
|
137
|
+
@rangepart.end.nowhere? &&
|
138
|
+
@exclude_begin &&
|
139
|
+
@exclude_end)
|
140
|
+
raise(ArgumentError, "NONE has been already defined.") if @is_none && self.class.const_defined?(:NONE)
|
141
|
+
super(*inar[0..2])
|
134
142
|
return
|
135
143
|
end
|
136
144
|
|
137
|
-
# Note: the order of exclude_begin? and end? is reversed from the input!
|
138
145
|
arout = RangeExtd.send(:_get_init_args, *inar, **hsopt)
|
139
|
-
# == [RangeBeginValue, RangeEndValue,
|
146
|
+
# == [RangeBeginValue, RangeEndValue, exclude_begin?, exclude_end?]
|
140
147
|
|
141
148
|
### The following routine is obsolete.
|
142
149
|
### Users, if they wish, should call RangeExtd::Infinity.overwrite_compare() beforehand.
|
@@ -171,30 +178,27 @@ class RangeExtd < Range
|
|
171
178
|
raise RangeError, "the combination of the arguments does not constitute a valid RangeExtd instance."
|
172
179
|
end
|
173
180
|
|
181
|
+
@exclude_end = arout.pop
|
174
182
|
@exclude_begin = arout.pop
|
175
|
-
|
176
|
-
@rangepart = Range.new(*
|
177
|
-
super(*
|
178
|
-
|
179
|
-
end # def initialize(*inar)
|
183
|
+
artmp = [arout[0], arout[1], @exclude_end]
|
184
|
+
@rangepart = Range.new(*artmp)
|
185
|
+
super(*artmp)
|
186
|
+
end # def initialize(*inar, **hsopt)
|
180
187
|
|
181
188
|
|
182
189
|
# true if self is identical to {RangeExtd::NONE}.
|
190
|
+
#
|
191
|
+
# Overwriting {Range#is_none?}
|
183
192
|
# This is different from {#==} method!
|
193
|
+
#
|
184
194
|
# @example
|
185
|
-
# RangeExtd(0,0,
|
186
|
-
# RangeExtd(0,0,
|
187
|
-
# RangeExtd(0,0,
|
195
|
+
# RangeExtd(0,0,true,true).valid? # => true
|
196
|
+
# RangeExtd(0,0,true,true) == RangeExtd::NONE # => true
|
197
|
+
# RangeExtd(0,0,true,true).empty? # => true
|
198
|
+
# RangeExtd(0,0,true,true).is_none? # => false
|
188
199
|
# RangeExtd::NONE.is_none? # => true
|
189
200
|
def is_none?
|
190
|
-
|
191
|
-
end
|
192
|
-
|
193
|
-
# true if self is identical to {RangeExtd::ALL} ({#==} does not mean it at all!)
|
194
|
-
# @example
|
195
|
-
# (RangeExtd::Infinity::NEGATIVE..RangeExtd::Infinity::POSITIVE).is_all? # => false
|
196
|
-
def is_all?
|
197
|
-
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.)
|
201
|
+
@is_none
|
198
202
|
end
|
199
203
|
|
200
204
|
|
@@ -217,11 +221,10 @@ class RangeExtd < Range
|
|
217
221
|
# this returns true, regardless of their boundary values.
|
218
222
|
# And any empty range is equal to RangeExtd::Infinity::NONE.
|
219
223
|
#
|
220
|
-
# Note the last example will return false for
|
221
|
-
#
|
222
|
-
# See {#eql?}
|
224
|
+
# Note the last example will return false for +#eql?+
|
223
225
|
#
|
224
226
|
# @example
|
227
|
+
# (1...1) == RangeExtd::NONE # => false (b/c the Range is invalid)
|
225
228
|
# (1<...1) == RangeExtd::NONE # => true
|
226
229
|
# (?a<...?b) == RangeExtd::NONE # => true
|
227
230
|
# (1<...1) == (2<...2) # => true
|
@@ -232,25 +235,27 @@ class RangeExtd < Range
|
|
232
235
|
#
|
233
236
|
# @return [Boolean]
|
234
237
|
def ==(r)
|
235
|
-
|
238
|
+
_re_equal_core(r, :==)
|
236
239
|
end # def ==(r)
|
237
240
|
|
238
|
-
#
|
239
|
-
#
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
#
|
251
|
-
|
252
|
-
|
253
|
-
|
241
|
+
# Modification to {#eql?} is dropped in RangeExtd Ver.2
|
242
|
+
#
|
243
|
+
## The same as {#==} but it uses eql?() as each comparison.
|
244
|
+
## For the empty ranges, it is similar to {#==}, except
|
245
|
+
## the immediate class has to agree to return true.
|
246
|
+
## Only the exception is the comparison with RangeExtd::Infinity::NONE.
|
247
|
+
## Therefore,
|
248
|
+
## @example
|
249
|
+
## (1...5) == (1.0...5.0) # => true
|
250
|
+
## (1...5).eql?(1.0...5.0) # => false
|
251
|
+
## (1<...1).eql?( RangeExtd::NONE) # => true
|
252
|
+
## (?a<...?b).eql?(RangeExtd::NONE) # => true
|
253
|
+
## (1<...1).eql?( 3<...4) # => true
|
254
|
+
## (1.0<...1.0).eql?(3<...4) # => false
|
255
|
+
##
|
256
|
+
#def eql?(r)
|
257
|
+
# _re_equal_core(r, :eql?)
|
258
|
+
#end # def eql?(r)
|
254
259
|
|
255
260
|
|
256
261
|
# If the object is open-ended to the negative (Infinity),
|
@@ -264,7 +269,7 @@ class RangeExtd < Range
|
|
264
269
|
# In the case of the former, after finite trials of [#succ] from ?c, it reaches the end (?z).
|
265
270
|
# In the latter, after finit trials of [#succ] from the begin ?a, it reaches the end (?z).
|
266
271
|
# Therefore it is theoretically possible to prove it (n.b., the actual
|
267
|
-
# algorithm of built-in
|
272
|
+
# algorithm of built-in +Range#include?+ is different and cheating!
|
268
273
|
# See below.).
|
269
274
|
#
|
270
275
|
# However, in the case of
|
@@ -276,11 +281,11 @@ class RangeExtd < Range
|
|
276
281
|
#
|
277
282
|
# Note
|
278
283
|
# (?B..?z) === 'dd' # => false
|
279
|
-
# as Ruby's {Range} knows the algorithm of
|
280
|
-
# and specifically checks with it, before using
|
284
|
+
# as Ruby's {Range} knows the algorithm of +String#succ+ and +String#<=>+
|
285
|
+
# and specifically checks with it, before using +Enumerable#include?+.
|
281
286
|
# {https://github.com/ruby/ruby/blob/trunk/range.c}
|
282
287
|
#
|
283
|
-
# Therefore, even if you change the definition of
|
288
|
+
# Therefore, even if you change the definition of +String#succ+
|
284
289
|
# so that 'B'.succ => 'dd', 'dd'.succ => 'z', as follows,
|
285
290
|
# class String
|
286
291
|
# alias :succ_orig :succ
|
@@ -294,7 +299,7 @@ class RangeExtd < Range
|
|
294
299
|
# end
|
295
300
|
# end
|
296
301
|
# end
|
297
|
-
# the resutl of
|
302
|
+
# the resutl of +Range#===+ will unchange;
|
298
303
|
# (?B..?z) === 'dd' # => false
|
299
304
|
# (?B..?z).to_a # => ["B", "dd", "z"]
|
300
305
|
#
|
@@ -307,29 +312,31 @@ class RangeExtd < Range
|
|
307
312
|
def ===(obj)
|
308
313
|
# ("a".."z")===("cc") # => false
|
309
314
|
|
310
|
-
return false if
|
315
|
+
return false if empty? # n.b, NONE includes nothing, even NOWHERE (because of exclude_begin/end)
|
311
316
|
|
312
|
-
|
313
|
-
|
317
|
+
rapart = _converted_rangepart
|
318
|
+
beg = rapart.begin
|
319
|
+
if beg.nil? && !beg.nowhere?
|
320
|
+
return rapart.send(__method__, obj)
|
321
|
+
end
|
314
322
|
|
323
|
+
begin
|
324
|
+
_ = 1.0+obj # OK if Numeric.
|
325
|
+
return cover?(obj) # This excludes begin() if need be.
|
315
326
|
rescue TypeError
|
316
|
-
|
317
|
-
beg = self.begin()
|
318
|
-
if defined?(beg.infinity?) && beg.infinity? || beg == -Infinity::FLOAT_INFINITY
|
319
|
-
return nil
|
320
|
-
# raise TypeError "can't iterate from -Infinity"
|
321
|
-
end
|
327
|
+
end
|
322
328
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
+
# obj is not Numeric, hence runs brute-force check.
|
330
|
+
beg = self.begin()
|
331
|
+
if beg.respond_to?(:infinity?) && beg.infinity?
|
332
|
+
return nil
|
333
|
+
# raise TypeError "can't iterate from -Infinity"
|
334
|
+
end
|
329
335
|
|
330
|
-
|
331
|
-
|
336
|
+
each do |ei| # This excludes begin() if need be.
|
337
|
+
return true if ei == obj
|
332
338
|
end
|
339
|
+
false
|
333
340
|
end # def ===(obj)
|
334
341
|
|
335
342
|
alias :include? :===
|
@@ -443,8 +450,8 @@ class RangeExtd < Range
|
|
443
450
|
# (Rational(36,10)..5).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Rational (Ruby 2.1)
|
444
451
|
# (3..Rational(61,10)).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Fixnum (Ruby 2.1)
|
445
452
|
#
|
446
|
-
#
|
447
|
-
# If either of begin and end is
|
453
|
+
# +Range#bsearch+ works only with Integer and/or Float (as in Ruby 2.1), not even Rational (as in Ruby 3.1).
|
454
|
+
# If either of begin and end is a Float, the search is conducted in Float and the returned value will be a Float, unless nil.
|
448
455
|
# If Float, it searches on the binary plane.
|
449
456
|
# If Integer, the search is conducted on the descrete Integer points only,
|
450
457
|
# and no search will be made in between the adjascent integers.
|
@@ -452,7 +459,7 @@ class RangeExtd < Range
|
|
452
459
|
# Given that, {RangeExtd#bsearch} follows basically the same, even when exclude_begin? is true.
|
453
460
|
# If either end is Float, it searches between begin*(1+Float::EPSILON) and end.
|
454
461
|
# If both are Integer, it searches from begin+1.
|
455
|
-
# When {#exclude_begin?} is false, {RangeExtd#bsearch} is identical to
|
462
|
+
# When {#exclude_begin?} is false, {RangeExtd#bsearch} is identical to +Range#bsearch+.
|
456
463
|
#
|
457
464
|
def bsearch(*rest, &bloc)
|
458
465
|
if is_none? # No need of null?(), supposedly!
|
@@ -482,97 +489,110 @@ class RangeExtd < Range
|
|
482
489
|
end # def bsearch(*rest, &bloc)
|
483
490
|
|
484
491
|
|
485
|
-
#
|
492
|
+
# This works without modification mostly.
|
493
|
+
#
|
494
|
+
# Presumably because Enumerable#count is called and #each is internally used.
|
495
|
+
# Exceptions are infinities and borderless (nil).
|
496
|
+
#
|
497
|
+
# (5..).count # => Float::INFINITY # exceptional case
|
498
|
+
# (..5).count # => Float::INFINITY # exceptional case
|
499
|
+
# (..nil).count # => Float::INFINITY # exceptional case
|
500
|
+
# (-Float::INFINITY..nil) # => Float::INFINITY # exceptional case
|
501
|
+
# (-Float::INFINITY..Float::INFINITY).count # raises (TypeError) "can't iterate from Float"
|
502
|
+
# (..5).count(4) # raises (TypeError)
|
503
|
+
# (..5).count{|i| i<3} # raises (TypeError)
|
504
|
+
# (1..).count{|i| i<3} # infinite loop!
|
505
|
+
#
|
506
|
+
# Here I define as another exceptional case:
|
507
|
+
# RangeExtd::ALL.count # => Float::INFINITY
|
508
|
+
#
|
509
|
+
# @return [Integer]
|
510
|
+
def count(*rest, &block)
|
511
|
+
return Float::INFINITY if self == RangeExtd::ALL
|
512
|
+
super
|
513
|
+
end
|
514
|
+
|
515
|
+
|
516
|
+
# See {#include?} or {#===}, and +Range#cover?+
|
486
517
|
def cover?(i)
|
487
518
|
# ("a".."z").cover?("cc") # => true
|
488
519
|
# (?B..?z).cover?('dd') # => true (though 'dd'.succ would never reach ?z)
|
489
520
|
|
490
|
-
return false if
|
521
|
+
return false if empty? # equivalent to null? in this case because self is alwasy true==valid?
|
491
522
|
|
492
|
-
if @exclude_begin
|
493
|
-
|
494
|
-
false
|
495
|
-
else
|
496
|
-
@rangepart.send(__method__, i)
|
497
|
-
end
|
523
|
+
if @exclude_begin && self.begin == i
|
524
|
+
false
|
498
525
|
else
|
499
526
|
@rangepart.send(__method__, i)
|
500
527
|
end
|
501
528
|
end # def cover?(i)
|
502
529
|
|
503
530
|
|
504
|
-
#
|
531
|
+
# slightly modified for {#exclude_begin?} being true
|
532
|
+
#
|
533
|
+
# @raise [TypeError] If {#exclude_begin?} is true, and {#begin}() (+@rangepart+) does not have a method of [#succ], then even if no block is given, this method raises TypeError straightaway.
|
505
534
|
# @return [RangeExtd] self
|
506
535
|
# @return [Enumerator] if block is not given.
|
507
|
-
#
|
508
536
|
def each(*rest, &bloc)
|
509
537
|
# (1...3.5).each{|i|print i} # => '123' to STDOUT
|
510
538
|
# (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
|
511
539
|
# (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
|
512
540
|
# 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.
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
541
|
+
|
542
|
+
_step_each_core(__method__, *rest, &bloc)
|
543
|
+
end
|
544
|
+
|
545
|
+
# Core routine for {#each} and {#step}
|
546
|
+
#
|
547
|
+
# @raise [TypeError] If {#exclude_begin?} is true, and {#begin}() or {#rangepart} does not have a method of [#succ], then even if no block is given, this method raises TypeError straightaway.
|
548
|
+
# @return [RangeExtd] self
|
549
|
+
# @return [Enumerator] if block is not given.
|
550
|
+
def _step_each_core(method, *rest, &bloc)
|
551
|
+
raise TypeError, "can't iterate for NONE range" if is_none?
|
552
|
+
|
553
|
+
if block_given?
|
554
|
+
# when a block is given to {#each}, self should be returned.
|
555
|
+
_converted_rangepart(consider_exclude_begin: true, raises: true ).send(method, *rest, &bloc)
|
556
|
+
self
|
526
557
|
else
|
527
|
-
|
558
|
+
_converted_rangepart(consider_exclude_begin: true, raises: false).send(method, *rest)
|
528
559
|
end
|
529
560
|
end
|
561
|
+
private :_step_each_core
|
530
562
|
|
531
|
-
|
532
|
-
#
|
533
|
-
#
|
563
|
+
# Like +Range#last+, if no argument is given, it behaves like {#begin}(), that is, it returns the initial value, regardless of {#exclude_begin?}.
|
564
|
+
#
|
565
|
+
# If an argument is given (nb., acceptable since Ruby 1.9.2) when {#exclude_begin?} is true, it returns the array that starts from {#begin}().succ(), in the same way as +Range#last+ with {#exclude_end?} of +true+.
|
566
|
+
#
|
567
|
+
# The default behaviours are:
|
568
|
+
#
|
569
|
+
# (1...3.1).last # => 3.1
|
570
|
+
# (1...3.1).last(1) # => [3]
|
571
|
+
# (1...3.0).last(1) # => [2]
|
572
|
+
# (3.0..8).first(1) # raise: can't iterate from Float (TypeError)
|
573
|
+
#
|
534
574
|
# @raise [TypeError] if the argument (Numeric) is given and if {#exclude_begin?} is true, yet if {#begin}().succ is not defined, or yet if {#is_none?}
|
535
|
-
# @
|
575
|
+
# @raise [RangeError] "cannot get the first element of beginless range" as per Range.
|
576
|
+
# @raise [ArgumentError] if more than 1 arguments are specified (delegated to {Range})
|
577
|
+
# @param rest [Integer] Optional. Must be non-negative. Consult +Range#first+ for detail.
|
536
578
|
# @return [Object] if no argument is given, equivalent to {#end}.
|
537
579
|
# @return [Array] if an argument is given.
|
538
580
|
def first(*rest)
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
@rangepart.first(*rest)
|
543
|
-
else
|
544
|
-
case rest.size
|
545
|
-
when 0
|
546
|
-
self.begin
|
547
|
-
when 1
|
548
|
-
if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8.
|
549
|
-
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)."
|
550
|
-
end
|
551
|
-
|
552
|
-
## Check the argument.
|
553
|
-
Array.new[ rest[0] ] # Check Type of rest[0] (if invalid, it should raise TypeError)
|
554
|
-
|
555
|
-
begin
|
556
|
-
if rest[0] < 0
|
557
|
-
raise ArgumentError, "negative array size (or size too big)"
|
558
|
-
end
|
559
|
-
rescue NoMethodError
|
560
|
-
# Should not happen, but just to play safe.
|
561
|
-
end
|
562
|
-
|
563
|
-
## Main
|
564
|
-
if ! defined? self.begin.succ
|
565
|
-
raise TypeError, "can't iterate from "+self.begin.class.name
|
566
|
-
end
|
581
|
+
if is_none?
|
582
|
+
raise RangeError, "cannot get the first element of RangeExtd::NONE"
|
583
|
+
end
|
567
584
|
|
568
|
-
|
569
|
-
|
570
|
-
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)"
|
571
|
-
end
|
572
|
-
end # if ! @exclude_begin
|
585
|
+
ran = _converted_rangepart(transform_to_nil: false, consider_exclude_begin: (1 == rest.size && exclude_begin?))
|
586
|
+
ran.send(__method__, *rest)
|
573
587
|
end # def first(*rest)
|
574
588
|
|
575
589
|
|
590
|
+
# Redefines the hash definition
|
591
|
+
#
|
592
|
+
# Without re-definition, the hash value does NOT depend on {#exclude_begin?}
|
593
|
+
# presumably because the parent class method +Range#hash+ does not take it
|
594
|
+
# into account (of course).
|
595
|
+
#
|
576
596
|
# 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.
|
577
597
|
#
|
578
598
|
def hash(*rest)
|
@@ -599,7 +619,8 @@ class RangeExtd < Range
|
|
599
619
|
end
|
600
620
|
|
601
621
|
|
602
|
-
#
|
622
|
+
# Updated version of +Range#last+, considering {#exclude_begin?}.
|
623
|
+
#
|
603
624
|
# If either (let alone both) side of the edge is Infinity, you can not give
|
604
625
|
# an argument in practice, the number of the members of the returned array.
|
605
626
|
#
|
@@ -607,41 +628,55 @@ class RangeExtd < Range
|
|
607
628
|
# @return [Object] if no argument is given, equivalent to {#end}.
|
608
629
|
# @return [Array] if an argument is given.
|
609
630
|
def last(*rest)
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
when 0
|
614
|
-
self.end
|
615
|
-
when 1
|
616
|
-
if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8.
|
617
|
-
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)."
|
618
|
-
end
|
631
|
+
if is_none?
|
632
|
+
raise RangeError, "cannot get the last element of RangeExtd::NONE"
|
633
|
+
end
|
619
634
|
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
635
|
+
_converted_rangepart(transform_to_nil: false).send(__method__, *rest)
|
636
|
+
end # def last(*rest)
|
637
|
+
|
638
|
+
|
639
|
+
# Converts RangeExtd::Infinity to nil in @rangepart
|
640
|
+
#
|
641
|
+
# @param consider_exclude_begin [Boolean] If true (Default), and if {#exclude_begin?} is true, the first element is ignored. Note the resultant Range may be +invalid+.
|
642
|
+
# @param transform_to_nil [Boolean] If true (Default), {RangeExtd::Infinity} objects are transformed into nil when appropriate (i.e., {RangeExtd::Infinity::NEGATIVE} should be {RangeExtd#begin} and not at the end, and vice versa).
|
643
|
+
# @param raises [Boolean] If true (Def: false), and if {#exclude_begin?} is true but [#succ] is not defined for {#begin}, this routine raises an Exception as per (almost) Ruby default.
|
644
|
+
# @return [Range]
|
645
|
+
def _converted_rangepart(consider_exclude_begin: true, transform_to_nil: true, raises: false)
|
646
|
+
rbeg = @rangepart.begin
|
647
|
+
if consider_exclude_begin && exclude_begin?
|
648
|
+
if rbeg.respond_to? :succ
|
649
|
+
rbeg = rbeg.succ
|
650
|
+
elsif raises
|
651
|
+
if rbeg.nil?
|
652
|
+
raise RangeError, "cannot get the first element of beginless range"
|
653
|
+
elsif is_none? # maybe empty?() in some cases?
|
654
|
+
raise RangeError, "cannot get the first element of NONE range"
|
655
|
+
else
|
656
|
+
# This includes {RangeExtd::Infinity} class objects (RangeExtd::Infinity.infinity?(rbeg) == true) and Float::INFINITY.
|
657
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
658
|
+
end
|
628
659
|
end
|
629
|
-
else
|
630
|
-
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)"
|
631
660
|
end
|
632
|
-
end # def last(*rest)
|
633
661
|
|
662
|
+
rbeg = nil if RangeExtd::Infinity::NEGATIVE == rbeg && transform_to_nil
|
663
|
+
rend = @rangepart.end
|
664
|
+
rend = nil if RangeExtd::Infinity::POSITIVE == rend && transform_to_nil
|
665
|
+
|
666
|
+
Range.new(rbeg, rend, exclude_end?)
|
667
|
+
end
|
668
|
+
private :_converted_rangepart
|
634
669
|
|
635
670
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
636
671
|
#
|
637
672
|
def min(*rest, &bloc)
|
638
|
-
|
673
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
639
674
|
end
|
640
675
|
|
641
676
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
642
677
|
#
|
643
678
|
def min_by(*rest, &bloc)
|
644
|
-
|
679
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
645
680
|
end
|
646
681
|
|
647
682
|
|
@@ -652,7 +687,7 @@ class RangeExtd < Range
|
|
652
687
|
# (1.3...5).minmax # => TypeError: can't iterate from Float
|
653
688
|
# Note that max() for the same Range raises an exception.
|
654
689
|
# In that sense, it is inconsistent!
|
655
|
-
|
690
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
656
691
|
end
|
657
692
|
|
658
693
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
@@ -661,31 +696,31 @@ class RangeExtd < Range
|
|
661
696
|
# (0...3.5).minmax # => [0, 3]
|
662
697
|
# Note that max() for the same Range raises an exception.
|
663
698
|
# In that sense, it is inconsistent!
|
664
|
-
|
699
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
665
700
|
end
|
666
701
|
|
667
702
|
|
668
703
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
669
704
|
#
|
670
705
|
def max(*rest, &bloc)
|
671
|
-
|
706
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
672
707
|
end
|
673
708
|
|
674
709
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
675
710
|
#
|
676
711
|
def max_by(*rest, &bloc)
|
677
|
-
|
712
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
678
713
|
end
|
679
714
|
|
680
715
|
|
681
|
-
# Implementation of
|
716
|
+
# Implementation of +Range#size+ to this class.
|
682
717
|
#
|
683
718
|
# It is essentially the same, but the behaviour when {#exclude_begin?} is true
|
684
719
|
# may not always be natural.
|
685
720
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
686
721
|
#
|
687
|
-
#
|
688
|
-
# And in
|
722
|
+
# +Range#size+ only works for Numeric ranges.
|
723
|
+
# And in +Range#size+, the value is calculated when the initial value is
|
689
724
|
# non-Integer, by stepping by 1.0 from the {#begin} value, and the returned
|
690
725
|
# value is an integer.
|
691
726
|
# For example,
|
@@ -746,18 +781,19 @@ class RangeExtd < Range
|
|
746
781
|
# (Float::INFINITY..Float::INFINITY).size
|
747
782
|
#
|
748
783
|
# has changed (I do not know in which Ruby version)!
|
749
|
-
# It used to be 0. However, As of Ruby 2.6, it
|
784
|
+
# It used to be 0 (in Ruby-2.1). However, As of Ruby 2.6, it raises +FloatDomainError: NaN+
|
750
785
|
# Again this class now follows Ruby's default ({RangeExtd} Ver.1.0 or later).
|
751
786
|
#
|
752
787
|
# @note When both ends n are the same INFINITY (of the same parity),
|
753
788
|
# +(n..n).size+ used to be 0. As of Ruby 2.6, it is FloatDomainError: NaN.
|
754
789
|
# This routine follows what Ruby produces, depending on Ruby's version it is run on.
|
755
790
|
#
|
756
|
-
# @see http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/49797 [ruby-list:49797] from matz for how
|
791
|
+
# @see http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/49797 [ruby-list:49797] from matz for how +Range#size+ behaves (in Japanese).
|
757
792
|
#
|
758
793
|
# @return [Integer] 0 if {RangeExtd::NONE}
|
759
794
|
# @return [Float] Float::INFINITY if either (or both) the end is infinity, regardless of the class of the elements.
|
760
795
|
# @return [nil] if the range is non-Numeric.
|
796
|
+
# @raise [FloatDomainError] (Infinity..Infinity).size (as in Ruby-3.1, though it used to be 0 in Ruby-2.1)
|
761
797
|
def size(*rest)
|
762
798
|
# (1..5).size # => 5
|
763
799
|
# (1...5).size # => 4
|
@@ -770,92 +806,60 @@ class RangeExtd < Range
|
|
770
806
|
# (1.5...4.5).size # => 3
|
771
807
|
# (0...Float::INFINITY).size # => Infinity
|
772
808
|
|
773
|
-
if is_none? # No need of null?(), supposedly!
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
# Note (Infinity..Infinity) => 0 (Range as in Ruby 2.1)
|
782
|
-
# however,
|
783
|
-
elsif (defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY) ||
|
784
|
-
(defined?(self.end.infinity?) && self.end.infinity? || self.end == Infinity::FLOAT_INFINITY) ||
|
785
|
-
(self.end.nil?) # RangeExtd#end can be nil only for Ruby-2.6
|
786
|
-
if self.begin == self.end
|
787
|
-
# This varies, depending on Ruby's version! It used to be 0. As of Ruby 2.6, it is FloatDomainError: NaN.
|
788
|
-
return (Float::INFINITY..Float::INFINITY).size
|
789
|
-
# return 0
|
790
|
-
elsif self.end.nil?
|
791
|
-
# Behaves as Ruby does -
|
792
|
-
# Infinity::FLOAT_INFINITY for Numeric and nil for any other
|
793
|
-
return (self.begin..nil).size
|
794
|
-
else
|
795
|
-
return Infinity::FLOAT_INFINITY
|
796
|
-
end
|
809
|
+
return 0 if is_none? # No need of null?(), supposedly!
|
810
|
+
|
811
|
+
if self.begin.nil? || self.end.nil? # RangeExtd#begin/end can be nil only in Ruby-2.7+/2.6+
|
812
|
+
# Behaves as Ruby does -
|
813
|
+
# Infinity::FLOAT_INFINITY for Numeric and nil, but nil for any other
|
814
|
+
# {#exclude_end?} does not matter.
|
815
|
+
return (self.begin..self.end).size
|
816
|
+
end
|
797
817
|
|
798
|
-
|
818
|
+
rbeg = self.begin
|
819
|
+
rend = self.end
|
799
820
|
|
800
|
-
|
801
|
-
|
821
|
+
# Either or both sides are (general or Float) Infinity
|
822
|
+
if RangeExtd::Infinity.infinite?(rbeg) || RangeExtd::Infinity.infinite?(rend)
|
823
|
+
return @rangepart.send(__method__, *rest) # delegates to {Range#size}
|
824
|
+
end
|
802
825
|
|
803
|
-
|
804
|
-
if defined? (self.begin().succ)
|
805
|
-
Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest)
|
806
|
-
else
|
807
|
-
size_no_exclude = Range.new(self.begin, self.end).send(__method__, *rest) # exclude_end? == true, ie., Range with both ends inclusinve.
|
808
|
-
diff = self.end - self.begin
|
809
|
-
if diff.to_i == diff # Integer difference
|
810
|
-
return size_no_exclude - 1 # At least exclude_begin?==true (so exclude_end? does not matter)
|
811
|
-
else
|
812
|
-
return size_no_exclude
|
813
|
-
end
|
814
|
-
end
|
815
|
-
rescue TypeError
|
816
|
-
# Non-Numeric
|
817
|
-
if defined? self.begin().succ
|
818
|
-
Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest) # => nil in Ruby 2.1
|
819
|
-
else
|
820
|
-
nil # See the line above.
|
821
|
-
# raise TypeError, "can't iterate from "+self.begin.class.name
|
822
|
-
end
|
826
|
+
return @rangepart.send(__method__, *rest) if !exclude_begin?
|
823
827
|
|
828
|
+
# Now, {#exclude_begin?} is true:
|
829
|
+
begin
|
830
|
+
_dummy = 1.0 + rbeg # _dummy to suppress warning: possibly useless use of + in void context
|
831
|
+
rescue TypeError
|
832
|
+
# Non-Numeric
|
833
|
+
if defined? rbeg.succ
|
834
|
+
return Range.new(rbeg.succ, rend, exclude_end?).send(__method__, *rest) # => nil in Ruby 2.1+
|
835
|
+
else
|
836
|
+
return nil # See the line above.
|
837
|
+
# raise TypeError, "can't iterate from "+self.begin.class.name
|
824
838
|
end
|
839
|
+
end
|
825
840
|
|
841
|
+
# Numeric
|
842
|
+
if rbeg.respond_to? :succ
|
843
|
+
Range.new(rbeg.succ, rend, exclude_end?).send(__method__, *rest)
|
826
844
|
else
|
827
|
-
|
845
|
+
size_no_exclude = Range.new(rbeg, rend).send(__method__, *rest) # exclude_end? == true, ie., Range with both ends inclusinve.
|
846
|
+
diff = self.end - self.begin
|
847
|
+
if diff.to_i == diff # Integer difference
|
848
|
+
return size_no_exclude - 1 # At least exclude_begin?==true (so exclude_end? does not matter)
|
849
|
+
else
|
850
|
+
return size_no_exclude
|
851
|
+
end
|
828
852
|
end
|
829
853
|
end # def size
|
830
854
|
|
831
855
|
|
832
856
|
# See {#each}.
|
857
|
+
#
|
833
858
|
# @raise [TypeError] If {#exclude_begin?} is true, and {#begin}() does not have the method [#succ], then even if no block is given, this method raises TypeError straightaway.
|
834
859
|
# @return [RangeExtd] self
|
835
860
|
# @return [Enumerator] if block is not given.
|
836
|
-
#
|
837
861
|
def step(*rest, &bloc)
|
838
|
-
|
839
|
-
# (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
|
840
|
-
# (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
|
841
|
-
# 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.
|
842
|
-
|
843
|
-
if @exclude_begin # including RangeExtd::NONE
|
844
|
-
if defined? self.begin.succ
|
845
|
-
ret = Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc)
|
846
|
-
if block_given?
|
847
|
-
self
|
848
|
-
else
|
849
|
-
ret
|
850
|
-
end
|
851
|
-
elsif is_none? # No need of null?(), supposedly!
|
852
|
-
raise TypeError, "can't iterate for NONE range"
|
853
|
-
else
|
854
|
-
raise TypeError, "can't iterate from "+self.begin.class.name
|
855
|
-
end
|
856
|
-
else
|
857
|
-
@rangepart.send(__method__, *rest, &bloc)
|
858
|
-
end
|
862
|
+
_step_each_core(__method__, *rest, &bloc)
|
859
863
|
end
|
860
864
|
|
861
865
|
|
@@ -865,7 +869,15 @@ class RangeExtd < Range
|
|
865
869
|
|
866
870
|
# Private class method to evaluate the arguments.
|
867
871
|
#
|
872
|
+
# @note The specification changed from RangeExtd Ver.1 to Ver.2.
|
873
|
+
# In Ver.1 or earlier, this returns [begin, end, exclude_end, exclude_begin]
|
874
|
+
# In Ver.2+, this returns [begin, end, exclude_begin, exclude_end]
|
875
|
+
# Notice the third and fourth elements are swapped. Now it is in line
|
876
|
+
# with {RangeExtd.new}.
|
877
|
+
#
|
878
|
+
# @param (see RangeExtd#initialize) (or valid)
|
868
879
|
# @raise [ArgumentError] if the input format is invalid (otherwise the caller may raise RangeError (it depends))
|
880
|
+
# @return [Array<Object, Object, Boolean, Boolean>] 4-compoents: [begin, end, exclude_begin, exclude_end]
|
869
881
|
def self._get_init_args(*inar, **hsopt)
|
870
882
|
nMin = 1; nMax = 5
|
871
883
|
if inar.size < nMin || nMax < inar.size
|
@@ -903,8 +915,6 @@ class RangeExtd < Range
|
|
903
915
|
end
|
904
916
|
|
905
917
|
beginend = [inar[0].begin, inar[0].end]
|
906
|
-
# arRet = [inar[0].begin, inar[0].end, exclude_end, exclude_begin]
|
907
|
-
# @rangepart = Range.new(inar[0].begin, inar[0].end, exclude_end)
|
908
918
|
|
909
919
|
when :object
|
910
920
|
nMin = 2; nMax = 5
|
@@ -924,8 +934,8 @@ class RangeExtd < Range
|
|
924
934
|
# Originally, defined?(inar[1].=~) seemed enough. But as of Ruby 2.6 (maybe even before),
|
925
935
|
# Numeric has :=~ method as well!
|
926
936
|
if (inar.size > 2 &&
|
927
|
-
inar[1].
|
928
|
-
inar[1].
|
937
|
+
inar[1].respond_to?(:=~) &&
|
938
|
+
inar[1].respond_to?(:to_str))
|
929
939
|
begin
|
930
940
|
cmp = (inar[0] <=> inar[2]).abs
|
931
941
|
rescue
|
@@ -936,16 +946,8 @@ class RangeExtd < Range
|
|
936
946
|
# Hence all the default values are overwritten.
|
937
947
|
beginend = [inar[0], inar[2]]
|
938
948
|
hsFlag[:excl_offset] = 1
|
939
|
-
|
940
|
-
|
941
|
-
else
|
942
|
-
exclude_begin = true
|
943
|
-
end
|
944
|
-
if $2 == @@middle_strings[4]
|
945
|
-
exclude_end = true
|
946
|
-
else
|
947
|
-
exclude_end = false
|
948
|
-
end
|
949
|
+
exclude_begin = ($1 != @@middle_strings[1])
|
950
|
+
exclude_end = ($2 == @@middle_strings[4])
|
949
951
|
else
|
950
952
|
nMin = 2; nMax = 4
|
951
953
|
if inar.size > nMax
|
@@ -967,9 +969,6 @@ class RangeExtd < Range
|
|
967
969
|
exclude_end = (true ^! inar[3+hsFlag[:excl_offset]]) # 4th or 5th argument
|
968
970
|
end
|
969
971
|
|
970
|
-
# arRet = [inar[0], inar[1], exclude_end, exclude_begin]
|
971
|
-
# @rangepart = Range.new(inar[0], inar[1], exclude_end)
|
972
|
-
|
973
972
|
else
|
974
973
|
raise # (for coding safety)
|
975
974
|
end # case hsFlag[:prm1st]
|
@@ -981,23 +980,23 @@ class RangeExtd < Range
|
|
981
980
|
exclude_end = (hsopt[:exclude_end] && true)
|
982
981
|
end
|
983
982
|
|
984
|
-
# [RangeBeginValue, RangeEndValue,
|
985
|
-
_normalize_infinity_float(beginend) + [
|
983
|
+
# [RangeBeginValue, RangeEndValue, exclude_begin?, exclude_end?]
|
984
|
+
_normalize_infinity_float(beginend) + [exclude_begin, exclude_end]
|
986
985
|
end # def self._get_init_args(*inar)
|
987
986
|
private_class_method :_get_init_args # From Ruby 1.8.7 (?)
|
988
987
|
|
989
988
|
# Replaces {RangeExtd::Infinity} with {Float::INFINITY} when appropriate
|
990
989
|
#
|
991
|
-
# @param beginend [Array] 2-compoents
|
992
|
-
# @return [Array] 2-compoents
|
990
|
+
# @param beginend [Array] 2-compoents(begin, end)
|
991
|
+
# @return [Array] 2-compoents(begin, end)
|
993
992
|
def self._normalize_infinity_float(beginend)
|
994
993
|
is_begin_inf = Infinity.infinity?(beginend[0])
|
995
994
|
return beginend if is_begin_inf ^! Infinity.infinity?(beginend[1])
|
996
995
|
|
997
996
|
# Now, only one of them is a {RangeExtd::Infinity} type object.
|
998
|
-
if is_begin_inf && beginend[1].
|
997
|
+
if is_begin_inf && beginend[1].respond_to?(:divmod)
|
999
998
|
[_normalize_infinity_float_core(beginend[0]), beginend[1]] # "begin" is Infinity
|
1000
|
-
elsif beginend[0].
|
999
|
+
elsif beginend[0].respond_to?(:divmod)
|
1001
1000
|
[beginend[0], _normalize_infinity_float_core(beginend[1])] # "end" is Infinity
|
1002
1001
|
else
|
1003
1002
|
beginend
|
@@ -1009,7 +1008,7 @@ class RangeExtd < Range
|
|
1009
1008
|
# @return [RangeExtd::Infinity, Float] +/-Float::INFINITY if Float
|
1010
1009
|
def self._normalize_infinity_float_core(inf)
|
1011
1010
|
msg = 'RangeExtd component of the RangeExtd::Infinity object replaced with Float::INFINITY.'
|
1012
|
-
warn msg if
|
1011
|
+
warn msg if $DEBUG || $VERBOSE
|
1013
1012
|
(inf.positive? ? 1 : -1) * Float::INFINITY
|
1014
1013
|
end
|
1015
1014
|
private_class_method :_normalize_infinity_float_core # From Ruby 1.8.7 (?)
|
@@ -1018,7 +1017,7 @@ class RangeExtd < Range
|
|
1018
1017
|
# Returns true if the range to be constructed (or given) is valid,
|
1019
1018
|
# as a range, accepted in {RangeExtd}.
|
1020
1019
|
#
|
1021
|
-
# This routine is also
|
1020
|
+
# This routine is also implemented as a method in {Range},
|
1022
1021
|
# and accordingly its sub-classes.
|
1023
1022
|
#
|
1024
1023
|
# This routine is called from {RangeExtd.new}, hence
|
@@ -1028,23 +1027,31 @@ class RangeExtd < Range
|
|
1028
1027
|
#
|
1029
1028
|
# 1. The {#begin} and {#end} elements must be Comparable to each other,
|
1030
1029
|
# and the comparison results must be consistent betwen the two.
|
1031
|
-
# The
|
1032
|
-
# introduced in Ruby 2.6 (see below for the exceptions),
|
1033
|
-
#
|
1034
|
-
#
|
1035
|
-
#
|
1036
|
-
#
|
1037
|
-
#
|
1030
|
+
# The three exceptions are {RangeExtd::NONE} and Beginless and Endless Ranges
|
1031
|
+
# introduced in Ruby 2.7 and 2.6, respectively (see below for the exceptions),
|
1032
|
+
# which are all valid. Accordingly, +(nil..nil)+ is
|
1033
|
+
# valid in {RangeExtd} Ver.1.0+ (nb., it used to raise Exception in Ruby 1.8).
|
1034
|
+
# 2. Except for {RangeExtd::NONE} and Beginless Range, {#begin} must have the method +<=+.
|
1035
|
+
# Therefore, some Endless Ranges (Ruby 2.6 and later) like +(true..)+ are *not* valid.
|
1036
|
+
# Note even "+true+" has the method +<=>+ and hence checking +<=+ is essential.
|
1037
|
+
# 3. Similarly, except for {RangeExtd::NONE} and Endless Range, {#end} must have the method +<=+.
|
1038
|
+
# Therefore, some Beginless Ranges (Ruby 2.7 and later) like +(..true)+ are *not* valid.
|
1039
|
+
# 4. {#begin} must be smaller than or equal to {#end},
|
1038
1040
|
# that is, ({#begin} <=> {#end}) must be either -1 or 0.
|
1039
|
-
#
|
1040
|
-
# the exclude status of the both ends must agree
|
1041
|
-
#
|
1041
|
+
# 5. If {#begin} is equal to {#end}, namely, ({#begin} <=> {#end}) == 0,
|
1042
|
+
# the exclude status of the both ends must agree, except for the cases
|
1043
|
+
# where both {#begin} and {#end} ani +nil+ (beginless and endless Range).
|
1044
|
+
# In other words, if the {#begin} is excluded, {#end} must be also excluded,
|
1042
1045
|
# and vice versa.
|
1043
|
-
# For example, (1...1) is NOT valid for
|
1046
|
+
# For example, +(1...1)+ is NOT valid for this reason,
|
1044
1047
|
# because any built-in Range object has the exclude status
|
1045
|
-
# of false (namely, inclusive) for {#begin}
|
1048
|
+
# of +false+ (namely, inclusive) for {#begin}, whereas
|
1049
|
+
# +RangeExtd(1...1, true)+ is valid and equal (+==+) to
|
1050
|
+
# {RangeExtd::NONE}.
|
1051
|
+
# 6. If either or both of {#begin} and {#end} is {RangeExtd::Nowhere::NOWHERE},
|
1052
|
+
# the range has to be {RangeExtd::NONE}.
|
1046
1053
|
#
|
1047
|
-
# Note the last
|
1054
|
+
# Note the second last point may change in the future release.
|
1048
1055
|
#
|
1049
1056
|
# Note ([2]..[5]) is NOT valid, because Array does not include Comparable
|
1050
1057
|
# for some reason, as of Ruby 2.1.1, even though it has the redefined
|
@@ -1053,8 +1060,10 @@ class RangeExtd < Range
|
|
1053
1060
|
#
|
1054
1061
|
# @example
|
1055
1062
|
#
|
1056
|
-
# RangeExtd.valid?(nil..nil) # =>
|
1057
|
-
# RangeExtd.valid?(nil...nil) # =>
|
1063
|
+
# RangeExtd.valid?(nil..nil) # => true
|
1064
|
+
# RangeExtd.valid?(nil...nil) # => true
|
1065
|
+
# RangeExtd.valid?(nil<..nil) # => true
|
1066
|
+
# RangeExtd.valid?(nil<...nil) # => true
|
1058
1067
|
# RangeExtd.valid?(0..0) # => true
|
1059
1068
|
# RangeExtd.valid?(0...0) # => false
|
1060
1069
|
# RangeExtd.valid?(0...) # => true
|
@@ -1068,6 +1077,7 @@ class RangeExtd < Range
|
|
1068
1077
|
# RangeExtd.valid?(3..Float::INFINITY, true) # => true
|
1069
1078
|
# RangeExtd.valid?(RangeExtd::Infinity::NEGATIVE..?d) # => true
|
1070
1079
|
# RangeExtd.valid?(RangeExtd::Infinity::NEGATIVE..?d, true) # => true
|
1080
|
+
# RangeExtd.valid?(RangeExtd::Nowhere::NOWHERE..nil) # => false
|
1071
1081
|
#
|
1072
1082
|
# @note The flag of exclude_begin|end can be given in the arguments in a couple of ways.
|
1073
1083
|
# If there is any duplication, those specified in the optional hash have the highest
|
@@ -1075,14 +1085,14 @@ class RangeExtd < Range
|
|
1075
1085
|
# If not, the values embeded in the {Range} or {RangeExtd} object
|
1076
1086
|
# in the parameter are used. In default, both of them are false.
|
1077
1087
|
#
|
1078
|
-
# @overload
|
1088
|
+
# @overload valid?(range, [exclude_begin=false, [exclude_end=false]])
|
1079
1089
|
# @param range [Object] Instance of Range or its subclasses, including RangeExtd
|
1080
1090
|
# @param exclude_begin [Boolean] If specified, this has the higher priority, or false in default.
|
1081
1091
|
# @param exclude_end [Boolean] If specified, this has the higher priority, or false in default.
|
1082
1092
|
#
|
1083
|
-
# @overload
|
1084
|
-
# @param obj_begin [Object] Any object that is
|
1085
|
-
# @param obj_end [Object] Any object that is Comparable with begin (or nil, for Ruby 2.6 onwards)
|
1093
|
+
# @overload valid?(obj_begin, obj_end, [exclude_begin=false, [exclude_end=false]])
|
1094
|
+
# @param obj_begin [Object] Any object that is +Comparable+ with end
|
1095
|
+
# @param obj_end [Object] Any object that is +Comparable+ with begin (or nil, for Ruby 2.6 onwards)
|
1086
1096
|
# @param exclude_begin [Boolean] If specified, this has the lower priority, or false in default.
|
1087
1097
|
# @param exclude_end [Boolean] If specified, this has the lower priority, or false in default.
|
1088
1098
|
#
|
@@ -1090,21 +1100,32 @@ class RangeExtd < Range
|
|
1090
1100
|
(vbeg, vend, exc_beg, exc_end) = _get_init_args(*inar)
|
1091
1101
|
|
1092
1102
|
if defined?(inar[0].is_none?) && inar[0].is_none? && exc_beg && exc_end
|
1103
|
+
# inar[0] is RangeExtd::NONE
|
1104
|
+
return true
|
1105
|
+
elsif vbeg.nil? && vbeg.nowhere? || vend.nil? && vend.nowhere?
|
1106
|
+
# RangeExtd::Nowhere::NOWHERE should not reside anywhere but in RangeExtd::NONE
|
1107
|
+
return false
|
1108
|
+
elsif vbeg.nil? && vend.nil?
|
1093
1109
|
return true
|
1094
1110
|
end
|
1111
|
+
|
1112
|
+
return false if !vbeg.respond_to?(:<=>)
|
1095
1113
|
begin
|
1096
1114
|
t = (vbeg <=> vend)
|
1097
1115
|
begin
|
1098
|
-
if
|
1116
|
+
if vbeg.nil? # Beginless Range introduced in Ruby 2.7
|
1117
|
+
return vend.respond_to?(:<=)
|
1118
|
+
elsif vend.nil?
|
1099
1119
|
begin
|
1100
1120
|
_ = (vbeg..nil) # Endless Range introduced in Ruby 2.6
|
1101
|
-
return vbeg.
|
1121
|
+
return vbeg.respond_to?(:<=)
|
1102
1122
|
rescue ArgumentError
|
1103
1123
|
# Before Ruby 2.6
|
1104
1124
|
return false
|
1105
1125
|
end
|
1106
1126
|
end
|
1107
|
-
return false if
|
1127
|
+
return false if !vend.respond_to?(:<=>)
|
1128
|
+
return false if t != -1*(vend <=> vbeg) # false if not commutative (n.b., an exception should not happen).
|
1108
1129
|
rescue NoMethodError, TypeError
|
1109
1130
|
if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) ||
|
1110
1131
|
(Float === vbeg && defined?(vend.infinity?) && vend.infinity?)
|
@@ -1112,7 +1133,8 @@ class RangeExtd < Range
|
|
1112
1133
|
end
|
1113
1134
|
return false # return
|
1114
1135
|
end
|
1115
|
-
rescue
|
1136
|
+
rescue
|
1137
|
+
warn "This should not happen. Contact the code developer (warn01)."
|
1116
1138
|
false # return
|
1117
1139
|
else
|
1118
1140
|
case t
|
@@ -1127,6 +1149,7 @@ class RangeExtd < Range
|
|
1127
1149
|
when 1
|
1128
1150
|
false
|
1129
1151
|
else
|
1152
|
+
warn "This should not happen. Contact the code developer (warn02)."
|
1130
1153
|
if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) ||
|
1131
1154
|
(Float === vbeg && defined?(vend.infinity?) && vend.infinity?)
|
1132
1155
|
warn self.const_get(:ERR_MSGS)[:infinity_compare] if !$VERBOSE.nil? # not tested so far?
|
@@ -1265,365 +1288,96 @@ class RangeExtd < Range
|
|
1265
1288
|
end # def re_inspect_core_orig(method)
|
1266
1289
|
|
1267
1290
|
|
1268
|
-
# Core routine for {#===}
|
1291
|
+
# Core routine for {#===}
|
1292
|
+
#
|
1269
1293
|
# @param [Object] r to compare.
|
1270
1294
|
# @param [Symbol] method of the method name.
|
1271
|
-
def
|
1272
|
-
if
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
true # (1<...1).eql? (2<...2) # => true (Fixnum <-> Fixnum) Yes!
|
1288
|
-
else
|
1289
|
-
false # (1.0<...1.0).eql? (2<...2) # => false (Float <-> Fixnum) No!
|
1290
|
-
end
|
1291
|
-
else
|
1292
|
-
(self.begin.class.ancestors - self.begin.class.included_modules - [Object, BasicObject]).each do |ec|
|
1293
|
-
if ec === r.begin
|
1294
|
-
return true # (1.0<...1.0) == (2<...2) # (Float<Numeric <-> Fixnum<Numeric) Yes!
|
1295
|
-
end
|
1295
|
+
def _re_equal_core(r, method=:==)
|
1296
|
+
return false if !r.respond_to? :exclude_end? # Not Range family.
|
1297
|
+
return false if !r.respond_to? :empty? # Not Range family.
|
1298
|
+
return false if !r.respond_to? :valid? # Not Range family.
|
1299
|
+
|
1300
|
+
return false if !r.valid? # always returns false with an invalid Range; n.b., RangeExtd#valid? is always true, hence they cannot be identical (eql?).
|
1301
|
+
|
1302
|
+
is_r_empty = r.empty?
|
1303
|
+
return !!is_r_empty if is_none? # RangeExtd::NONE==(1<...1); n.b. "!!" is redundant because r must be valid.
|
1304
|
+
return true if empty? && r.respond_to?(:is_none?) && r.is_none? # r is RangeExtd::NONE
|
1305
|
+
return false if !_both_same_nowhere_parity?(r) # inconsistent nil, non-nil, NOWHERE combination
|
1306
|
+
|
1307
|
+
if empty? && is_r_empty
|
1308
|
+
(self.begin.class.ancestors - self.begin.class.included_modules - [Object, BasicObject]).each do |ec|
|
1309
|
+
if ec === r.begin
|
1310
|
+
return true # (1.0<...1.0) == (2<...2) # (Float<Numeric <-> Fixnum<Numeric) Yes!
|
1296
1311
|
end
|
1297
|
-
false # (?a...?a) != (2<...2) # (String <-> Numeric) No!
|
1298
1312
|
end
|
1313
|
+
return false # (?a...?a) != (2<...2) # (String <-> Numeric) No!
|
1314
|
+
end
|
1315
|
+
|
1316
|
+
return false if !(self.exclude_end? ^! r.exclude_end?)
|
1299
1317
|
|
1300
|
-
|
1318
|
+
# Neither self nor r is guaranteed to be RangeExtd::NONE
|
1319
|
+
is_nil_equal = _both_eqleql_nil?(r, method)
|
1320
|
+
|
1321
|
+
if defined? r.exclude_begin? # r is RangeExtd
|
1301
1322
|
(self.exclude_begin? ^! r.exclude_begin?) &&
|
1302
1323
|
(self.exclude_end? ^! r.exclude_end?) &&
|
1303
|
-
(self.begin.send(method, r.begin))
|
1304
|
-
(self.end.send( method, r.end))
|
1305
|
-
# (self.begin == r.begin) &&
|
1306
|
-
# (self.end == r.end)
|
1324
|
+
(self.begin.send(method, r.begin) && self.end.send(method, r.end) || is_nil_equal)
|
1307
1325
|
else
|
1308
1326
|
# r is Range
|
1309
1327
|
if self.exclude_begin?
|
1310
1328
|
false
|
1311
1329
|
else
|
1312
|
-
@rangepart.send(method, r) # Comparison as two Range-s.
|
1330
|
+
is_nil_equal || @rangepart.send(method, r) # Comparison as two Range-s.
|
1313
1331
|
end
|
1314
1332
|
end
|
1315
|
-
end # def
|
1333
|
+
end # def _re_equal_core(r, method)
|
1334
|
+
private :_re_equal_core
|
1316
1335
|
|
1317
1336
|
# Core routine for {#min}, {#max}, {#minmax} etc.
|
1318
1337
|
# @param method [Symbol] of the method name.
|
1319
1338
|
# @param rest [Object]
|
1320
|
-
def
|
1339
|
+
def _re_min_max_core(method, *rest, &bloc)
|
1321
1340
|
# (1...3.5).max # => TypeError: cannot exclude non Integer end value
|
1322
|
-
if is_none?
|
1341
|
+
if is_none? || self.begin.nil? && self.begin.nowhere? || self.end.nil? && self.end.nowhere?
|
1342
|
+
# In fact, Range#minmax etc should be modified to deal with RangeExtd::Nowhere::NOWHERE (it is treated as nil at the moment)...
|
1323
1343
|
raise TypeError, "no meaningful range."
|
1324
|
-
elsif @exclude_begin
|
1325
|
-
if defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY
|
1326
|
-
raise TypeError, "can't exclude "+self.begin.to_s
|
1327
|
-
elsif ! defined? self.begin.succ
|
1328
|
-
raise TypeError, "can't iterate from "+self.begin.class.name
|
1329
|
-
else
|
1330
|
-
Range.new(self.begin.succ, self.end, exclude_end?).send(method, *rest, &bloc)
|
1331
|
-
end
|
1332
|
-
else
|
1333
|
-
@rangepart.send(method, *rest)
|
1334
1344
|
end
|
1335
|
-
end # def re_min_max_core(method, *rest, &bloc)
|
1336
1345
|
|
1346
|
+
if !@exclude_begin ||
|
1347
|
+
[:max, :max_by].include?(method) ||
|
1348
|
+
self.begin.respond_to?(:succ) ||
|
1349
|
+
[:min_by, :minmax_by].include?(method) && !block_given?
|
1350
|
+
# For the last one, if Range starts from something uniterable, it returns
|
1351
|
+
# Enumerator (without errors); but once it is executed, it will raise TypeError
|
1352
|
+
# "can't iterate from Float" or RangeError "cannot get the minimum of beginless range"
|
1353
|
+
return _converted_rangepart(transform_to_nil: false).send(method, *rest, &bloc)
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
# Now @exclude_begin is true, "begin" is not discrete, and block is given (if _by).
|
1357
|
+
raise TypeError, "can't exclude non-iterable begin value" #+self.begin.to_s
|
1358
|
+
end # def _re_min_max_core(method, *rest, &bloc)
|
1359
|
+
private :_re_min_max_core
|
1360
|
+
|
1361
|
+
self.remove_const :NONE if defined? self::NONE # tricky manoeuvre for documentation purposes... (see infinity.rb for the explanatory document)
|
1337
1362
|
# No range.
|
1338
1363
|
# In Ruby1.8, this causes ArgumentError: bad value for range (because (nil..nil) is unaccepted).
|
1339
|
-
NONE = RangeExtd.new(nil, nil, true, true, :Constant)
|
1340
|
-
|
1364
|
+
#NONE = RangeExtd.new(nil, nil, true, true, :Constant)
|
1365
|
+
NONE = RangeExtd.new(RangeExtd::Nowhere::NOWHERE, RangeExtd::Nowhere::NOWHERE, true, true, :Constant)
|
1341
1366
|
|
1367
|
+
self.remove_const :ALL if defined? self::ALL # tricky manoeuvre for documentation purposes... (see infinity.rb for the explanatory document)
|
1342
1368
|
# Range covers everything.
|
1343
1369
|
ALL = RangeExtd.new(Infinity::NEGATIVE, Infinity::POSITIVE, false, false, :Constant)
|
1344
|
-
#ALL = RangeExtd.new(Infinity::NEGATIVE..Infinity::POSITIVE, false, false, :Constant)
|
1345
1370
|
|
1346
1371
|
end # class RangeExtd < Range
|
1347
1372
|
|
1348
|
-
|
1349
|
-
|
1350
|
-
#
|
1351
|
-
#== Summary
|
1352
|
-
#
|
1353
|
-
# Modifies {#==}, {#eql?} and add methods of
|
1354
|
-
# {#valid?}, {#empty?}, {#null?}, {#is_none?} and {#is_all?}.
|
1355
|
-
#
|
1356
|
-
class Range
|
1357
|
-
|
1358
|
-
alias_method :equal_prerangeextd?, :== if ! self.method_defined?(:equal_prerangeextd?) # No overwriting.
|
1359
|
-
|
1360
|
-
# It is extended to handle {RangeExtd} objects.
|
1361
|
-
# For each element, that is, {#begin} and {#end},
|
1362
|
-
# this uses their method of ==(). See {#eql?}.
|
1363
|
-
#
|
1364
|
-
# As long as the comparison is limited within {Range} objects,
|
1365
|
-
# the returned value of this method has unchanged.
|
1366
|
-
#
|
1367
|
-
# A note of caution is, some ranges which the built-in Range accepts,
|
1368
|
-
# are now regarded as NOT valid, such as, (1...1) and (nil..nil)
|
1369
|
-
# (the latter was not permitted in Ruby 1.8), though you can still
|
1370
|
-
# use them;
|
1371
|
-
# (1...1).valid? # => false
|
1372
|
-
# On the other hand, {RangeExtd} class does not accept or create
|
1373
|
-
# any invalid range; for any {RangeExtd} object, RangeExtd#valid?
|
1374
|
-
# returns true. For example, there is no {RangeExtd} object
|
1375
|
-
# that is expressed as (1...1) (See {#valid?} for detail).
|
1376
|
-
#
|
1377
|
-
# For that reason, when those non-valid Range objects are compared
|
1378
|
-
# with a {RangeExtd} object, the returned value may not be what
|
1379
|
-
# you would expect. For example,
|
1380
|
-
# (1...1) == RangeExtd(1, 1, true, true) # => false.
|
1381
|
-
# The former is an invalid range, while the latter is
|
1382
|
-
# a rigidly-defined empty range.
|
1383
|
-
#
|
1384
|
-
# Consult {#valid?} and {RangeExtd#==} for more detail.
|
1385
|
-
def ==(r)
|
1386
|
-
equal_core(r, :==, :equal_prerangeextd?)
|
1387
|
-
end
|
1388
|
-
|
1389
|
-
|
1390
|
-
alias :eql_prerangeextd? :eql? if ! self.method_defined?(:eql_prerangeextd?) # No overwriting.
|
1391
|
-
|
1392
|
-
# Same as {#==}, but the comparison is made with eql?() method.
|
1393
|
-
def eql?(r)
|
1394
|
-
equal_core(r, :eql?, :eql_prerangeextd?)
|
1395
|
-
end
|
1396
|
-
|
1397
|
-
|
1398
|
-
# Returns true if self is valid as a comparable range.
|
1399
|
-
#
|
1400
|
-
# See {RangeExtd.valid?} for the definition of what is valid
|
1401
|
-
# and more examples.
|
1402
|
-
#
|
1403
|
-
# See {#empty?} and {#null?}, too.
|
1404
|
-
#
|
1405
|
-
# @example
|
1406
|
-
# (nil..nil).valid? # => false
|
1407
|
-
# (0..0).valid? # => true
|
1408
|
-
# (0...0).valid? # => false
|
1409
|
-
# (2..-1).valid? # => false
|
1410
|
-
# RangeExtd(0...0, true) # => true
|
1411
|
-
# (3..Float::INFINITY).valid? # => true
|
1412
|
-
# RangeExtd::NONE.valid? # => true
|
1413
|
-
# RangeExtd::ALL.valid? # => true
|
1414
|
-
#
|
1415
|
-
# @note By definition, all the {RangeExtd} instances are valid,
|
1416
|
-
# because {RangeExtd.new} checks the validity.
|
1417
|
-
def valid?
|
1418
|
-
RangeExtd.valid?(self)
|
1419
|
-
end # def valid?
|
1420
|
-
|
1421
|
-
|
1422
|
-
# Returns true if self is empty.
|
1423
|
-
# Returns nil if self is not valid (nb., any RangeExtd instance is valid.)
|
1424
|
-
# Otherwise false.
|
1425
|
-
#
|
1426
|
-
# The definition of what is empty is as follow.
|
1427
|
-
#
|
1428
|
-
# 1. the range must be valid: {#valid?} => true
|
1429
|
-
# 2. if the range id discrete, that is, {#begin} has
|
1430
|
-
# [#succ] method, there must be no member within the range:
|
1431
|
-
# {#to_a}.empty? => true
|
1432
|
-
# 3. if the range is continuous, that is, {#begin} does not have
|
1433
|
-
# [#succ] method, {#begin} and {#end} must be equal
|
1434
|
-
# (({#begin} <=> {#end}) => 0) and both the boundaries must
|
1435
|
-
# be excluded: ({#exclude_begin?} && {#exclude_end?}) => true.
|
1436
|
-
# Note that ranges with equal {#begin} and {#end} with
|
1437
|
-
# inconsistent two exclude status are not valid, and the built-in
|
1438
|
-
# Range always has the {#begin}-exclude status of false.
|
1439
|
-
#
|
1440
|
-
# In these conditions, none of Range instance would return true in {#empty?}.
|
1441
|
-
#
|
1442
|
-
# @example
|
1443
|
-
# (nil..nil).empty? # => nil
|
1444
|
-
# (1...1).empty? # => nil
|
1445
|
-
# (1..1).empty? # => false
|
1446
|
-
# RangeExtd(1...1, true).empty? # => true
|
1447
|
-
# RangeExtd(1...2, true).empty? # => true
|
1448
|
-
# RangeExtd(1.0...2, true).empty? # => false
|
1449
|
-
# RangeExtd(?a...?b, true).empty? # => true
|
1450
|
-
# RangeExtd::NONE.empty? # => true
|
1451
|
-
#
|
1452
|
-
# @note to check whether it is either empty or invalid, use {#null?}.
|
1453
|
-
# See {#valid?} and {RangeExtd.valid?}, too.
|
1454
|
-
#
|
1455
|
-
# @return [Boolean, nil]
|
1456
|
-
def empty?
|
1457
|
-
# This is basically for the sake of sub-classes, as any built-in Range instance
|
1458
|
-
# always returns either nil or false.
|
1459
|
-
|
1460
|
-
if !valid?
|
1461
|
-
return nil
|
1462
|
-
elsif defined?(self.is_none?) && self.is_none?
|
1463
|
-
return true
|
1464
|
-
end
|
1465
|
-
|
1466
|
-
t = (self.begin() <=> self.end())
|
1467
|
-
case t
|
1468
|
-
when -1
|
1469
|
-
if (defined?(self.exclude_begin?)) &&
|
1470
|
-
exclude_begin? &&
|
1471
|
-
exclude_end? &&
|
1472
|
-
defined?(self.begin().succ) &&
|
1473
|
-
(self.begin().succ == self.end())
|
1474
|
-
true # e.g., ("a"<..."b")
|
1475
|
-
else
|
1476
|
-
false
|
1477
|
-
end
|
1478
|
-
when 0
|
1479
|
-
if defined?(self.boundary) && self.boundary.nil? # for RangeOpen
|
1480
|
-
# RangeExtd::NONE or RangeExtd::All
|
1481
|
-
if self.exclude_end?
|
1482
|
-
true # RangeOpen::NONE
|
1483
|
-
else
|
1484
|
-
false # RangeOpen::ALL
|
1485
|
-
end
|
1486
|
-
else
|
1487
|
-
if defined?(self.exclude_begin?)
|
1488
|
-
t2 = self.exclude_begin?
|
1489
|
-
else
|
1490
|
-
t2 = false # == return false
|
1491
|
-
end
|
1492
|
-
(t2 && exclude_end?)
|
1493
|
-
end
|
1494
|
-
when 1
|
1495
|
-
nil # redundant, as it should not be valid in the first place.
|
1496
|
-
else
|
1497
|
-
nil # redundant, as it should not be valid in the first place.
|
1498
|
-
end
|
1499
|
-
end # def empty?
|
1500
|
-
|
1501
|
-
|
1502
|
-
# Returns true if it is either empty or invalid. false otherwise.
|
1503
|
-
# See {#empty?} and {#valid?}.
|
1504
|
-
def null?
|
1505
|
-
(! valid?) || empty?
|
1506
|
-
end
|
1507
|
-
|
1508
|
-
# @return [FalseClass]
|
1509
|
-
def is_none?
|
1510
|
-
false
|
1511
|
-
end
|
1512
|
-
|
1513
|
-
# @return [FalseClass]
|
1514
|
-
def is_all?
|
1515
|
-
false
|
1516
|
-
end
|
1517
|
-
|
1518
|
-
|
1519
|
-
# Return true if self and the other are equivalent; if [#to_a] is defined, it is similar to
|
1520
|
-
# (self.to_a == other.to_a)
|
1521
|
-
# (though the ends are checked more rigorously), and if not, equivalent to
|
1522
|
-
# (self == other)
|
1523
|
-
#
|
1524
|
-
# @example
|
1525
|
-
# (3...7).equiv?(3..6) # => true
|
1526
|
-
# (3...7).equiv?(3..6.0) # => false
|
1527
|
-
# (3...7).equiv?(3.0..6.0) # => false
|
1528
|
-
# (3...7).equiv?(3..6.5) # => false
|
1529
|
-
# (3...7).equiv?(3.0...7.0) # => true
|
1530
|
-
# (3...7.0).equiv?(3..6) # => true
|
1531
|
-
# (3...7.0).equiv?(3.0..6) # => false
|
1532
|
-
#
|
1533
|
-
# @param other [Range, RangeExtd]
|
1534
|
-
def equiv?(other)
|
1535
|
-
t_or_f = (defined?(self.begin.succ) && defined?(other.begin.succ) && defined?(other.end) && defined?(other.exclude_end?))
|
1536
|
-
if ! t_or_f
|
1537
|
-
return(self == other) # succ() for begin is not defined.
|
1538
|
-
else
|
1539
|
-
# Checking the begins.
|
1540
|
-
if defined?(other.exclude_begin?) && other.exclude_begin? # The other is RangeExtd with exclude_begin?==true.
|
1541
|
-
if self.begin != other.begin.succ
|
1542
|
-
return false
|
1543
|
-
else
|
1544
|
-
# Pass
|
1545
|
-
end
|
1546
|
-
elsif (self.begin != other.begin)
|
1547
|
-
return false
|
1548
|
-
end
|
1549
|
-
|
1550
|
-
# Now, the begins agreed. Checking the ends.
|
1551
|
-
if (self.end == other.end)
|
1552
|
-
if (exclude_end? ^! other.exclude_end?)
|
1553
|
-
return true
|
1554
|
-
else
|
1555
|
-
return false
|
1556
|
-
end
|
1557
|
-
else # if (self.end == other.end)
|
1558
|
-
if (exclude_end? ^! other.exclude_end?)
|
1559
|
-
return false
|
1560
|
-
elsif ( exclude_end? && defined?(other.end.succ) && (self.end == other.end.succ)) ||
|
1561
|
-
(other.exclude_end? && defined?( self.end.succ) && (self.end.succ == other.end))
|
1562
|
-
return true
|
1563
|
-
else
|
1564
|
-
return false
|
1565
|
-
end
|
1566
|
-
end # if (self.end == other.end)
|
1567
|
-
end # if ! t_or_f
|
1568
|
-
|
1569
|
-
end # def equiv?(other)
|
1570
|
-
|
1571
|
-
|
1572
|
-
############## pravate methods of Range ##############
|
1573
|
-
|
1574
|
-
private
|
1575
|
-
|
1576
|
-
# True if obj is Comparable.
|
1577
|
-
def is_comparable?(obj)
|
1578
|
-
if defined?(obj.<=) # Comparable?
|
1579
|
-
true
|
1580
|
-
else
|
1581
|
-
false
|
1582
|
-
end
|
1583
|
-
end
|
1584
|
-
|
1585
|
-
# @param r [Object] to compare.
|
1586
|
-
# @param method [Symbol] of the method name.
|
1587
|
-
# @param method_pre [Symbol] of the backed-up original method name.
|
1588
|
-
def equal_core(r, method, method_pre)
|
1589
|
-
if (! defined? r.exclude_end?) || (! defined? r.is_none?) || (! defined? r.empty?)
|
1590
|
-
false # Not Range family.
|
1591
|
-
# elsif empty? && defined?(r.is_none?) && r.is_none? # r is RangeExtd::NONE
|
1592
|
-
# true
|
1593
|
-
# elsif empty? && r.empty?
|
1594
|
-
# # None of built-in Range class object can be #empty?==true - This is for sub-class.
|
1595
|
-
#
|
1596
|
-
elsif defined? r.exclude_begin?
|
1597
|
-
# Either RangeExtd or RangeOpen object.
|
1598
|
-
if r.exclude_begin?
|
1599
|
-
false # self(Range) has always the inclusive begin.
|
1600
|
-
else
|
1601
|
-
# It could do with a single line,
|
1602
|
-
# self.begin.send(method_pre, r)
|
1603
|
-
# if this was for RangeExtd===r, but not for RangeOpen.
|
1604
|
-
if (self.exclude_end? ^ r.exclude_end?)
|
1605
|
-
false
|
1606
|
-
elsif (self.begin.send(method, r.begin) && self.end.send(method, r.end))
|
1607
|
-
true
|
1608
|
-
else
|
1609
|
-
false
|
1610
|
-
end
|
1611
|
-
end
|
1612
|
-
else
|
1613
|
-
self.send(method_pre, r) # r is Range.
|
1614
|
-
end
|
1615
|
-
end # def equal_core(r, method, method_pre)
|
1616
|
-
|
1617
|
-
end # class Range
|
1618
|
-
|
1373
|
+
# require Range; it is necessary to modify Range if in a backward-compatible way.
|
1374
|
+
require_relative "range_extd/range" if !Range.method_defined?(:eql_prerangeextd?)
|
1619
1375
|
|
1620
1376
|
# Constant-form of {#RangeExtd}.
|
1621
|
-
# #RangeExtd(*) is equivalent to {#RangeExtd.new}.
|
1622
1377
|
#
|
1623
|
-
#
|
1624
|
-
#
|
1625
|
-
#
|
1626
|
-
# deal with this, other than adding **hs.
|
1378
|
+
# +RangeExtd()+ is equivalent to {#RangeExtd.new}().
|
1379
|
+
#
|
1380
|
+
# @return [RangeExtd]
|
1627
1381
|
def RangeExtd(*rest, **hs, &b)
|
1628
1382
|
RangeExtd.new(*rest, **hs, &b)
|
1629
1383
|
end
|