range_extd 1.0 → 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 +31 -10
- data/Makefile +7 -3
- data/News +8 -0
- data/README.en.rdoc +1163 -250
- data/README.ja.rdoc +1163 -250
- 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} +440 -628
- data/range_extd.gemspec +50 -0
- data/test/all_required_test.rb +173 -0
- data/test/test_range_extd.rb +649 -157
- data/test/test_range_extd_nowhere.rb +84 -0
- metadata +29 -16
- data/lib/range_extd/infinity/infinity.rb +0 -392
@@ -1,34 +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
|
23
|
-
end
|
24
|
-
if req_file.empty?
|
25
|
-
raise err1st
|
6
|
+
begin
|
7
|
+
require_relative req_file
|
8
|
+
rescue LoadError
|
9
|
+
require req_file
|
26
10
|
end
|
27
|
-
end
|
11
|
+
end
|
28
12
|
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
32
20
|
|
33
21
|
# =Class RangeExtd
|
34
22
|
#
|
@@ -39,20 +27,22 @@ end # req_files.each do |req_file|
|
|
39
27
|
#
|
40
28
|
# Extended Range class that features:
|
41
29
|
# 1. includes exclude_begin? (to exclude the "begin" boundary),
|
42
|
-
# 2. allows open-ended range
|
30
|
+
# 2. allows open-ended range to the infinity (very similar to beginless/endless Range),
|
43
31
|
# 3. defines NONE and ALL constants,
|
44
32
|
# 4. the first self-consistent logical structure,
|
45
|
-
# 5. complete
|
33
|
+
# 5. complete compatibility with the built-in Range.
|
46
34
|
#
|
47
35
|
# The instance of this class is immutable, that is, you can not
|
48
36
|
# alter the element once an instance is generated.
|
49
37
|
#
|
38
|
+
# This class has some constants
|
39
|
+
#
|
50
40
|
# What is valid is checked with the class method {RangeExtd.valid?}.
|
51
41
|
# See the document of that method for the definition.
|
52
42
|
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
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.
|
56
46
|
#
|
57
47
|
# @example An instance of a range of 5 to 8 with both ends being exclusive is created as
|
58
48
|
# r = RangeExtd(5...8, true)
|
@@ -60,6 +50,10 @@ end # req_files.each do |req_file|
|
|
60
50
|
#
|
61
51
|
class RangeExtd < Range
|
62
52
|
|
53
|
+
# To conrol how the {RangeExtd} should be displayed or set (in one form).
|
54
|
+
# It can be read and reset by {RangeExtd.middle_strings} and
|
55
|
+
# {RangeExtd.middle_strings=}
|
56
|
+
# Default is +['', '', '<', '..', '.', '', '']+
|
63
57
|
@@middle_strings = []
|
64
58
|
|
65
59
|
# Error messages
|
@@ -92,7 +86,7 @@ class RangeExtd < Range
|
|
92
86
|
# @option opts [Boolean] :exclude_end If specified, this has the highest priority, or false in default.
|
93
87
|
#
|
94
88
|
# @overload new(obj_begin, obj_end, [exclude_begin=false, [exclude_end=false]], opts)
|
95
|
-
# @param obj_begin [Object] Any object that is
|
89
|
+
# @param obj_begin [Object] Any object that is +Comparable+ with end
|
96
90
|
# @param obj_end [Object] Any object that is Comparable with begin
|
97
91
|
# @param exclude_begin [Boolean] If specified, this has the lower priority, or false in default.
|
98
92
|
# @param exclude_end [Boolean] If specified, this has the lower priority, or false in default.
|
@@ -100,9 +94,9 @@ class RangeExtd < Range
|
|
100
94
|
# @option opts [Boolean] :exclude_end If specified, this has the higher priority, or false in default.
|
101
95
|
#
|
102
96
|
# @overload new(obj_begin, string_form, obj_end, [exclude_begin=false, [exclude_end=false]], opts)
|
103
|
-
# @param obj_begin [Object] Any object that is
|
97
|
+
# @param obj_begin [Object] Any object that is +Comparable+ with end
|
104
98
|
# @param string_form [Object] String form (without pre/postfix) of range expression set by {RangeExtd.middle_strings=}()
|
105
|
-
# @param obj_end [Object] Any object that is Comparable with begin
|
99
|
+
# @param obj_end [Object] Any object that is +Comparable+ with begin
|
106
100
|
# @param exclude_begin [Boolean] If specified, this has the lower priority, or false in default.
|
107
101
|
# @param exclude_end [Boolean] If specified, this has the lower priority, or false in default.
|
108
102
|
# @option opts [Boolean] :exclude_begin If specified, this has the higher priority, or false in default.
|
@@ -125,17 +119,31 @@ class RangeExtd < Range
|
|
125
119
|
# @raise [ArgumentError] particularly if the range to be created is not {#valid?}.
|
126
120
|
def initialize(*inar, **hsopt) # **k expression from Ruby 1.9?
|
127
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
|
+
|
128
126
|
if inar[4] == :Constant
|
129
|
-
# Special case to create two Constants
|
130
|
-
super(*inar[0..2])
|
127
|
+
# Special case to create two Constants (NONE and ALL)
|
131
128
|
@rangepart = (inar[2] ? (inar[0]...inar[1]) : (inar[0]..inar[1]))
|
132
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])
|
133
142
|
return
|
134
143
|
end
|
135
144
|
|
136
|
-
|
137
|
-
|
138
|
-
# == [RangeBeginValue, RangeEndValue, exclude_end?, exclude_begin?]
|
145
|
+
arout = RangeExtd.send(:_get_init_args, *inar, **hsopt)
|
146
|
+
# == [RangeBeginValue, RangeEndValue, exclude_begin?, exclude_end?]
|
139
147
|
|
140
148
|
### The following routine is obsolete.
|
141
149
|
### Users, if they wish, should call RangeExtd::Infinity.overwrite_compare() beforehand.
|
@@ -170,30 +178,27 @@ class RangeExtd < Range
|
|
170
178
|
raise RangeError, "the combination of the arguments does not constitute a valid RangeExtd instance."
|
171
179
|
end
|
172
180
|
|
181
|
+
@exclude_end = arout.pop
|
173
182
|
@exclude_begin = arout.pop
|
174
|
-
|
175
|
-
@rangepart = Range.new(*
|
176
|
-
super(*
|
177
|
-
|
178
|
-
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)
|
179
187
|
|
180
188
|
|
181
189
|
# true if self is identical to {RangeExtd::NONE}.
|
190
|
+
#
|
191
|
+
# Overwriting {Range#is_none?}
|
182
192
|
# This is different from {#==} method!
|
193
|
+
#
|
183
194
|
# @example
|
184
|
-
# RangeExtd(0,0,
|
185
|
-
# RangeExtd(0,0,
|
186
|
-
# 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
|
187
199
|
# RangeExtd::NONE.is_none? # => true
|
188
200
|
def is_none?
|
189
|
-
|
190
|
-
end
|
191
|
-
|
192
|
-
# true if self is identical to {RangeExtd::ALL} ({#==} does not mean it at all!)
|
193
|
-
# @example
|
194
|
-
# (RangeExtd::Infinity::NEGATIVE..RangeExtd::Infinity::POSITIVE).is_all? # => false
|
195
|
-
def is_all?
|
196
|
-
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
|
197
202
|
end
|
198
203
|
|
199
204
|
|
@@ -216,11 +221,10 @@ class RangeExtd < Range
|
|
216
221
|
# this returns true, regardless of their boundary values.
|
217
222
|
# And any empty range is equal to RangeExtd::Infinity::NONE.
|
218
223
|
#
|
219
|
-
# Note the last example will return false for
|
220
|
-
#
|
221
|
-
# See {#eql?}
|
224
|
+
# Note the last example will return false for +#eql?+
|
222
225
|
#
|
223
226
|
# @example
|
227
|
+
# (1...1) == RangeExtd::NONE # => false (b/c the Range is invalid)
|
224
228
|
# (1<...1) == RangeExtd::NONE # => true
|
225
229
|
# (?a<...?b) == RangeExtd::NONE # => true
|
226
230
|
# (1<...1) == (2<...2) # => true
|
@@ -231,25 +235,27 @@ class RangeExtd < Range
|
|
231
235
|
#
|
232
236
|
# @return [Boolean]
|
233
237
|
def ==(r)
|
234
|
-
|
238
|
+
_re_equal_core(r, :==)
|
235
239
|
end # def ==(r)
|
236
240
|
|
237
|
-
#
|
238
|
-
#
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
#
|
250
|
-
|
251
|
-
|
252
|
-
|
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)
|
253
259
|
|
254
260
|
|
255
261
|
# If the object is open-ended to the negative (Infinity),
|
@@ -263,7 +269,7 @@ class RangeExtd < Range
|
|
263
269
|
# In the case of the former, after finite trials of [#succ] from ?c, it reaches the end (?z).
|
264
270
|
# In the latter, after finit trials of [#succ] from the begin ?a, it reaches the end (?z).
|
265
271
|
# Therefore it is theoretically possible to prove it (n.b., the actual
|
266
|
-
# algorithm of built-in
|
272
|
+
# algorithm of built-in +Range#include?+ is different and cheating!
|
267
273
|
# See below.).
|
268
274
|
#
|
269
275
|
# However, in the case of
|
@@ -275,11 +281,11 @@ class RangeExtd < Range
|
|
275
281
|
#
|
276
282
|
# Note
|
277
283
|
# (?B..?z) === 'dd' # => false
|
278
|
-
# as Ruby's {Range} knows the algorithm of
|
279
|
-
# 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?+.
|
280
286
|
# {https://github.com/ruby/ruby/blob/trunk/range.c}
|
281
287
|
#
|
282
|
-
# Therefore, even if you change the definition of
|
288
|
+
# Therefore, even if you change the definition of +String#succ+
|
283
289
|
# so that 'B'.succ => 'dd', 'dd'.succ => 'z', as follows,
|
284
290
|
# class String
|
285
291
|
# alias :succ_orig :succ
|
@@ -293,7 +299,7 @@ class RangeExtd < Range
|
|
293
299
|
# end
|
294
300
|
# end
|
295
301
|
# end
|
296
|
-
# the resutl of
|
302
|
+
# the resutl of +Range#===+ will unchange;
|
297
303
|
# (?B..?z) === 'dd' # => false
|
298
304
|
# (?B..?z).to_a # => ["B", "dd", "z"]
|
299
305
|
#
|
@@ -306,29 +312,31 @@ class RangeExtd < Range
|
|
306
312
|
def ===(obj)
|
307
313
|
# ("a".."z")===("cc") # => false
|
308
314
|
|
309
|
-
return false if
|
315
|
+
return false if empty? # n.b, NONE includes nothing, even NOWHERE (because of exclude_begin/end)
|
310
316
|
|
311
|
-
|
312
|
-
|
317
|
+
rapart = _converted_rangepart
|
318
|
+
beg = rapart.begin
|
319
|
+
if beg.nil? && !beg.nowhere?
|
320
|
+
return rapart.send(__method__, obj)
|
321
|
+
end
|
313
322
|
|
323
|
+
begin
|
324
|
+
_ = 1.0+obj # OK if Numeric.
|
325
|
+
return cover?(obj) # This excludes begin() if need be.
|
314
326
|
rescue TypeError
|
315
|
-
|
316
|
-
beg = self.begin()
|
317
|
-
if defined?(beg.infinity?) && beg.infinity? || beg == -Infinity::FLOAT_INFINITY
|
318
|
-
return nil
|
319
|
-
# raise TypeError "can't iterate from -Infinity"
|
320
|
-
end
|
327
|
+
end
|
321
328
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
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
|
328
335
|
|
329
|
-
|
330
|
-
|
336
|
+
each do |ei| # This excludes begin() if need be.
|
337
|
+
return true if ei == obj
|
331
338
|
end
|
339
|
+
false
|
332
340
|
end # def ===(obj)
|
333
341
|
|
334
342
|
alias :include? :===
|
@@ -442,8 +450,8 @@ class RangeExtd < Range
|
|
442
450
|
# (Rational(36,10)..5).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Rational (Ruby 2.1)
|
443
451
|
# (3..Rational(61,10)).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Fixnum (Ruby 2.1)
|
444
452
|
#
|
445
|
-
#
|
446
|
-
# 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.
|
447
455
|
# If Float, it searches on the binary plane.
|
448
456
|
# If Integer, the search is conducted on the descrete Integer points only,
|
449
457
|
# and no search will be made in between the adjascent integers.
|
@@ -451,7 +459,7 @@ class RangeExtd < Range
|
|
451
459
|
# Given that, {RangeExtd#bsearch} follows basically the same, even when exclude_begin? is true.
|
452
460
|
# If either end is Float, it searches between begin*(1+Float::EPSILON) and end.
|
453
461
|
# If both are Integer, it searches from begin+1.
|
454
|
-
# When {#exclude_begin?} is false, {RangeExtd#bsearch} is identical to
|
462
|
+
# When {#exclude_begin?} is false, {RangeExtd#bsearch} is identical to +Range#bsearch+.
|
455
463
|
#
|
456
464
|
def bsearch(*rest, &bloc)
|
457
465
|
if is_none? # No need of null?(), supposedly!
|
@@ -481,97 +489,110 @@ class RangeExtd < Range
|
|
481
489
|
end # def bsearch(*rest, &bloc)
|
482
490
|
|
483
491
|
|
484
|
-
#
|
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?+
|
485
517
|
def cover?(i)
|
486
518
|
# ("a".."z").cover?("cc") # => true
|
487
519
|
# (?B..?z).cover?('dd') # => true (though 'dd'.succ would never reach ?z)
|
488
520
|
|
489
|
-
return false if
|
521
|
+
return false if empty? # equivalent to null? in this case because self is alwasy true==valid?
|
490
522
|
|
491
|
-
if @exclude_begin
|
492
|
-
|
493
|
-
false
|
494
|
-
else
|
495
|
-
@rangepart.send(__method__, i)
|
496
|
-
end
|
523
|
+
if @exclude_begin && self.begin == i
|
524
|
+
false
|
497
525
|
else
|
498
526
|
@rangepart.send(__method__, i)
|
499
527
|
end
|
500
528
|
end # def cover?(i)
|
501
529
|
|
502
530
|
|
503
|
-
#
|
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.
|
504
534
|
# @return [RangeExtd] self
|
505
535
|
# @return [Enumerator] if block is not given.
|
506
|
-
#
|
507
536
|
def each(*rest, &bloc)
|
508
537
|
# (1...3.5).each{|i|print i} # => '123' to STDOUT
|
509
538
|
# (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
|
510
539
|
# (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
|
511
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.
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
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
|
525
557
|
else
|
526
|
-
|
558
|
+
_converted_rangepart(consider_exclude_begin: true, raises: false).send(method, *rest)
|
527
559
|
end
|
528
560
|
end
|
561
|
+
private :_step_each_core
|
529
562
|
|
530
|
-
|
531
|
-
#
|
532
|
-
#
|
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
|
+
#
|
533
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?}
|
534
|
-
# @
|
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.
|
535
578
|
# @return [Object] if no argument is given, equivalent to {#end}.
|
536
579
|
# @return [Array] if an argument is given.
|
537
580
|
def first(*rest)
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
@rangepart.first(*rest)
|
542
|
-
else
|
543
|
-
case rest.size
|
544
|
-
when 0
|
545
|
-
self.begin
|
546
|
-
when 1
|
547
|
-
if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8.
|
548
|
-
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)."
|
549
|
-
end
|
550
|
-
|
551
|
-
## Check the argument.
|
552
|
-
Array.new[ rest[0] ] # Check Type of rest[0] (if invalid, it should raise TypeError)
|
553
|
-
|
554
|
-
begin
|
555
|
-
if rest[0] < 0
|
556
|
-
raise ArgumentError, "negative array size (or size too big)"
|
557
|
-
end
|
558
|
-
rescue NoMethodError
|
559
|
-
# Should not happen, but just to play safe.
|
560
|
-
end
|
561
|
-
|
562
|
-
## Main
|
563
|
-
if ! defined? self.begin.succ
|
564
|
-
raise TypeError, "can't iterate from "+self.begin.class.name
|
565
|
-
end
|
581
|
+
if is_none?
|
582
|
+
raise RangeError, "cannot get the first element of RangeExtd::NONE"
|
583
|
+
end
|
566
584
|
|
567
|
-
|
568
|
-
|
569
|
-
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)"
|
570
|
-
end
|
571
|
-
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)
|
572
587
|
end # def first(*rest)
|
573
588
|
|
574
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
|
+
#
|
575
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.
|
576
597
|
#
|
577
598
|
def hash(*rest)
|
@@ -598,7 +619,8 @@ class RangeExtd < Range
|
|
598
619
|
end
|
599
620
|
|
600
621
|
|
601
|
-
#
|
622
|
+
# Updated version of +Range#last+, considering {#exclude_begin?}.
|
623
|
+
#
|
602
624
|
# If either (let alone both) side of the edge is Infinity, you can not give
|
603
625
|
# an argument in practice, the number of the members of the returned array.
|
604
626
|
#
|
@@ -606,41 +628,55 @@ class RangeExtd < Range
|
|
606
628
|
# @return [Object] if no argument is given, equivalent to {#end}.
|
607
629
|
# @return [Array] if an argument is given.
|
608
630
|
def last(*rest)
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
when 0
|
613
|
-
self.end
|
614
|
-
when 1
|
615
|
-
if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8.
|
616
|
-
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)."
|
617
|
-
end
|
631
|
+
if is_none?
|
632
|
+
raise RangeError, "cannot get the last element of RangeExtd::NONE"
|
633
|
+
end
|
618
634
|
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
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
|
627
659
|
end
|
628
|
-
else
|
629
|
-
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)"
|
630
660
|
end
|
631
|
-
end # def last(*rest)
|
632
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
|
633
669
|
|
634
670
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
635
671
|
#
|
636
672
|
def min(*rest, &bloc)
|
637
|
-
|
673
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
638
674
|
end
|
639
675
|
|
640
676
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
641
677
|
#
|
642
678
|
def min_by(*rest, &bloc)
|
643
|
-
|
679
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
644
680
|
end
|
645
681
|
|
646
682
|
|
@@ -651,7 +687,7 @@ class RangeExtd < Range
|
|
651
687
|
# (1.3...5).minmax # => TypeError: can't iterate from Float
|
652
688
|
# Note that max() for the same Range raises an exception.
|
653
689
|
# In that sense, it is inconsistent!
|
654
|
-
|
690
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
655
691
|
end
|
656
692
|
|
657
693
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
@@ -660,31 +696,31 @@ class RangeExtd < Range
|
|
660
696
|
# (0...3.5).minmax # => [0, 3]
|
661
697
|
# Note that max() for the same Range raises an exception.
|
662
698
|
# In that sense, it is inconsistent!
|
663
|
-
|
699
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
664
700
|
end
|
665
701
|
|
666
702
|
|
667
703
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
668
704
|
#
|
669
705
|
def max(*rest, &bloc)
|
670
|
-
|
706
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
671
707
|
end
|
672
708
|
|
673
709
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
674
710
|
#
|
675
711
|
def max_by(*rest, &bloc)
|
676
|
-
|
712
|
+
_re_min_max_core(__method__, *rest, &bloc)
|
677
713
|
end
|
678
714
|
|
679
715
|
|
680
|
-
# Implementation of
|
716
|
+
# Implementation of +Range#size+ to this class.
|
681
717
|
#
|
682
718
|
# It is essentially the same, but the behaviour when {#exclude_begin?} is true
|
683
719
|
# may not always be natural.
|
684
720
|
# See {#first} for the definition when {#exclude_begin?} is true.
|
685
721
|
#
|
686
|
-
#
|
687
|
-
# And in
|
722
|
+
# +Range#size+ only works for Numeric ranges.
|
723
|
+
# And in +Range#size+, the value is calculated when the initial value is
|
688
724
|
# non-Integer, by stepping by 1.0 from the {#begin} value, and the returned
|
689
725
|
# value is an integer.
|
690
726
|
# For example,
|
@@ -726,15 +762,38 @@ class RangeExtd < Range
|
|
726
762
|
# in the range whereas 4.8 is not in the range by definition,
|
727
763
|
# but not the example right above.
|
728
764
|
#
|
765
|
+
# === Ruby 2.6 Endless Range and Infinity.
|
766
|
+
#
|
767
|
+
# Before RangeExtd Ver.1.1, if a {RangeExtd} object contains
|
768
|
+
# {RangeExtd::Infinity} objects for either begin or end, {#size} used to
|
769
|
+
# be always +Float::INFINITY+ no matter what the other object is
|
770
|
+
# (except when the other object is also a {RangeExtd::Infinity} object).
|
771
|
+
# However, since the introduction of the endless Range in Ruby 2.6,
|
772
|
+
# Ruby returns as follows:
|
773
|
+
#
|
774
|
+
# (5..).size # => Float::INFINITY
|
775
|
+
# (?a..).size # => nil
|
776
|
+
#
|
777
|
+
# Accordingly, this class {RangeExtd} now behaves the same as Ruby (2.6 or later).
|
778
|
+
#
|
779
|
+
# Similarly,
|
780
|
+
#
|
781
|
+
# (Float::INFINITY..Float::INFINITY).size
|
782
|
+
#
|
783
|
+
# has changed (I do not know in which Ruby version)!
|
784
|
+
# It used to be 0 (in Ruby-2.1). However, As of Ruby 2.6, it raises +FloatDomainError: NaN+
|
785
|
+
# Again this class now follows Ruby's default ({RangeExtd} Ver.1.0 or later).
|
786
|
+
#
|
729
787
|
# @note When both ends n are the same INFINITY (of the same parity),
|
730
788
|
# +(n..n).size+ used to be 0. As of Ruby 2.6, it is FloatDomainError: NaN.
|
731
789
|
# This routine follows what Ruby produces, depending on Ruby's version it is run on.
|
732
790
|
#
|
733
|
-
# @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).
|
734
792
|
#
|
735
793
|
# @return [Integer] 0 if {RangeExtd::NONE}
|
736
794
|
# @return [Float] Float::INFINITY if either (or both) the end is infinity, regardless of the class of the elements.
|
737
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)
|
738
797
|
def size(*rest)
|
739
798
|
# (1..5).size # => 5
|
740
799
|
# (1...5).size # => 4
|
@@ -747,87 +806,60 @@ class RangeExtd < Range
|
|
747
806
|
# (1.5...4.5).size # => 3
|
748
807
|
# (0...Float::INFINITY).size # => Infinity
|
749
808
|
|
750
|
-
if is_none? # No need of null?(), supposedly!
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
# Note (Infinity..Infinity) => 0 (Range as in Ruby 2.1)
|
759
|
-
# however,
|
760
|
-
elsif (defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY) ||
|
761
|
-
(defined?(self.end.infinity?) && self.end.infinity? || self.end == Infinity::FLOAT_INFINITY)
|
762
|
-
if self.begin == self.end
|
763
|
-
# This varies, depending on Ruby's version! It used to be 0. As of Ruby 2.6, it is FloatDomainError: NaN.
|
764
|
-
return (Float::INFINITY..Float::INFINITY).size
|
765
|
-
# return 0
|
766
|
-
else
|
767
|
-
return Infinity::FLOAT_INFINITY
|
768
|
-
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
|
769
817
|
|
770
|
-
|
818
|
+
rbeg = self.begin
|
819
|
+
rend = self.end
|
771
820
|
|
772
|
-
|
773
|
-
|
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
|
774
825
|
|
775
|
-
|
776
|
-
if defined? (self.begin().succ)
|
777
|
-
Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest)
|
778
|
-
else
|
779
|
-
size_no_exclude = Range.new(self.begin, self.end).send(__method__, *rest) # exclude_end? == true, ie., Range with both ends inclusinve.
|
780
|
-
diff = self.end - self.begin
|
781
|
-
if diff.to_i == diff # Integer difference
|
782
|
-
return size_no_exclude - 1 # At least exclude_begin?==true (so exclude_end? does not matter)
|
783
|
-
else
|
784
|
-
return size_no_exclude
|
785
|
-
end
|
786
|
-
end
|
787
|
-
rescue TypeError
|
788
|
-
# Non-Numeric
|
789
|
-
if defined? self.begin().succ
|
790
|
-
Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest) # => nil in Ruby 2.1
|
791
|
-
else
|
792
|
-
nil # See the line above.
|
793
|
-
# raise TypeError, "can't iterate from "+self.begin.class.name
|
794
|
-
end
|
826
|
+
return @rangepart.send(__method__, *rest) if !exclude_begin?
|
795
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
|
796
838
|
end
|
839
|
+
end
|
797
840
|
|
841
|
+
# Numeric
|
842
|
+
if rbeg.respond_to? :succ
|
843
|
+
Range.new(rbeg.succ, rend, exclude_end?).send(__method__, *rest)
|
798
844
|
else
|
799
|
-
|
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
|
800
852
|
end
|
801
853
|
end # def size
|
802
854
|
|
803
855
|
|
804
856
|
# See {#each}.
|
857
|
+
#
|
805
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.
|
806
859
|
# @return [RangeExtd] self
|
807
860
|
# @return [Enumerator] if block is not given.
|
808
|
-
#
|
809
861
|
def step(*rest, &bloc)
|
810
|
-
|
811
|
-
# (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
|
812
|
-
# (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
|
813
|
-
# 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.
|
814
|
-
|
815
|
-
if @exclude_begin # including RangeExtd::NONE
|
816
|
-
if defined? self.begin.succ
|
817
|
-
ret = Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc)
|
818
|
-
if block_given?
|
819
|
-
self
|
820
|
-
else
|
821
|
-
ret
|
822
|
-
end
|
823
|
-
elsif is_none? # No need of null?(), supposedly!
|
824
|
-
raise TypeError, "can't iterate for NONE range"
|
825
|
-
else
|
826
|
-
raise TypeError, "can't iterate from "+self.begin.class.name
|
827
|
-
end
|
828
|
-
else
|
829
|
-
@rangepart.send(__method__, *rest, &bloc)
|
830
|
-
end
|
862
|
+
_step_each_core(__method__, *rest, &bloc)
|
831
863
|
end
|
832
864
|
|
833
865
|
|
@@ -837,7 +869,15 @@ class RangeExtd < Range
|
|
837
869
|
|
838
870
|
# Private class method to evaluate the arguments.
|
839
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)
|
840
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]
|
841
881
|
def self._get_init_args(*inar, **hsopt)
|
842
882
|
nMin = 1; nMax = 5
|
843
883
|
if inar.size < nMin || nMax < inar.size
|
@@ -875,8 +915,6 @@ class RangeExtd < Range
|
|
875
915
|
end
|
876
916
|
|
877
917
|
beginend = [inar[0].begin, inar[0].end]
|
878
|
-
# arRet = [inar[0].begin, inar[0].end, exclude_end, exclude_begin]
|
879
|
-
# @rangepart = Range.new(inar[0].begin, inar[0].end, exclude_end)
|
880
918
|
|
881
919
|
when :object
|
882
920
|
nMin = 2; nMax = 5
|
@@ -896,8 +934,8 @@ class RangeExtd < Range
|
|
896
934
|
# Originally, defined?(inar[1].=~) seemed enough. But as of Ruby 2.6 (maybe even before),
|
897
935
|
# Numeric has :=~ method as well!
|
898
936
|
if (inar.size > 2 &&
|
899
|
-
inar[1].
|
900
|
-
inar[1].
|
937
|
+
inar[1].respond_to?(:=~) &&
|
938
|
+
inar[1].respond_to?(:to_str))
|
901
939
|
begin
|
902
940
|
cmp = (inar[0] <=> inar[2]).abs
|
903
941
|
rescue
|
@@ -908,16 +946,8 @@ class RangeExtd < Range
|
|
908
946
|
# Hence all the default values are overwritten.
|
909
947
|
beginend = [inar[0], inar[2]]
|
910
948
|
hsFlag[:excl_offset] = 1
|
911
|
-
|
912
|
-
|
913
|
-
else
|
914
|
-
exclude_begin = true
|
915
|
-
end
|
916
|
-
if $2 == @@middle_strings[4]
|
917
|
-
exclude_end = true
|
918
|
-
else
|
919
|
-
exclude_end = false
|
920
|
-
end
|
949
|
+
exclude_begin = ($1 != @@middle_strings[1])
|
950
|
+
exclude_end = ($2 == @@middle_strings[4])
|
921
951
|
else
|
922
952
|
nMin = 2; nMax = 4
|
923
953
|
if inar.size > nMax
|
@@ -939,9 +969,6 @@ class RangeExtd < Range
|
|
939
969
|
exclude_end = (true ^! inar[3+hsFlag[:excl_offset]]) # 4th or 5th argument
|
940
970
|
end
|
941
971
|
|
942
|
-
# arRet = [inar[0], inar[1], exclude_end, exclude_begin]
|
943
|
-
# @rangepart = Range.new(inar[0], inar[1], exclude_end)
|
944
|
-
|
945
972
|
else
|
946
973
|
raise # (for coding safety)
|
947
974
|
end # case hsFlag[:prm1st]
|
@@ -952,18 +979,45 @@ class RangeExtd < Range
|
|
952
979
|
if hsopt.has_key?(:exclude_end)
|
953
980
|
exclude_end = (hsopt[:exclude_end] && true)
|
954
981
|
end
|
955
|
-
# [RangeBeginValue, RangeEndValue, exclude_end?, exclude_begin?]
|
956
|
-
beginend + [exclude_end, exclude_begin]
|
957
982
|
|
983
|
+
# [RangeBeginValue, RangeEndValue, exclude_begin?, exclude_end?]
|
984
|
+
_normalize_infinity_float(beginend) + [exclude_begin, exclude_end]
|
958
985
|
end # def self._get_init_args(*inar)
|
959
|
-
|
960
986
|
private_class_method :_get_init_args # From Ruby 1.8.7 (?)
|
961
987
|
|
988
|
+
# Replaces {RangeExtd::Infinity} with {Float::INFINITY} when appropriate
|
989
|
+
#
|
990
|
+
# @param beginend [Array] 2-compoents(begin, end)
|
991
|
+
# @return [Array] 2-compoents(begin, end)
|
992
|
+
def self._normalize_infinity_float(beginend)
|
993
|
+
is_begin_inf = Infinity.infinity?(beginend[0])
|
994
|
+
return beginend if is_begin_inf ^! Infinity.infinity?(beginend[1])
|
995
|
+
|
996
|
+
# Now, only one of them is a {RangeExtd::Infinity} type object.
|
997
|
+
if is_begin_inf && beginend[1].respond_to?(:divmod)
|
998
|
+
[_normalize_infinity_float_core(beginend[0]), beginend[1]] # "begin" is Infinity
|
999
|
+
elsif beginend[0].respond_to?(:divmod)
|
1000
|
+
[beginend[0], _normalize_infinity_float_core(beginend[1])] # "end" is Infinity
|
1001
|
+
else
|
1002
|
+
beginend
|
1003
|
+
end
|
1004
|
+
end
|
1005
|
+
private_class_method :_normalize_infinity_float # From Ruby 1.8.7 (?)
|
1006
|
+
|
1007
|
+
# @param inf [RangeExtd::Infinity]
|
1008
|
+
# @return [RangeExtd::Infinity, Float] +/-Float::INFINITY if Float
|
1009
|
+
def self._normalize_infinity_float_core(inf)
|
1010
|
+
msg = 'RangeExtd component of the RangeExtd::Infinity object replaced with Float::INFINITY.'
|
1011
|
+
warn msg if $DEBUG || $VERBOSE
|
1012
|
+
(inf.positive? ? 1 : -1) * Float::INFINITY
|
1013
|
+
end
|
1014
|
+
private_class_method :_normalize_infinity_float_core # From Ruby 1.8.7 (?)
|
1015
|
+
|
962
1016
|
|
963
1017
|
# Returns true if the range to be constructed (or given) is valid,
|
964
1018
|
# as a range, accepted in {RangeExtd}.
|
965
1019
|
#
|
966
|
-
# This routine is also
|
1020
|
+
# This routine is also implemented as a method in {Range},
|
967
1021
|
# and accordingly its sub-classes.
|
968
1022
|
#
|
969
1023
|
# This routine is called from {RangeExtd.new}, hence
|
@@ -973,23 +1027,31 @@ class RangeExtd < Range
|
|
973
1027
|
#
|
974
1028
|
# 1. The {#begin} and {#end} elements must be Comparable to each other,
|
975
1029
|
# and the comparison results must be consistent betwen the two.
|
976
|
-
# The
|
977
|
-
# introduced in Ruby 2.6 (see below for the exceptions),
|
978
|
-
#
|
979
|
-
#
|
980
|
-
#
|
981
|
-
#
|
982
|
-
#
|
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},
|
983
1040
|
# that is, ({#begin} <=> {#end}) must be either -1 or 0.
|
984
|
-
#
|
985
|
-
# the exclude status of the both ends must agree
|
986
|
-
#
|
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,
|
987
1045
|
# and vice versa.
|
988
|
-
# For example, (1...1) is NOT valid for
|
1046
|
+
# For example, +(1...1)+ is NOT valid for this reason,
|
989
1047
|
# because any built-in Range object has the exclude status
|
990
|
-
# 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}.
|
991
1053
|
#
|
992
|
-
# Note the last
|
1054
|
+
# Note the second last point may change in the future release.
|
993
1055
|
#
|
994
1056
|
# Note ([2]..[5]) is NOT valid, because Array does not include Comparable
|
995
1057
|
# for some reason, as of Ruby 2.1.1, even though it has the redefined
|
@@ -998,8 +1060,10 @@ class RangeExtd < Range
|
|
998
1060
|
#
|
999
1061
|
# @example
|
1000
1062
|
#
|
1001
|
-
# RangeExtd.valid?(nil..nil) # =>
|
1002
|
-
# 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
|
1003
1067
|
# RangeExtd.valid?(0..0) # => true
|
1004
1068
|
# RangeExtd.valid?(0...0) # => false
|
1005
1069
|
# RangeExtd.valid?(0...) # => true
|
@@ -1013,6 +1077,7 @@ class RangeExtd < Range
|
|
1013
1077
|
# RangeExtd.valid?(3..Float::INFINITY, true) # => true
|
1014
1078
|
# RangeExtd.valid?(RangeExtd::Infinity::NEGATIVE..?d) # => true
|
1015
1079
|
# RangeExtd.valid?(RangeExtd::Infinity::NEGATIVE..?d, true) # => true
|
1080
|
+
# RangeExtd.valid?(RangeExtd::Nowhere::NOWHERE..nil) # => false
|
1016
1081
|
#
|
1017
1082
|
# @note The flag of exclude_begin|end can be given in the arguments in a couple of ways.
|
1018
1083
|
# If there is any duplication, those specified in the optional hash have the highest
|
@@ -1020,14 +1085,14 @@ class RangeExtd < Range
|
|
1020
1085
|
# If not, the values embeded in the {Range} or {RangeExtd} object
|
1021
1086
|
# in the parameter are used. In default, both of them are false.
|
1022
1087
|
#
|
1023
|
-
# @overload
|
1088
|
+
# @overload valid?(range, [exclude_begin=false, [exclude_end=false]])
|
1024
1089
|
# @param range [Object] Instance of Range or its subclasses, including RangeExtd
|
1025
1090
|
# @param exclude_begin [Boolean] If specified, this has the higher priority, or false in default.
|
1026
1091
|
# @param exclude_end [Boolean] If specified, this has the higher priority, or false in default.
|
1027
1092
|
#
|
1028
|
-
# @overload
|
1029
|
-
# @param obj_begin [Object] Any object that is
|
1030
|
-
# @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)
|
1031
1096
|
# @param exclude_begin [Boolean] If specified, this has the lower priority, or false in default.
|
1032
1097
|
# @param exclude_end [Boolean] If specified, this has the lower priority, or false in default.
|
1033
1098
|
#
|
@@ -1035,29 +1100,41 @@ class RangeExtd < Range
|
|
1035
1100
|
(vbeg, vend, exc_beg, exc_end) = _get_init_args(*inar)
|
1036
1101
|
|
1037
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?
|
1038
1109
|
return true
|
1039
1110
|
end
|
1111
|
+
|
1112
|
+
return false if !vbeg.respond_to?(:<=>)
|
1040
1113
|
begin
|
1041
1114
|
t = (vbeg <=> vend)
|
1042
1115
|
begin
|
1043
|
-
if
|
1116
|
+
if vbeg.nil? # Beginless Range introduced in Ruby 2.7
|
1117
|
+
return vend.respond_to?(:<=)
|
1118
|
+
elsif vend.nil?
|
1044
1119
|
begin
|
1045
1120
|
_ = (vbeg..nil) # Endless Range introduced in Ruby 2.6
|
1046
|
-
return vbeg.
|
1121
|
+
return vbeg.respond_to?(:<=)
|
1047
1122
|
rescue ArgumentError
|
1048
1123
|
# Before Ruby 2.6
|
1049
1124
|
return false
|
1050
1125
|
end
|
1051
1126
|
end
|
1052
|
-
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).
|
1053
1129
|
rescue NoMethodError, TypeError
|
1054
1130
|
if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) ||
|
1055
1131
|
(Float === vbeg && defined?(vend.infinity?) && vend.infinity?)
|
1056
|
-
warn self.const_get(:ERR_MSGS)[:infinity_compare] # one of the tests comes here.
|
1132
|
+
warn self.const_get(:ERR_MSGS)[:infinity_compare] if !$VERBOSE.nil? # one of the tests comes here.
|
1057
1133
|
end
|
1058
1134
|
return false # return
|
1059
1135
|
end
|
1060
|
-
rescue
|
1136
|
+
rescue
|
1137
|
+
warn "This should not happen. Contact the code developer (warn01)."
|
1061
1138
|
false # return
|
1062
1139
|
else
|
1063
1140
|
case t
|
@@ -1072,9 +1149,10 @@ class RangeExtd < Range
|
|
1072
1149
|
when 1
|
1073
1150
|
false
|
1074
1151
|
else
|
1152
|
+
warn "This should not happen. Contact the code developer (warn02)."
|
1075
1153
|
if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) ||
|
1076
1154
|
(Float === vbeg && defined?(vend.infinity?) && vend.infinity?)
|
1077
|
-
warn self.const_get(:ERR_MSGS)[:infinity_compare] # not tested so far?
|
1155
|
+
warn self.const_get(:ERR_MSGS)[:infinity_compare] if !$VERBOSE.nil? # not tested so far?
|
1078
1156
|
end
|
1079
1157
|
false # Not Comparable.
|
1080
1158
|
end # case t
|
@@ -1082,7 +1160,6 @@ class RangeExtd < Range
|
|
1082
1160
|
end
|
1083
1161
|
end # def valid?
|
1084
1162
|
|
1085
|
-
|
1086
1163
|
# Set the class variable to be used in {RangeExtd#to_s} and {RangeExtd#inspect}
|
1087
1164
|
# to configure the format of their returned values.
|
1088
1165
|
#
|
@@ -1211,362 +1288,97 @@ class RangeExtd < Range
|
|
1211
1288
|
end # def re_inspect_core_orig(method)
|
1212
1289
|
|
1213
1290
|
|
1214
|
-
# Core routine for {#===}
|
1291
|
+
# Core routine for {#===}
|
1292
|
+
#
|
1215
1293
|
# @param [Object] r to compare.
|
1216
1294
|
# @param [Symbol] method of the method name.
|
1217
|
-
def
|
1218
|
-
if
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
true # (1<...1).eql? (2<...2) # => true (Fixnum <-> Fixnum) Yes!
|
1234
|
-
else
|
1235
|
-
false # (1.0<...1.0).eql? (2<...2) # => false (Float <-> Fixnum) No!
|
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!
|
1236
1311
|
end
|
1237
|
-
else
|
1238
|
-
(self.begin.class.ancestors - self.begin.class.included_modules - [Object, BasicObject]).each do |ec|
|
1239
|
-
if ec === r.begin
|
1240
|
-
return true # (1.0<...1.0) == (2<...2) # (Float<Numeric <-> Fixnum<Numeric) Yes!
|
1241
|
-
end
|
1242
|
-
end
|
1243
|
-
false # (?a...?a) != (2<...2) # (String <-> Numeric) No!
|
1244
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?)
|
1245
1317
|
|
1246
|
-
|
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
|
1247
1322
|
(self.exclude_begin? ^! r.exclude_begin?) &&
|
1248
1323
|
(self.exclude_end? ^! r.exclude_end?) &&
|
1249
|
-
(self.begin.send(method, r.begin))
|
1250
|
-
(self.end.send( method, r.end))
|
1251
|
-
# (self.begin == r.begin) &&
|
1252
|
-
# (self.end == r.end)
|
1324
|
+
(self.begin.send(method, r.begin) && self.end.send(method, r.end) || is_nil_equal)
|
1253
1325
|
else
|
1254
1326
|
# r is Range
|
1255
1327
|
if self.exclude_begin?
|
1256
1328
|
false
|
1257
1329
|
else
|
1258
|
-
@rangepart.send(method, r) # Comparison as two Range-s.
|
1330
|
+
is_nil_equal || @rangepart.send(method, r) # Comparison as two Range-s.
|
1259
1331
|
end
|
1260
1332
|
end
|
1261
|
-
end # def
|
1333
|
+
end # def _re_equal_core(r, method)
|
1334
|
+
private :_re_equal_core
|
1262
1335
|
|
1263
1336
|
# Core routine for {#min}, {#max}, {#minmax} etc.
|
1264
1337
|
# @param method [Symbol] of the method name.
|
1265
1338
|
# @param rest [Object]
|
1266
|
-
def
|
1339
|
+
def _re_min_max_core(method, *rest, &bloc)
|
1267
1340
|
# (1...3.5).max # => TypeError: cannot exclude non Integer end value
|
1268
|
-
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)...
|
1269
1343
|
raise TypeError, "no meaningful range."
|
1270
|
-
elsif @exclude_begin
|
1271
|
-
if defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY
|
1272
|
-
raise TypeError, "can't exclude "+self.begin.to_s
|
1273
|
-
elsif ! defined? self.begin.succ
|
1274
|
-
raise TypeError, "can't iterate from "+self.begin.class.name
|
1275
|
-
else
|
1276
|
-
Range.new(self.begin.succ, self.end, exclude_end?).send(method, *rest, &bloc)
|
1277
|
-
end
|
1278
|
-
else
|
1279
|
-
@rangepart.send(method, *rest)
|
1280
1344
|
end
|
1281
|
-
end # def re_min_max_core(method, *rest, &bloc)
|
1282
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)
|
1283
1362
|
# No range.
|
1284
1363
|
# In Ruby1.8, this causes ArgumentError: bad value for range (because (nil..nil) is unaccepted).
|
1285
|
-
NONE = RangeExtd.new(nil, nil, true, true, :Constant)
|
1286
|
-
|
1364
|
+
#NONE = RangeExtd.new(nil, nil, true, true, :Constant)
|
1365
|
+
NONE = RangeExtd.new(RangeExtd::Nowhere::NOWHERE, RangeExtd::Nowhere::NOWHERE, true, true, :Constant)
|
1287
1366
|
|
1367
|
+
self.remove_const :ALL if defined? self::ALL # tricky manoeuvre for documentation purposes... (see infinity.rb for the explanatory document)
|
1288
1368
|
# Range covers everything.
|
1289
1369
|
ALL = RangeExtd.new(Infinity::NEGATIVE, Infinity::POSITIVE, false, false, :Constant)
|
1290
|
-
#ALL = RangeExtd.new(Infinity::NEGATIVE..Infinity::POSITIVE, false, false, :Constant)
|
1291
1370
|
|
1292
1371
|
end # class RangeExtd < Range
|
1293
1372
|
|
1294
|
-
|
1295
|
-
|
1296
|
-
#
|
1297
|
-
#== Summary
|
1298
|
-
#
|
1299
|
-
# Modifies {#==}, {#eql?} and add methods of
|
1300
|
-
# {#valid?}, {#empty?}, {#null?}, {#is_none?} and {#is_all?}.
|
1301
|
-
#
|
1302
|
-
class Range
|
1303
|
-
|
1304
|
-
alias_method :equal_prerangeextd?, :== if ! self.method_defined?(:equal_prerangeextd?) # No overwriting.
|
1305
|
-
|
1306
|
-
# It is extended to handle {RangeExtd} objects.
|
1307
|
-
# For each element, that is, {#begin} and {#end},
|
1308
|
-
# this uses their method of ==(). See {#eql?}.
|
1309
|
-
#
|
1310
|
-
# As long as the comparison is limited within {Range} objects,
|
1311
|
-
# the returned value of this method has unchanged.
|
1312
|
-
#
|
1313
|
-
# A note of caution is, some ranges which the built-in Range accepts,
|
1314
|
-
# are now regarded as NOT valid, such as, (1...1) and (nil..nil)
|
1315
|
-
# (the latter was not permitted in Ruby 1.8), though you can still
|
1316
|
-
# use them;
|
1317
|
-
# (1...1).valid? # => false
|
1318
|
-
# On the other hand, {RangeExtd} class does not accept or create
|
1319
|
-
# any invalid range; for any {RangeExtd} object, RangeExtd#valid?
|
1320
|
-
# returns true. For example, there is no {RangeExtd} object
|
1321
|
-
# that is expressed as (1...1) (See {#valid?} for detail).
|
1322
|
-
#
|
1323
|
-
# For that reason, when those non-valid Range objects are compared
|
1324
|
-
# with a {RangeExtd} object, the returned value may not be what
|
1325
|
-
# you would expect. For example,
|
1326
|
-
# (1...1) == RangeExtd(1, 1, true, true) # => false.
|
1327
|
-
# The former is an invalid range, while the latter is
|
1328
|
-
# a rigidly-defined empty range.
|
1329
|
-
#
|
1330
|
-
# Consult {#valid?} and {RangeExtd#==} for more detail.
|
1331
|
-
def ==(r)
|
1332
|
-
equal_core(r, :==, :equal_prerangeextd?)
|
1333
|
-
end
|
1334
|
-
|
1335
|
-
|
1336
|
-
alias :eql_prerangeextd? :eql? if ! self.method_defined?(:eql_prerangeextd?) # No overwriting.
|
1337
|
-
|
1338
|
-
# Same as {#==}, but the comparison is made with eql?() method.
|
1339
|
-
def eql?(r)
|
1340
|
-
equal_core(r, :eql?, :eql_prerangeextd?)
|
1341
|
-
end
|
1342
|
-
|
1343
|
-
|
1344
|
-
# Returns true if self is valid as a comparable range.
|
1345
|
-
#
|
1346
|
-
# See {RangeExtd.valid?} for the definition of what is valid
|
1347
|
-
# and more examples.
|
1348
|
-
#
|
1349
|
-
# See {#empty?} and {#null?}, too.
|
1350
|
-
#
|
1351
|
-
# @example
|
1352
|
-
# (nil..nil).valid? # => false
|
1353
|
-
# (0..0).valid? # => true
|
1354
|
-
# (0...0).valid? # => false
|
1355
|
-
# (2..-1).valid? # => false
|
1356
|
-
# RangeExtd(0...0, true) # => true
|
1357
|
-
# (3..Float::INFINITY).valid? # => true
|
1358
|
-
# RangeExtd::NONE.valid? # => true
|
1359
|
-
# RangeExtd::ALL.valid? # => true
|
1360
|
-
#
|
1361
|
-
# @note By definition, all the {RangeExtd} instances are valid,
|
1362
|
-
# because {RangeExtd.new} checks the validity.
|
1363
|
-
def valid?
|
1364
|
-
RangeExtd.valid?(self)
|
1365
|
-
end # def valid?
|
1366
|
-
|
1367
|
-
|
1368
|
-
# Returns true if self is empty.
|
1369
|
-
# Returns nil if self is not valid (nb., any RangeExtd instance is valid.)
|
1370
|
-
# Otherwise false.
|
1371
|
-
#
|
1372
|
-
# The definition of what is empty is as follow.
|
1373
|
-
#
|
1374
|
-
# 1. the range must be valid: {#valid?} => true
|
1375
|
-
# 2. if the range id discrete, that is, {#begin} has
|
1376
|
-
# [#succ] method, there must be no member within the range:
|
1377
|
-
# {#to_a}.empty? => true
|
1378
|
-
# 3. if the range is continuous, that is, {#begin} does not have
|
1379
|
-
# [#succ] method, {#begin} and {#end} must be equal
|
1380
|
-
# (({#begin} <=> {#end}) => 0) and both the boundaries must
|
1381
|
-
# be excluded: ({#exclude_begin?} && {#exclude_end?}) => true.
|
1382
|
-
# Note that ranges with equal {#begin} and {#end} with
|
1383
|
-
# inconsistent two exclude status are not valid, and the built-in
|
1384
|
-
# Range always has the {#begin}-exclude status of false.
|
1385
|
-
#
|
1386
|
-
# In these conditions, none of Range instance would return true in {#empty?}.
|
1387
|
-
#
|
1388
|
-
# @example
|
1389
|
-
# (nil..nil).empty? # => nil
|
1390
|
-
# (1...1).empty? # => nil
|
1391
|
-
# (1..1).empty? # => false
|
1392
|
-
# RangeExtd(1...1, true).empty? # => true
|
1393
|
-
# RangeExtd(1...2, true).empty? # => true
|
1394
|
-
# RangeExtd(1.0...2, true).empty? # => false
|
1395
|
-
# RangeExtd(?a...?b, true).empty? # => true
|
1396
|
-
# RangeExtd::NONE.empty? # => true
|
1397
|
-
#
|
1398
|
-
# @note to check whether it is either empty or invalid, use {#null?}.
|
1399
|
-
# See {#valid?} and {RangeExtd.valid?}, too.
|
1400
|
-
#
|
1401
|
-
# @return [Boolean, nil]
|
1402
|
-
def empty?
|
1403
|
-
# This is basically for the sake of sub-classes, as any built-in Range instance
|
1404
|
-
# always returns either nil or false.
|
1405
|
-
|
1406
|
-
if !valid?
|
1407
|
-
return nil
|
1408
|
-
elsif defined?(self.is_none?) && self.is_none?
|
1409
|
-
return true
|
1410
|
-
end
|
1411
|
-
|
1412
|
-
t = (self.begin() <=> self.end())
|
1413
|
-
case t
|
1414
|
-
when -1
|
1415
|
-
if (defined?(self.exclude_begin?)) &&
|
1416
|
-
exclude_begin? &&
|
1417
|
-
exclude_end? &&
|
1418
|
-
defined?(self.begin().succ) &&
|
1419
|
-
(self.begin().succ == self.end())
|
1420
|
-
true # e.g., ("a"<..."b")
|
1421
|
-
else
|
1422
|
-
false
|
1423
|
-
end
|
1424
|
-
when 0
|
1425
|
-
if defined?(self.boundary) && self.boundary.nil? # for RangeOpen
|
1426
|
-
# RangeExtd::NONE or RangeExtd::All
|
1427
|
-
if self.exclude_end?
|
1428
|
-
true # RangeOpen::NONE
|
1429
|
-
else
|
1430
|
-
false # RangeOpen::ALL
|
1431
|
-
end
|
1432
|
-
else
|
1433
|
-
if defined?(self.exclude_begin?)
|
1434
|
-
t2 = self.exclude_begin?
|
1435
|
-
else
|
1436
|
-
t2 = false # == return false
|
1437
|
-
end
|
1438
|
-
(t2 && exclude_end?)
|
1439
|
-
end
|
1440
|
-
when 1
|
1441
|
-
nil # redundant, as it should not be valid in the first place.
|
1442
|
-
else
|
1443
|
-
nil # redundant, as it should not be valid in the first place.
|
1444
|
-
end
|
1445
|
-
end # def empty?
|
1446
|
-
|
1447
|
-
|
1448
|
-
# Returns true if it is either empty or invalid. false otherwise.
|
1449
|
-
# See {#empty?} and {#valid?}.
|
1450
|
-
def null?
|
1451
|
-
(! valid?) || empty?
|
1452
|
-
end
|
1453
|
-
|
1454
|
-
# @return [FalseClass]
|
1455
|
-
def is_none?
|
1456
|
-
false
|
1457
|
-
end
|
1458
|
-
|
1459
|
-
# @return [FalseClass]
|
1460
|
-
def is_all?
|
1461
|
-
false
|
1462
|
-
end
|
1463
|
-
|
1464
|
-
|
1465
|
-
# Return true if self and the other are equivalent; if [#to_a] is defined, it is similar to
|
1466
|
-
# (self.to_a == other.to_a)
|
1467
|
-
# (though the ends are checked more rigorously), and if not, equivalent to
|
1468
|
-
# (self == other)
|
1469
|
-
#
|
1470
|
-
# @example
|
1471
|
-
# (3...7).equiv?(3..6) # => true
|
1472
|
-
# (3...7).equiv?(3..6.0) # => false
|
1473
|
-
# (3...7).equiv?(3.0..6.0) # => false
|
1474
|
-
# (3...7).equiv?(3..6.5) # => false
|
1475
|
-
# (3...7).equiv?(3.0...7.0) # => true
|
1476
|
-
# (3...7.0).equiv?(3..6) # => true
|
1477
|
-
# (3...7.0).equiv?(3.0..6) # => false
|
1478
|
-
#
|
1479
|
-
# @param other [Range, RangeExtd]
|
1480
|
-
def equiv?(other)
|
1481
|
-
t_or_f = (defined?(self.begin.succ) && defined?(other.begin.succ) && defined?(other.end) && defined?(other.exclude_end?))
|
1482
|
-
if ! t_or_f
|
1483
|
-
return(self == other) # succ() for begin is not defined.
|
1484
|
-
else
|
1485
|
-
# Checking the begins.
|
1486
|
-
if defined?(other.exclude_begin?) && other.exclude_begin? # The other is RangeExtd with exclude_begin?==true.
|
1487
|
-
if self.begin != other.begin.succ
|
1488
|
-
return false
|
1489
|
-
else
|
1490
|
-
# Pass
|
1491
|
-
end
|
1492
|
-
elsif (self.begin != other.begin)
|
1493
|
-
return false
|
1494
|
-
end
|
1495
|
-
|
1496
|
-
# Now, the begins agreed. Checking the ends.
|
1497
|
-
if (self.end == other.end)
|
1498
|
-
if (exclude_end? ^! other.exclude_end?)
|
1499
|
-
return true
|
1500
|
-
else
|
1501
|
-
return false
|
1502
|
-
end
|
1503
|
-
else # if (self.end == other.end)
|
1504
|
-
if (exclude_end? ^! other.exclude_end?)
|
1505
|
-
return false
|
1506
|
-
elsif ( exclude_end? && defined?(other.end.succ) && (self.end == other.end.succ)) ||
|
1507
|
-
(other.exclude_end? && defined?( self.end.succ) && (self.end.succ == other.end))
|
1508
|
-
return true
|
1509
|
-
else
|
1510
|
-
return false
|
1511
|
-
end
|
1512
|
-
end # if (self.end == other.end)
|
1513
|
-
end # if ! t_or_f
|
1514
|
-
|
1515
|
-
end # def equiv?(other)
|
1516
|
-
|
1517
|
-
|
1518
|
-
############## pravate methods of Range ##############
|
1519
|
-
|
1520
|
-
private
|
1521
|
-
|
1522
|
-
# True if obj is Comparable.
|
1523
|
-
def is_comparable?(obj)
|
1524
|
-
if defined?(obj.<=) # Comparable?
|
1525
|
-
true
|
1526
|
-
else
|
1527
|
-
false
|
1528
|
-
end
|
1529
|
-
end
|
1530
|
-
|
1531
|
-
# @param r [Object] to compare.
|
1532
|
-
# @param method [Symbol] of the method name.
|
1533
|
-
# @param method_pre [Symbol] of the backed-up original method name.
|
1534
|
-
def equal_core(r, method, method_pre)
|
1535
|
-
if (! defined? r.exclude_end?) || (! defined? r.is_none?) || (! defined? r.empty?)
|
1536
|
-
false # Not Range family.
|
1537
|
-
# elsif empty? && defined?(r.is_none?) && r.is_none? # r is RangeExtd::NONE
|
1538
|
-
# true
|
1539
|
-
# elsif empty? && r.empty?
|
1540
|
-
# # None of built-in Range class object can be #empty?==true - This is for sub-class.
|
1541
|
-
#
|
1542
|
-
elsif defined? r.exclude_begin?
|
1543
|
-
# Either RangeExtd or RangeOpen object.
|
1544
|
-
if r.exclude_begin?
|
1545
|
-
false # self(Range) has always the inclusive begin.
|
1546
|
-
else
|
1547
|
-
# It could do with a single line,
|
1548
|
-
# self.begin.send(method_pre, r)
|
1549
|
-
# if this was for RangeExtd===r, but not for RangeOpen.
|
1550
|
-
if (self.exclude_end? ^ r.exclude_end?)
|
1551
|
-
false
|
1552
|
-
elsif (self.begin.send(method, r.begin) && self.end.send(method, r.end))
|
1553
|
-
true
|
1554
|
-
else
|
1555
|
-
false
|
1556
|
-
end
|
1557
|
-
end
|
1558
|
-
else
|
1559
|
-
self.send(method_pre, r) # r is Range.
|
1560
|
-
end
|
1561
|
-
end # def equal_core(r, method, method_pre)
|
1562
|
-
|
1563
|
-
end # class Range
|
1564
|
-
|
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?)
|
1565
1375
|
|
1566
1376
|
# Constant-form of {#RangeExtd}.
|
1567
|
-
# #RangeExtd(*) is equivalent to {#RangeExtd.new}.
|
1568
1377
|
#
|
1569
|
-
|
1570
|
-
|
1378
|
+
# +RangeExtd()+ is equivalent to {#RangeExtd.new}().
|
1379
|
+
#
|
1380
|
+
# @return [RangeExtd]
|
1381
|
+
def RangeExtd(*rest, **hs, &b)
|
1382
|
+
RangeExtd.new(*rest, **hs, &b)
|
1571
1383
|
end
|
1572
1384
|
|