rangeary 1.0.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.
data/README.ja.rdoc CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  = Rangeary - Multiple Range(Extd) class
3
3
 
4
- This package defines Rangeary class, which contains any multiple
4
+ This package defines Rangeary class, which contains multiple
5
5
  1-dimensional ranges ({RangeExtd}[http://rubygems.org/gems/range_extd] objects). For example, a multiple
6
6
  range of
7
7
 
@@ -9,25 +9,30 @@ range of
9
9
 
10
10
  for <tt>x</tt> can be defined in this class.
11
11
 
12
- The element objects for {Rangeary} can be anthing that can form
13
- RangeExtd objects; not only Numeric (or Real) but anything
14
- Comparable. {Rangeary} accepts the built-in Range-class objects for
15
- the elements in initialisation, too.
12
+ The element objects for +Rangeary+ can be anything that can form
13
+ RangeExtd objects, not only Numeric (or Real) but anything
14
+ Comparable. +Rangeary+ accepts the built-in Range-class objects for
15
+ the elements in initialization, too, providing it is a valid
16
+ (<tt>Range#valid? == true</tt>) ones for the
17
+ {RangeExtd}[https://rubygems.org/gems/range_extd] class (n.b., most Range
18
+ objects are valid, but +(true..true)+ is not, for example, which would
19
+ not make sense to constitute multiple ranges).
16
20
 
17
21
  All the four standard logical operations, that is, negation
18
22
  (<tt>~</tt>), conjunction (<tt>&</tt> or <tt>*</tt>), disjunction
19
23
  (<tt>|</tt> or <tt>+</tt>) and exclusive disjunction (<tt>^</tt> or
20
24
  <tt>xor</tt>) are defined, as well as subtraction (<tt>-</tt>).
21
25
 
22
- {Rangeary} objects are immutable - once it is created, you can not
26
+ {Rangeary} objects are immutable - once it is created, you cannot
23
27
  alter the contents.
24
28
 
25
- Practically, {Rangeary} is a sub-class of Array, works as an array of
26
- RangeExtd, and inherits most of the methods, hence you can apply most
27
- operations to {Rangeary} that Array accpets, within the restriction of
28
- immutability of {Rangeary}. In addition, {Rangeary} offers several
29
- methods that directly work on its element as a range. In the above
30
- example, <tt>#cover?(1.0)</tt> would return true, whereas
29
+ {Rangeary} is implemented as a sub-class of Array, works as an array of
30
+ RangeExtd, and inherits most of the methods. Thus, you can apply most
31
+ operations to {Rangeary} that Array accepts, within the restriction of
32
+ immutability of {Rangeary}; for example, +Array#push+ is disabled for
33
+ {Rangeary}. In addition, {Rangeary} offers several
34
+ methods that directly work on its element as a range. In the
35
+ example above, <tt>#cover?(1.0)</tt> (like +Range#cover?+) returns true, whereas
31
36
  <tt>#cover?(9.0)</tt>, false.
32
37
 
33
38
 
@@ -35,7 +40,21 @@ With this class, logical operations of 1-dimensional range objects are
35
40
  now possible and easy.
36
41
  I hope you find it to be useful.
37
42
 
38
- === News: Endless Range supported
43
+ === News: Library locations and support for Beginless Range
44
+
45
+ **IMPORTANT**: The path for the library is moved up by one
46
+ directory in {Rangeary} Ver.2 from Ver.1 (same as the change in +RangeExtd+) in order that the
47
+ location follows the Ruby Gems convention. In short, the standard way
48
+ to require is +require "rangeary"+, the path of which used to be "rangeary/rangeary"
49
+
50
+ Version of {Rangeary} is now 2.0.
51
+
52
+ Ruby 2.7 and later supports {Beginless range}[https://rubyreferences.github.io/rubychanges/2.7.html#beginless-range].
53
+
54
+ +Rangeary+ (Ver.2) also supports it now, requiring
55
+ {RangeExtd}[https://rubygems.org/gems/range_extd] Ver.2 (or later).
56
+
57
+ ==== News: Endless Range supported
39
58
 
40
59
  Now, as of 2019 October, this fully supports {Endless Range}[https://rubyreferences.github.io/rubychanges/2.6.html#endless-range-1]
41
60
  introduced in Ruby 2.6. It is released as Version 1.* finally!
@@ -43,39 +62,40 @@ introduced in Ruby 2.6. It is released as Version 1.* finally!
43
62
 
44
63
  == Install
45
64
 
46
- First, you need {RangeExtd}[https://rubygems.org/gems/range_extd] class library (basically, two files of pure ruby code), which is in gem:
65
+ First, you need {RangeExtd}[https://rubygems.org/gems/range_extd] class library Ver.2 or later, which is in gem:
47
66
 
48
67
  gem install range_extd
49
68
 
50
69
  Or, get it from
51
- https://rubygems.org/gems/range_extd
52
- if you have not yet installed it.
70
+ {https://rubygems.org/gems/range_extd}
71
+
72
+ If you choose manual installation for some reason,
73
+ follow the INSTALL section in the manual of
74
+ {RangeExtd}[https://rubygems.org/gems/range_extd]
53
75
 
54
- Then, install this library.
76
+ Then, install this library with
55
77
 
56
78
  gem install rangeary
57
79
 
58
- A file
80
+ Files of
59
81
 
60
- rangeary/rangeary.rb
82
+ rangeary.rb
83
+ rangeary/util.rb
84
+ rangeary/util/hash_inf.rb
61
85
 
62
86
  should be installed in one of your <tt>$LOAD_PATH</tt>.
63
87
  Alternatively, get it from
64
- http://rubygems.org/gems/rangeary
88
+ {http://rubygems.org/gems/rangeary}
65
89
 
66
90
  Then all you need to do is
67
91
 
68
- require 'rangeary/rangeary'
69
-
70
- or, possibly as follows, if you manually install it
71
-
72
92
  require 'rangeary'
73
93
 
74
- in your Ruby script (or irb). The library files like
75
- <tt>range_extd/range_extd.rb</tt> for RangeExtd are automatically
76
- loaded.
94
+ in your Ruby script (or irb)
95
+ (Note the path used to be, in Rangeary Ver.1 or earlier), "rangeary/rangeary"),
96
+ in which all the files are automatically loaded.
77
97
 
78
- {Rangeary} itself may work in Ruby 1.8, but RangeExtd works in only Ruby 2.1 or later.
98
+ {Rangeary} Ver.2, along with RangeExtd, works in only Ruby 2.7 or later.
79
99
 
80
100
  Have fun!
81
101
 
@@ -95,9 +115,9 @@ Here are some simple examples.
95
115
  Basically, <tt>Rangeary()</tt> or <tt>Rangeary.new()</tt> accepts
96
116
  an arbitrary number of either Range, RangeExtd, {Rangeary}, or a
97
117
  combination of them. Note Range objects that return false in
98
- +Range#valid?+ will raise an exception.
118
+ +Range#valid?+ raise an exception.
99
119
 
100
- For more detail and examples, see {Rangeary.new}.
120
+ For more detail and examples, see {Rangeary.initialize}.
101
121
 
102
122
 
103
123
  === Practical application examples
@@ -133,39 +153,54 @@ it does not violate the immutability of {Rangeary} objects, such as
133
153
  == Description
134
154
 
135
155
  Once the file <tt>rangeary.rb</tt> is required, the class +Rangeary+ is defined, in
136
- addtion to the two defined in the RangeExtd library
137
- (<tt>RangeExtd</tt> and <tt>RangeExtd::Infinity</tt>).
156
+ addition to those defined in the {RangeExtd}[https://rubygems.org/gems/range_extd] library
157
+ (<tt>RangeExtd</tt>, <tt>RangeExtd::Infinity</tt>, and <tt>RangeExtd::Nowhere</tt>).
158
+ <tt>RangeExtd</tt> adds some methods to the built-in <tt>Range</tt> class.
138
159
 
139
160
  === Rangeary Class
140
161
 
141
162
  {Rangeary} objects are immutable, the same as Range and RangeExtd.
142
163
  Hence once an instance is created, it would not change.
143
164
 
144
- How to create an instance is explained above (in the Examples
165
+ The ways to create an instance are explained above (in the Example
145
166
  sections). Any attempt to try to create an instance with one of
146
- elements being not "valid" as a range, that is, +Range#valid?+ returns
167
+ elements being not "valid" as a range, that is, of which +Range#valid?+ returns
147
168
  false, raises an exception (<tt>ArgumentError</tt>), and fails.
148
169
 
149
- Note +RangeExtd+ (or +Range+) objects used to initialise {Rangeary}
150
- instances can contain negative and/or positive infinity objects.
151
- Since Ruby 2.6 the latter is called {Endless Range}[https://rubyreferences.github.io/rubychanges/2.6.html#endless-range-1]
152
- and is supported as built-in Range. The former is defined only in
153
- {RangeExtd}[https://rubygems.org/gems/range_extd] class.
154
- In default they are of the type of either +Float::INFINITY+ or
155
- either of the two instances of
170
+ +RangeExtd+ (or +Range+) objects given as arguments to initialize +Rangeary+
171
+ instances may contain negative and/or positive infinity objects.
172
+ Since Ruby 2.7 and 2.6,
173
+ {Beginless range}[https://rubyreferences.github.io/rubychanges/2.7.html#beginless-range]
174
+ and {Endless Range}[https://rubyreferences.github.io/rubychanges/2.6.html#endless-range-1]
175
+ are respectively introduced. They correspond to more conventional
156
176
  {RangeExtd::Infinity}[https://www.rubydoc.info/gems/range_extd/RangeExtd/Infinity]
157
- class, +POSITIVE+ or +NEGATIVE+. See {Rangeary.new} for detail about
158
- how to initialise.
159
-
160
- When multiple ranges are given in initialising, they are sorted in
177
+ objects, which Rangeary has supported since its first release.
178
+
179
+ The built-in borderless Ranges and +RangeExtd::Infinity+ objects are
180
+ similar but are conceptually slightly different; the difference is
181
+ similar to that between borderless Ranges and +Float::INFINITY+.
182
+ See the reference document of {RangeExtd}[http://rubygems.org/gems/range_extd]
183
+ for detail.
184
+
185
+ In default, when Ranges that contain infinities, be it built-in borderless Ranges
186
+ or +Float::INFINITY+ or +RangeExtd::Infinity+ are given to {Rangeary},
187
+ +Rangeary+ use them. Alternatively, a user can specify their own
188
+ infinity objects; for example, you may provide +"a"+ as the negative
189
+ infinity for +Rangeary+ with String Ranges. If nothing is provided
190
+ and yet if +Rangeary+ requires one(s), which may happen, for example,
191
+ in an operation of negation, +Rangeary+ uses +Float::INFINITY+ for
192
+ Numeric (Real numbers) and +nil+ for anything else in default.
193
+ See {Rangeary} and {Rangeary.initialize} for detail.
194
+
195
+ When multiple ranges are given in initializing, they are internally sorted in
161
196
  storing, and if there are any overlaps among any of the elements, they
162
- are treated as disjunction (that is, simple summation). That means
197
+ are treated as disjunction (that is, simple summation). This means that
163
198
  the objects a {Rangeary} instance holds internally can be different from
164
- the objects given in initialisation, namely, their {#object_id} may be
199
+ the objects given in initialization, namely, their <tt>#object_id</tt> may be
165
200
  different. In particular, if built-in Range objects are given,
166
201
  they are always converted into RangeExtd objects internally.
167
202
 
168
- If any of the given range in the intialisation is empty, that is,
203
+ If any of the given range in initialization is "empty", that is,
169
204
  +Range#empty?+ returns true, they are ignored, unless all of the
170
205
  ranges given are empty ranges, in which case the "smallest" one will be
171
206
  preserved.
@@ -174,112 +209,289 @@ If the result of the operation is empty, only the element of the
174
209
  resultant {Rangeary} is +RangeExtd::NONE+, and hence
175
210
  {Rangeary#empty_element?} will return true.
176
211
 
177
- For any Rangeary objects, {Rangeary#to_a}.size is always
178
- positive and not zero for that reason, or {Rangeary#empty?} returns
179
- always false, as {Rangeary#empty?} is a method inherited from Array.
212
+ For any Rangeary objects, <tt>(Rangeary#to_a).size</tt> returns always
213
+ positive and not zero for this reason, or +Rangeary#empty?+ returns
214
+ always false, as +Rangeary#empty?+ is a method inherited from Array.
180
215
  Use {Rangeary#empty_element?} instead to check whether the instance is
181
- *practically* empty or not as a range.
216
+ *practically* empty, or in other words, an empty range.
182
217
 
183
218
  Rangeary(RangeExtd::NONE).empty? # => false
184
219
  Rangeary(RangeExtd::NONE).empty_element? # => true
185
220
 
186
221
  As mentioned, all the methods of Array but a few are inherited
187
- to this {Rangeary} class, and they work based on each element
188
- RangeExtd. Four methods work differently: {Rangeary#+} and
222
+ to this {Rangeary} class, and they in principle work as if +Rangeary+ is an Array of
223
+ +RangeExtd+ (which is indeed the case!). Four methods work differently: {Rangeary#+} and
189
224
  {Rangeary#*} are the alias to {Rangeary#disjunction} and
190
- {Rangeary#conjunction}, repectively. {Rangeary#===} performs
225
+ {Rangeary#conjunction}, respectively. {Rangeary#===} performs
191
226
  +Range#===+ for all the Rangeary element ranges and return true if any
192
227
  of them returns true. Therefore, {Rangeary#===}(RangeExtd(**))
193
228
  returns always false. Also, +#length+
194
229
  and +#reverse+ are undefined. Finally, {Array#==} is modified (see below).
195
230
 
196
- All the other methods operating on the element of ranges, rather than
231
+ {Rangeary#==} and +Rangeary#eql?+ work in the same way as of Array. Therefore,
232
+ [2..4, 6..8] == Rangeary(2..4, 6..8) # => true
233
+
234
+ However, you should note the following:
235
+
236
+ [2..4, 6..8] == Rangeary(6..8, 2..4) # => true
237
+ [6..8, 2..4] == Rangeary(6..8, 2..4) # => false
238
+
239
+ because Rangeary always sort its contents at the initialization.
240
+ In short, it is really not recommended to compare Array and Rangeary
241
+ directly. Instead, you should compare two {Rangeary} objects.
242
+
243
+ All the other methods operating on the element ranges, rather than
197
244
  on the ranges themselves, have a suffix of <tt>_element</tt>, if there
198
245
  is the same method name in the built-in Array. For example,
199
- {Rangeary#size} returns the number of RangeExtd objects it holds,
246
+ +Rangeary#size+ returns the number of +RangeExtd+ objects it holds as an Array,
200
247
  and {Rangeary#size_element} returns the total +Range#size+ of all
201
248
  the RangeExtd objects it holds.
202
249
 
203
250
  Rangeary(1..3, 5..8).size # => 2
204
251
  Rangeary(1..3, 5..8).size_element # => 7
205
252
 
206
- Or, {Rangeary#flatten_element} returns the concatnated
207
- single array of {Rangeary#to_a} for all the discrete range elements
253
+ Or, {Rangeary#flatten_element} returns the concatenated
254
+ single array of +Rangeary#to_a+ for all the discrete range elements
208
255
  (the element ranges have to be discrete like Integer).
209
256
 
257
+ To flatten an Array containing both Arrays and Rangeary while you do
258
+ not want to flatten each Rangeary, use {Rangeary.flatten_no_rangeary}.
259
+
210
260
  The complete reference of the class and methods is available at
211
261
  {Rubygems website}[http://rubygems.org/gems/rangeary], or you can compile the
212
262
  reference with +yard+ from the source package (+make doc+ at the
213
263
  package root directory would do).
214
264
 
215
- === Array#==
265
+ === Infinities
216
266
 
217
- Equal method of +Rangeary#==+ should work differently from Array.
218
- First, it behaves differently for *empty* objects, as Rangeary objects are
219
- never empty in the sense of Array. Second, handling of
220
- {Endless Range}[https://rubyreferences.github.io/rubychanges/2.6.html#endless-range-1]
221
- introduced in Ruby 2.6 is conceptionally not straightforward.
222
- For example, in Ruby 2.6,
267
+ The infinities are vital in the logical operation of Rangeary.
268
+ Without it, negation could not be defined, and other logical
269
+ operations are also closely related to it; for example, *subtraction*
270
+ is basically a combination of *negation* and *conjunction*.
271
+
272
+ To determine what the positive and negative infinities for the given
273
+ elements is not a trivial task. In default, +nil+ is used except for
274
+ +Numerics+ (Integer, Rational, Float etc), for which +Float::INFINITY+
275
+ is used. Note that the default used to be
276
+ <tt>RangeExtd::Infinity::POSITIVE</tt> and <tt>RangeExtd::Infinity::NEGATIVE</tt>
277
+ defined in {RangeExtd}[http://rubygems.org/gems/range_extd]
278
+ up to Rangeary Ver.1, where both beginless and endless Ranges were not
279
+ been introduced or supported. Rangeary Ver.2 changes the specification
280
+ to be in line with the latest Ruby Range.
281
+
282
+ Alternatively, a user can specify their own infinities in
283
+ initialization of {Rangeary} with options of +positive:+ and
284
+ +negative:+. The boundaries at the opposite polarities usually should
285
+ match unless they are comparable or either of them is +nil+.
286
+
287
+ Here are examples of how infinities work with {Rangeary}. In the first
288
+ example, infinities are implicitly contained in the specified Range.
289
+ Then, the infinities are internally preserved throughout operations.
290
+ Note that the first set of 2 operations and the second set of a single operation
291
+ means the same.
292
+
293
+ r1 = Rangeary(nil..Float::INFINITY).conjunction( RangeExtd::NONE )
294
+ # => Rangeary(RangeExtd::NONE)
295
+ r2 = r1.negation
296
+ # => Rangeary(nil..Float::INFINITY)
297
+
298
+ ~(Rangeary(nil..Float::INFINITY) * RangeExtd::NONE)
299
+ # => Rangeary(nil..Float::INFINITY)
300
+
301
+ In the second example below, a negative infinity of "+d+" is explicitly specified
302
+ for a Range of single alphabet String.
303
+
304
+ Rangeary("f".."k", negative: "d").negation
305
+ # => Rangeary("d"..."f", "k"<..nil)
306
+
307
+ where +"k"<..nil+ means a begin-exclude Range or +RangeExtd("k"..nil, true)+.
308
+
309
+ A note of caution is that once an infinity is defined for a Rangeary object, any other
310
+ Rangeary objects with which operations are performed should be in line with the
311
+ same infinities. If you specify your own infinities, it is advised to
312
+ do so at the beginning. And once the infinities have been manually
313
+ set, it is advised not to modify them (although this library should
314
+ handle such changes appropriately -- see the method document for detail)
315
+ because unexpected errors may occur. Here is a set of examples.
316
+
317
+ r3 = Rangeary("f".."k", negative: "d")
318
+ r4 = ~r3
319
+ # => Rangeary("d"..."f", "k"<..nil)
320
+ _ = Rangeary(r4, positive: "t") # raises ArgumentError(!):
321
+ # because "t" is smaller than end of Endless Range
322
+ r6 = Rangeary(r3, positive: "t") # OK: because end of r3 is only "k"
323
+ r7 = ~r6
324
+ # => Rangeary("a"..."d", "f".."k") # differs from r4 in the second Range
325
+
326
+ In the example above, +r4+, which is the negation of +r3+ is
327
+ "Endless", i.e., the last Range in +r3+ is an endless Range.
328
+ So, attempting to set a positive infinity of "+t+" raises an Exception (+ArgumentError+).
329
+
330
+ If the new infinity(ies) does not contradict the current contents (like +r6+), it
331
+ is set accordingly and the subsequent operations (e.g., +r7+) adopt the value
332
+ (though you should make sure it is exactly what you want).
333
+
334
+
335
+ ==== Algorithm of determining default infinities
336
+
337
+ Callers can supply user-defined infinity objects for both or either
338
+ positive and negative infinity and in that case they are accepted
339
+ as the infinities with the highest priority, though ArgumentError might be
340
+ issued if they contradict the elements; for example, if a {Rangeary}
341
+ instance consists of an array of Integer Ranges (RangeExtd) like +(3..8)+,
342
+ and yet if String "abc" is specified as an infinity, it *contradicts*
343
+ the elements in the sense they are not comparable.
344
+
345
+ Internally, the {Rangeary} instance has a Hash extended with {Rangeary::Util::HashInf},
346
+ which can be obtained with {Rangeary#infinities}.
347
+ It has only 2 keys of +:negative+ and +:positive+, the values of which
348
+ are the current best-guessed or definite infinities. The Hash also
349
+ holds status information for each polarity with 3 levels of
350
+
351
+ 1. <tt>false</tt>
352
+ 2. <tt>:guessed</tt>
353
+ 3. <tt>:definite</tt>
354
+
355
+ It is +false+ only when the Rangeary is absolutely void with no
356
+ information about the contents: +Rangeary(RangeExtd::NONE)+.
357
+
358
+ If the user explicitly specifies a boundary in the optional arguments in
359
+ initialization of {Rangeary}, it is accepted in principle with an associated status of <tt>:definite</tt>.
360
+
361
+ If the user-specified main arguments in initialization contain
362
+ a (potentially multiple) {Rangeary}, their defined infinities are
363
+ inherited with their associated statuses.
364
+
365
+ Also, user-supplied Range-s or RangeExtd-s to the arguments in
366
+ initialization of {Rangeary} always have, except for
367
+ +RangeExtd::NONE+, concrete boundary values, which can be +nil+.
368
+
369
+ If one of the boundaries of a Range (n.b., it is *not* Rangeary) contains either +nil+ or one of infinite values
370
+ (which is checked with +RangeExtd::Infinity.infinite?+, where in practice
371
+ a duck-typing check is performed, using the method +infinite?+), then
372
+ it is accepted as an infinite value with an associated status of <tt>:definite</tt>.
373
+
374
+ Otherwise,
375
+
376
+ 1. if a boundary value is a (real-type) Numeric, +Float::INFINITY+ (or
377
+ its negative,
378
+ 2. or otherwise, +nil+
379
+
380
+ is set as an infinity of the boundary with an associated status of +:guessed+.
223
381
 
224
- (?a..).size # => nil
225
- (10..).size # => Infinity
226
- (10..Float::INFINITY)).size # => Infinity
227
- (1.0..) != (1.0..Float::INFINITY)
382
+ Note that the priority used to be different up to Rangeary Ver.1; +nil+ was not used and
383
+ instead the +RangeExtd::Infinity+ objects were used. It was because
384
+ the beginless Range (and endless Range before Ruby-2.6) has not been defined before
385
+ Ruby 2.7. Now they are defined, it is only natural to use +nil+ as the
386
+ default infinities in both ends, hence the change in specification in
387
+ {Rangeary} Ver.2.
388
+
389
+ Usually the arguments given in initialization of a {Rangeary} contain
390
+ more than one set of infinities candidate, unless only a single
391
+ argument of either Range (or its subclass instance) with no optional
392
+ arguments is given. The priority is judged in the following order:
228
393
 
229
- These are not trivial, or even a little inconsistent. For Range#size method, if it is for
230
- Numeric (but Complex), Endless Range behaves like the number Infinity,
231
- yet they (the mathematical infinity and the end of Endless Range) do
232
- not compare, and are not equal.
394
+ 1. the optional arguments
395
+ 2. an associated status of <tt>:definite</tt>, <tt>:guessed</tt>, and
396
+ <tt>false</tt> in this order
233
397
 
234
- Rangeary offers functions of logical operations of arrays of Ranges.
235
- A consistent definitions of infinity is essential. For example, the
236
- negation of +[5..Infinity]+ is
398
+ If the associated statuses are equal for two or more inputs, the most
399
+ extreme one among them for each polarity is chosen. For example, suppose
400
+ two instances of {Rangeary} are given in initialization of another
401
+ {Rangeary} and their negative infinities are "+b+" and "+c+". Then,
402
+ because of
237
403
 
238
- [-Infinity...5]
404
+ "b" < "c"
239
405
 
240
- that is, from the negative infinity to 5, excluding the end. Then the
241
- negation of it must recover the original Rangeary +(5..Infinity)+.
406
+ the former ("+b+") is adopted as the new negative infinity. Note that
407
+ the parameters given in the optional arguments have always higher
408
+ priority regardless.
409
+
410
+
411
+ The following examples demonstrate the specification.
242
412
 
243
- Obviously, it could not be done with +Endless Range+, as it defines only the positive infinity,
244
- or more accurately *positive endlessness*. For that reason, the use of
245
- +RangeExtd+ is crucial for Rangeary.
413
+ Rangeary(7..).negation
414
+ # => Rangeary(-Float::INFINITY...7)
415
+ Rangeary(7..).negation.negation
416
+ # => Rangeary(7..)
246
417
 
247
- In the Rangeary operation (of negation in this case), the following can happen:
418
+ Remember the default infinity for Float is +Float::INFINITY+. In this
419
+ case, however, the positive infinity was in practice specified by the
420
+ user to be +nil+ in the form of argument of +(7..)+ If you want to
421
+ specify the negative infinity instead, you must do it explicitly:
248
422
 
249
- r1 = Rangeary(5..) # => [(5..)] (equivalent to (5..nil))
250
- r2 = ~Rangeary(5..) # => [(-Float::INFINITY...5)]
251
- r3 = ~~Rangeary(5..) # => [(5..Float::INFINITY)]
423
+ Rangeary(7.., negative: nil).negation
424
+ # => Rangeary(...7)
252
425
 
253
- r4 = Rangeary(?a..) # => [(?a..)] (equivalent to (?a..nil))
254
- r5 = ~Rangeary(?a..) # => [(RangeExtd::Infinity::NEGATIVE...?a)]
255
- r6 = ~~Rangeary(?a..) # => [(?a..RangeExtd::Infinity::POSITIVE)]
426
+ Alternatively, you can always use conjunction like (the following two mean the same):
256
427
 
257
- There is no reason for this operation to result in the (Ruby-2.6) Endless Range
258
- that began with, after the negation operation from the negative infinity.
259
- Nevertheless, +r1==r3+ and +r4==r6+ must hold.
428
+ Rangeary(..nil).conjunction(Rangeary(7..)).negation
429
+ # => Rangeary(...7)
430
+ ~(Rangeary(..nil) * Rangeary(7..))
431
+ # => Rangeary(...7)
260
432
 
261
- +RangeExtd#==+ behaves in an almost identical way to +Range#==+
262
- (except it has +exclude_begin+), such as,
433
+ The registered infinities for each instance is obtained (Hash extended with
434
+ HashInf), which has
435
+ two keys of +:positive+ and +negative+, with the method {#infinities};
436
+ for example,
263
437
 
264
- (1.0..) != (1.0..Float::INFINITY)
265
- RangeExtd(1.0..) != RangeExtd(1.0..Float::INFINITY)
438
+ ran.infinities
439
+ # => <Hash(Inf): {:negative=>"a", :positive=>nil},
440
+ # status: {:negative=>:definite, :positive=>:guessed}>
266
441
 
267
- To maintain the mathematical consistency, Rangeary needs modify the
268
- equal operator so +r1==r3+ and +r4==r6+ hold, as the standard equal operator of Array would not do the job.
269
- Hence, Rangeary now regards an Endless Range and a Range that ends with another Infinity
270
- as equal, as long as all the other Range parameters like the "begin"
271
- and +exclude_end+ are equal.
442
+ Note that the values of the returned Hash (+HashInf) may be +false+;
443
+ if it is not convenient, call it as +#instances(convert: true)+
444
+ with which +false+ in the returned value, if there is any, is converted
445
+ to +nil+ and the standard Hash as opposed to
446
+ Hash extended with {Rangeary::Util::HashInf} is returned:
272
447
 
273
- In practice, +Array#==+ is modified in
274
- this library (which is naturally inherited to Rangeary), and hence the
275
- commutativity of the equal operator is assured.
448
+ ran.infinities(convert: true) # => { :negative => "a"
449
+ # :positive => nil, }
276
450
 
277
- Note that +Rangeary#equiv+ method may behave differently from the equal operator.
451
+ Consult the manuals of the methods for detail.
452
+
453
+
454
+ === Array#==
455
+
456
+ Equal method of +Array#==+ (and thus its child-class +Rangeary#==+) is
457
+ modified slightly so that the behaviour when both are *practically*
458
+ empty.
459
+
460
+ Rangeary objects are never empty in the sense of Array; it contains at
461
+ least +RangeExtd::NONE+, which is *empty*. Therefore, if either or
462
+ both the Array/Rangeary return true with {Rangeary#empty_element?},
463
+ then it is regarded as equivalent to +Array#empty?+.
464
+
465
+ Up to Ver.1 (or more precisely, in Ver.1), the equality behaviour was
466
+ far more complicated. The complexity originated in the incompleteness
467
+ of the Ruby built-in borderless Range;
468
+ while Ruby-2.6 introduced the endless Range, which Rangeary Ver.1 supported,
469
+ it lacked the beginless Range till the release of Ruby-2.7.
470
+
471
+ Since Rangeary Ver.1 implemented only the endless Range, logical
472
+ operations, especially those that involve negation either explicitly
473
+ or implicitly (like subtraction), were self-incomplete. To mitigate
474
+ the problem, the equality method was designed to handle the
475
+ incompleteness instead in Rangeary Ver.1.0.
476
+
477
+ Now that both beginless and endless Ranges are supported by Ruby and
478
+ Rangeary Ver.2, such an ad hoc fix is no longer necessary or
479
+ desirable. The difference from the Ruby default is now minimum.
480
+
481
+ Note that this library loads the utility library associated with
482
+ +RangeExtd+, which modifies some behaviours of the equality method
483
+ (operator) of all Object, if in a backward-compatible way, i.e., users
484
+ do not have to worry about it for the use outside RangeExtd. For
485
+ logical operations implemented in this library, commutative behaviours
486
+ of the operators are essential and they would be only achieved by
487
+ modifying +Object#==+.
488
+
489
+ Also note that +Rangeary#equiv+ method may behave differently from the equal operator.
278
490
  For example,
279
491
 
280
492
  Rangeary(RangeExtd(1,"<...",4), 5...8).equiv?(Rangeary(2..3, 5..7))
281
493
 
282
- returns true. To describe this, this left and right Rangearies are arrays of
494
+ returns true. To describe this, the left and right Rangearies are arrays of
283
495
  ranges of Integers which consist of
284
496
 
285
497
  * (Left)
@@ -290,41 +502,23 @@ ranges of Integers which consist of
290
502
  2. Range that starts and ending at 5 and 7, respectively, both inclusive.
291
503
 
292
504
  According to +Integer#succ+ (+Range#each+) method, the left and right ones are equivalent.
293
- Therefore +Rangeary#equiv+ returns true, wheras the equal operator
505
+ Therefore +Rangeary#equiv+ returns true, whereas the equal operator
294
506
  returns false for this.
295
507
 
296
- === Infinities
297
-
298
- The infinities are vital in the logical operation of Rangeary. In
299
- default, the general infinities of
300
- <tt>RangeExtd::Infinity::POSITIVE</tt> and <tt>RangeExtd::Infinity::NEGATIVE</tt>
301
- are used (see ({RangeExtd}[http://rubygems.org/gems/range_extd] for
302
- detail), except for comparable Numerics (Integer, Rational, Float
303
- etc), for which +Float::INFINITY is used in default. However, a user
304
- can specify their own infinities in initialisation with the options of
305
- +positive:+ and +negative:+.
306
-
307
- Note once an infinity is defined for a Rangeary object, any other
308
- Rangeary objects with which operations are performed should have the
309
- same infinities. In default, if different infinities are specified
310
- (or if only one of them has a pair of specified infinities and the
311
- others have the default values), the values that is smallest for the
312
- Positive infinity and largest for the Negative are used, though a
313
- warning may be issued (only if the built-in global variable +$VERBOSE+
314
- is true). An exception is when a user explicitly specifies the infinities in the
315
- option in creating a new Rangeary out of other Rangeary objects and else, in which case the infinities from the
316
- other "inherited" Rangeary included in the main parameter are ignored.
317
-
318
- The last example above shows how it works.
319
508
 
320
509
  == Known bugs
321
510
 
322
- * To suppress warnings in Ruby-2.7, {RangeExtd}[https://rubygems.org/gems/range_extd] must be Ver.1.1.1 or later.
323
- * <tt>Rangeary.new(-6..-5, 2..5, 8..8).last_element(3)</tt> returns
324
- <tt>[3, 4, 5]</tt> wrongly in Ruby-2.7 but correctly in Ruby-2.6.x or earlier.
511
+ * Rangeary Ver.2, which supports both beginless and endless Ranges (of Ruby 2.7 and later), requires {RangeExtd}[https://rubygems.org/gems/range_extd] Ver.2 or later.
512
+ * To suppress warnings in Ruby-2.7 (in Rangeary Ver.1), {RangeExtd}[https://rubygems.org/gems/range_extd] must be Ver.1.1.1 or later.
513
+ * +Rangeary#last_element+ includes a monkey patch (which used to raise
514
+ an error before Rangeary Ver.2) to handle a bug in +Range#last+ in Ruby-2.7
515
+ and above (at least up to 3.1.2). See
516
+ {Bug #18994}[https://bugs.ruby-lang.org/issues/18994] for detail of the bug,
517
+ which was in no time resolved with ({patch #6324}[https://github.com/ruby/ruby/pull/6324],
518
+ applied at commit {bbe5ec7}[https://github.com/ruby/ruby/commit/bbe5ec78463f8d6ef2e1a3571f17357a3d9ec8e4]).
519
+ It happens only in very limited conditions.
325
520
 
326
- This library requires Ruby 2.1 or above (it may work all right with
327
- Ruby 1.9.3, however I have never tested it).
521
+ This library (for Ver.2 and later) requires Ruby 2.7 or later.
328
522
 
329
523
  Extensive tests have been performed, as included in the package.
330
524
 
@@ -336,22 +530,23 @@ Nothing planned.
336
530
 
337
531
  == Final comment
338
532
 
339
- This work is inspired by Ian Stewart, who developped a (numeric)
340
- multiple-range library in Fortran90 for the SAS software package for
533
+ This work is inspired by Ian Stewart, who developed a (numeric)
534
+ multiple-range library in FORTRAN90 for the SAS software package for
341
535
  XMM-Newton telescope. I appreciate his work.
342
536
 
343
537
  The implementation to Ruby was not straightforward, though, partly
344
538
  because the built-in Range does not allow to exclude the begin
345
539
  boundary, and partly because the infinity is not defined for general
346
540
  objects but Numeric (Real) in Ruby (which later changed partially with
347
- the introduction of Endless Range in Ruby 2.6 released in 2018 December). RangeExtd class I have developed
541
+ the introduction of Endless Range in Ruby 2.6 released in 2018
542
+ December and further and completely with the introduction of Beginless Range in Ruby
543
+ 2.7 released in 2019 December). +RangeExtd+ class I have developed
348
544
  makes this library possible. I am glad the completeness of ranges in
349
545
  the 1-dimensional space for arbitrary Comparable object is achieved now.
350
546
 
351
547
  Enjoy.
352
548
 
353
549
 
354
-
355
550
  == Miscellaneous
356
551
 
357
552
  == Copyright etc
@@ -371,10 +566,14 @@ Versions:: The versions of this package follow Semantic Versioning (2.0.0) http:
371
566
  0.5 < x <= 2.5, 6.0 <= x < 8.0, 10.0 <= x (<= 無限大)
372
567
  がこのクラスで定義できます。
373
568
 
374
- RangeExtd の要素たり得るオブジェクトは全て {Rangeary} の要素となり
375
- 得ます。Numeric(の実数)に限らず、Comparable であるオブジェクトは全て
376
- 可能です。また、{Rangeary} は、組込Rangeクラスのオブジェクトも初期化に
377
- 使えます。
569
+ RangeExtd の要素たり得るオブジェクトは全て +Rangeary+ の要素となり得ます。
570
+ Numeric(の実数)に限らず、Comparable であるオブジェクトは全て
571
+ 可能です。また、+Rangeary+ は、組込Rangeクラスのオブジェクトも、それが
572
+ {RangeExtd}[https://rubygems.org/gems/range_extd] クラスの
573
+ (<tt>Range#valid? == true</tt>) を満たす限り初期化に使えます
574
+ (注: ほとんどの Rangeオブジェクトはこの条件を満たすものの、
575
+ +(true..true)+ のように満たさないものもあって、実際、それらは複数
576
+ Rangeの構成要素としては意味をなさないものです)。
378
577
 
379
578
  四つの標準論理演算全て、すなわち否定(negation; <tt>~</tt>)、論理積
380
579
  (conjunction; <tt>&</tt> または <tt>*</tt>)、論理和(disjunction;
@@ -390,38 +589,65 @@ RangeExtd の要素たり得るオブジェクトは全て {Rangeary} の要素
390
589
  す。したがって、イミュータブルな{Rangeary}に許される限りにおいて、
391
590
  Arrayに使えるほとんどの操作を{Rangeary}に適用できます。加えて、
392
591
  {Rangeary}には、そのレンジの要素に直接働きかけるメソッドも幾つかあり
393
- ます。上に挙げた例ならば、<tt>#cover?(1.0)</tt> は真を返し、
394
- <tt>#cover?(9.0)</tt> は偽を返します。
592
+ ます。上に挙げた例ならば、<tt>Range#cover?(1.0)</tt> は真を返し、
593
+ <tt>Range#cover?(9.0)</tt> は偽を返します。
395
594
 
396
595
  このクラスにより、1次元レンジへの論理演算が可能かつ容易になりました。
397
596
  これが有用なものであることを願ってここにリリースします。
398
597
 
598
+ === News: Libraryの場所、Beginless Rangeのサポート他
599
+
600
+ **重要**: ライブラリのパスが{Rangeary} Ver.1 から Ver.2 で、
601
+ ディレクトリの階層一つ上がりました。これは、Ruby Gemの慣用にそうように
602
+ するためです。端的には、標準的方法は、+require "rangeary"+ です。
603
+ 以前のパスは、"rangeary/rangeary" でした。
604
+
605
+ それに伴い、{Rangeary} のバージョンを2.0にあげました。
606
+
607
+ Ruby 2.7以上では、 {Beginless range}[https://rubyreferences.github.io/rubychanges/2.7.html#beginless-range]
608
+ がサポートされます。
609
+
610
+ +Rangeary+ (Ver.2) もそれをサポートします。ただし、
611
+ {RangeExtd}[https://rubygems.org/gems/range_extd] Ver.2 (以上)
612
+ が必要です。
613
+
614
+ ==== News: Endless Range サポートしました
615
+
616
+ 2019年10月(Rangeary Ver.1)より、Ruby-2.6 で導入された
617
+ {Endless Range}[https://rubyreferences.github.io/rubychanges/2.6.html#endless-range-1]
618
+ をついにサポートしました!
619
+
399
620
 
400
621
  == インストール
401
622
 
402
- まず、RangeExtd クラスのライブラリ(純粋に rubyで書かれたファイル 2
403
- です)が必要です。gem を使ってインストールできます。
623
+ まず、 {RangeExtd}[https://rubygems.org/gems/range_extd] クラス(Ver.2
624
+ 以上)のライブラリが必要です。gem を使ってインストールできます。
404
625
  gem install range_extd
405
626
  もしくは以下から入手して下さい。
406
- https://rubygems.org/gems/range_extd
627
+ {https://rubygems.org/gems/range_extd}
628
+
629
+ (同ライブラリを手動でインストールする場合は、同ライブラリのマニュアルを参照。)
407
630
 
408
631
  次いで、このライブラリをインストールします。
409
632
  gem install rangeary
410
633
 
411
- ファイルが一つ、
634
+ ファイル
635
+ rangeary.rb
636
+ rangeary/util.rb
637
+ rangeary/util/hash_inf.rb
412
638
  rangeary/rangeary.rb
413
- <tt>$LOAD_PATH</tt>上のどこかにインストールされるはずです。
639
+ が<tt>$LOAD_PATH</tt>上のどこかにインストールされるはずです。
414
640
  あるいは以下から入手可能です。
415
- http://rubygems.org/gems/rangeary
641
+ {http://rubygems.org/gems/rangeary}
416
642
 
417
643
  後は、Ruby のコード(又は irb)から
418
- require 'rangeary/rangeary'
419
- とするだけです。もしくは、特に手でインストールした場合は、
420
644
  require 'rangeary'
421
- とする必要があるかも知れません。他のファイル(RangeExtd 用の
422
- <tt>range_extd/range_extd.rb</tt>)は、自動的に読込まれます。
645
+ とするだけです。
646
+ (このパスは、Rangeary Ver.1 では "rangeary/rangeary" でした!)
423
647
 
424
- {Rangeary} は Ruby 2.1 以上で動きます。
648
+ 他のファイル(特に<tt>range_extd.rb</tt>)は、自動的に読込まれます。
649
+
650
+ {Rangeary} Ver.2 は Ruby 2.7 以上で動きます。
425
651
 
426
652
  お楽しみあれ!
427
653
 
@@ -443,7 +669,7 @@ http://rubygems.org/gems/rangeary
443
669
  て取ります。ただし、Rangeクラスのオブジェクトで +Range#valid?+ が偽
444
670
  を返すものは、例外が発生します。
445
671
 
446
- さらなる解説及び例は、{Rangeary.new}を参照して下さい。
672
+ さらなる解説及び例は、{Rangeary.initialize}を参照して下さい。
447
673
 
448
674
 
449
675
  === 実践例
@@ -478,33 +704,52 @@ http://rubygems.org/gems/rangeary
478
704
 
479
705
  == 詳説
480
706
 
481
- ファイル <tt>rangeary.rb</tt> が読まれた段階で、RangeExtd ライブラリで
482
- 定義される二つのクラス(<tt>RangeExtd</tt>
483
- <tt>RangeExtd::Infinity</tt>)に加えて、クラス一つが定義されます。
484
-
485
- * Rangeary
486
-
707
+ ファイル <tt>rangeary.rb</tt> が読まれた段階で、
708
+ {RangeExtd}[https://rubygems.org/gems/range_extd] ライブラリで定義されるクラス
709
+ (<tt>RangeExtd</tt>, <tt>RangeExtd::Infinity</tt>, <tt>RangeExtd::Nowhere</tt>)
710
+ が読み込まれます。
711
+ <tt>RangeExtd</tt> は、組込み built-in <tt>Range</tt> クラスにいくつかのメソッドを追加します。
487
712
 
488
713
  === Rangeary クラス
489
714
 
490
715
  {Rangeary} オブジェクトは、Range や RangeExtd と同様、イミュータブルで
491
716
  す。だから、一度インスタンスが生成されると、変化しません。
492
717
 
493
- インスタンスの生成方法は上述の通りです(「使用例」の章)。レンジとして"valid"と見
494
- なされないインスタンスを生成しようとすると、すなわち +Range#valid?+ が
718
+ インスタンスの生成方法は上述の通りです(「使用例」の章)。レンジとして"valid"
719
+ と見なされないインスタンスを生成しようとすると、すなわち +Range#valid?+ が
495
720
  偽を返すレンジを使おうとすると、例外(<tt>ArgumentError</tt>)が発生し、
496
721
  失敗します。
497
722
 
498
- なお、ユーザーは、{Rangeary} インスタンス生成の時、レンジ要素に対応し
499
- た正負の無限大オブジェクトを付加することができます。デフォルトでは、
500
- +Float::INFINITY+ または +RangeExtd::Infinity::POSITIVE+ の類に
501
- なります。詳しくは {Rangeary.new} を参照下さい。
723
+ +Rangeary+初期化引数の +RangeExtd+ (または +Range+) オブジェクトは
724
+ (どちらかあるいは両方の境界に)無限大オブジェクトを含むかも知れません。
725
+ Ruby 2.7 と2.6にて、
726
+ {Beginless range}[https://rubyreferences.github.io/rubychanges/2.7.html#beginless-range],
727
+ {Endless Range}[https://rubyreferences.github.io/rubychanges/2.6.html#endless-range-1]
728
+ がそれぞれ導入されました。これらは、本ライブラリに当初から定義されていた
729
+ {RangeExtd::Infinity}[https://www.rubydoc.info/gems/range_extd/RangeExtd/Infinity]
730
+ に対応すると言えます。
731
+
732
+ 組込みのこれら境界のない Ranges と +RangeExtd::Infinity+ オブジェクトは
733
+ 類似しているものの概念としては少し異なります。この違いは、
734
+ 境界のない Ranges と+Float::INFINITY+ との違いに似ています。
735
+ {RangeExtd}[http://rubygems.org/gems/range_extd]
736
+ のマニュアルに詳細に解説されています。
737
+
738
+ デフォルトでは、引数に与えられたRangeがこれら無限大オブジェクト
739
+ (+nil+であれ+Float::INFINITY+であれ+RangeExtd::Infinity+であれ)
740
+ を含む場合、+Rangeary+ はそれをそのまま使います。あるいは、
741
+ ユーザーは、{Rangeary} インスタンス生成の時、
742
+ レンジ要素に対応した正負の無限大オブジェクトを指定することもできます。
743
+ たとえば、1文字のStringのRangeに対して、+"a"+ を負の無限大オブジェクト
744
+ として指定することができます。もし何も指定がない場合は、Rangearyは、
745
+ デフォルトで、 Numeric (実数) に対しては+Float::INFINITY+、
746
+ それ以外には +nil+ を無限大とします。
502
747
 
503
748
  生成時に複数のレンジが引数として与えられた時、ソートされて保持されます。
504
749
  その時、要素のどこかに重複する部分があった時は、論理和として扱われます
505
750
  (つまり、単純に足し合わされます)。これはつまり、{Rangeary} が内部的に
506
751
  保持するオブジェクトは生成時に与えられたものとは異なる、すなわち
507
- {#object_id} が異なるかも知れないことを意味します。特に、組込Rangeが引
752
+ <tt>#object_id</tt> が異なるかも知れないことを意味します。特に、組込Rangeが引
508
753
  数として与えられた時は、常に RangeExtd オブジェクトに内部で変換されます。
509
754
 
510
755
  もし生成時に与えられたレンジのどれかが空、すなわち +Range#empty?+ が真
@@ -515,9 +760,9 @@ http://rubygems.org/gems/rangeary
515
760
  +RangeExtd::NONE+となり、したがって
516
761
  {Rangeary#empty_element?} が真を返します。
517
762
 
518
- そのため、どの Rangeary オブジェクトも、{Rangeary#to_a}.size は常に正
763
+ そのため、どの Rangeary オブジェクトも、+Rangeary#to_a.size+ は常に正
519
764
  の値を返し、零を返すことはありません。あるいは、Array から継承した
520
- {Rangeary#empty?} は常に偽を返します(オブジェクトがレンジとして空かど
765
+ +Rangeary#empty?+ は常に偽を返します(オブジェクトがレンジとして空かど
521
766
  うかをチェックするには、{Rangeary#empty_element?} を使って下さい)。
522
767
  Rangeary(RangeExtd::NONE).empty? # => false
523
768
  Rangeary(RangeExtd::NONE).empty_element? # => true
@@ -532,7 +777,7 @@ http://rubygems.org/gems/rangeary
532
777
  に偽を返します。
533
778
  また、[#length] と [#reverse] とは未定義化されています。
534
779
 
535
- {Rangeary#==} と {Rangeary#eql?} は、Arrayと同様に動作します。だから
780
+ {Rangeary#==} と +Rangeary#eql?+ は、Arrayと同様に動作します。だから
536
781
  [2..4, 6..8] == Rangeary(2..4, 6..8) # => true
537
782
  も成り立ちます。しかし注意すべきは以下です。
538
783
  [2..4, 6..8] == Rangeary(6..8, 2..4) # => true
@@ -542,7 +787,7 @@ http://rubygems.org/gems/rangeary
542
787
 
543
788
  レンジ要素に対してではなく、レンジを構成する要素に対して動作する他の
544
789
  全てのメソッドは、組込Arrayクラスに同名のメソッドが存在する場合、接尾辞
545
- <tt>_element</tt> がつきます。たとえば、{Rangeary#size} は、保持する
790
+ <tt>_element</tt> がつきます。たとえば、+Rangeary#size+ は、保持する
546
791
  RangeExtd オブジェクトの数を返し、一方、{Rangeary#size_element}は、
547
792
  保持するすべての RangeExtdオブジェクトに対して +Range#size+ を行った
548
793
  その総和を返します。
@@ -550,7 +795,10 @@ RangeExtd オブジェクトの数を返し、一方、{Rangeary#size_element}
550
795
  Rangeary(1..3, 5..8).size_element # => 7
551
796
 
552
797
  {Rangeary#flatten_element} は、全てのRangeExtd要素に対して
553
- {Rangeary#to_a} を実行して、その結果を結合した配列を返します。
798
+ +Rangeary#to_a+ を実行して、その結果を結合した配列を返します。
799
+
800
+ もしArrayをflattenしたいけれど各Rangeary は保持したい場合は、
801
+ {Rangeary.flatten_no_rangeary} を使えます。
554
802
 
555
803
  クラスとメソッドの完全なマニュアルは、
556
804
  {Rubygems のウェブサイト}[http://rubygems.org/gems/rangeary]上にあります。
@@ -558,70 +806,33 @@ RangeExtd オブジェクトの数を返し、一方、{Rangeary#size_element}
558
806
  することで、手元でコンパイルすることもできます(RubyGems の +yard+
559
807
  がインストールされている必要があります)。
560
808
 
561
- === Array#==
562
-
563
- 等号メソッド +Rangeary#==+ は、Array とは異なった挙動をするべきであり、
564
- 実際します。まず、*empty?* の際の意味が、異なります。なぜならば、
565
- Rangeary オブジェクトは、Array的な意味では、empty になることは決してな
566
- いからです。次に、Ruby 2.6で導入された
567
- {Endless Range}[https://rubyreferences.github.io/rubychanges/2.6.html#endless-range-1]
568
- は、概念的にそう素直には組み込まれません。例えば、Ruby 2.6 では以下の
569
- ような挙動を示します。
570
-
571
- (?a..).size # => nil
572
- (10..).size # => Infinity
573
- (10..Float::INFINITY)).size # => Infinity
574
- (1.0..) != (1.0..Float::INFINITY)
575
-
576
- これらは、当たり前の挙動ではありませんし、若干矛盾があるとさえ言えるかも
577
- 知れません。Range#size メソッドに関しては、Numericクラス(Complexを除く)
578
- のオブジェクトに関しては、Endless Range は、数学的無限のように振舞いま
579
- す。しかし、両者を比較することはできず、等号比較すると、偽が返ります。
580
-
581
- Rangeary は、複数の Range からなる数列(array)に対して、論理演算する機
582
- 能を提供します。そのためには、一貫した「無限」の定義が不可欠です。例え
583
- ば、 +[5..Infinity]+ の否定は、
584
-
585
- [-Infinity...5]
586
-
587
- すなわち、無限小から始まり、5 で終わるが、終端は含まれない、ということ
588
- になります。これに対して再度否定演算を行うと、元の Rangeary +(5..Infinity)+
589
- が再度得られなければなりません。
590
-
591
- この演算は、当然、+Endless Range+ だけでは不可能です。+Endless Range+
592
- は正の無限大、あるいは正確には、正の方向に開いたもの、しか定義していないからです。
593
- このため、+RangeExtd+ を使用することは、Rangeary では決定的に重要なの
594
- です。
595
-
596
- Rangearyの演算(ここでは否定演算)においては、以下のようになります。
809
+ === 無限大
597
810
 
598
- r1 = Rangeary(5..) # => [(5..)] (equivalent to (5..nil))
599
- r2 = ~Rangeary(5..) # => [(-Float::INFINITY...5)]
600
- r3 = ~~Rangeary(5..) # => [(5..Float::INFINITY)]
811
+ 無限大の扱いは単純ではありません。
601
812
 
602
- r4 = Rangeary(?a..) # => [(?a..)] (equivalent to (?a..nil))
603
- r5 = ~Rangeary(?a..) # => [(RangeExtd::Infinity::NEGATIVE...?a)]
604
- r6 = ~~Rangeary(?a..) # => [(?a..RangeExtd::Infinity::POSITIVE)]
813
+ 最大の注意点として、一旦、無限大が定義されると、そのオブジェクトと演算を行う全ての
814
+ Rangearyオブジェクトも同じ無限大をもつべきです。
605
815
 
606
- この演算において、負の無限大も経過した最後の結果が、元々の出発点である(Ruby2.6で言う)
607
- Endless Rangeにならなければいけない理由はないことになります。しかしながら、
608
- +r1==r3+ および +r4==r6+ は成立すべきです。
816
+ 取扱いと挙動の詳細は英語版マニュアルの「Infinities」章を参照してください。
609
817
 
610
- +RangeExtd#==+ は、+Range#==+ とほぼ同一に振舞います(前者は
611
- +exclude_begin+ メソッドを持つことを除いて)。例えば、
818
+ === Array#==
612
819
 
613
- (1.0..) != (1.0..Float::INFINITY)
614
- RangeExtd(1.0..) != RangeExtd(1.0..Float::INFINITY)
820
+ 等号メソッド +#==+ は、Array とはほんの少し異なった挙動をします。
821
+ というのも、*empty?* の意味が、異なるからです。なぜならば、
822
+ Rangeary オブジェクトは、Array的な意味では、empty になることは決してありません。
823
+ したがって、{Rangeary#empty_element?} または通常の *empty?* が双方真の時は+Rangeary#==+
824
+ は真を返すように +Array#==+ (したがって +Rangeary#==+)が変更されています。
615
825
 
616
- +r1==r3+ +r4==r6+ も成立するよう、数学的一貫性を保つために、Rangeary では、
617
- 等号の定義を変更する必要がありました。Array クラスのデフォルトの標準等
618
- 号ではダメだからです。
619
- そこで、Rangeary では、Endless Range と終端が何か別の無限大である
620
- Rangeとは、他のパラメーター(例えば始端や+exclude_end+)が等しい限り、等
621
- しいと見なします。
826
+ Rangeary Ver.1.0 では、
827
+ Ruby 2.6で導入された
828
+ {Endless Range}[https://rubyreferences.github.io/rubychanges/2.6.html#endless-range-1]
829
+ を概念的に素直に組み込むのが困難であり、Rangeary 内部で矛盾ない扱いを保証するために、
830
+ +Range#==+ に複雑な変更を含みました。Ruby 2.7 で Beginless Range
831
+ が導入されたことでその矛盾が自然に解消されたため、Rangeary Ver.2.0 では、その複雑な判断ルーチンは撤廃されました。
622
832
 
623
- 実装としては、本ライブラリ内で +Array#==+ (Rangearyに自然に継承される)を変更しています。
624
- そのため、等号の可換性が保たれています。
833
+ 本ライブラリは +RangeExtd+ 付属ライブラリを読み込み、そこでは Object
834
+ の等号が少し変更されています(Ruby標準と互換性はあるので心配無用)。
835
+ これは、Rangeary内部で演算の双方向性を保証するために必要な措置です。
625
836
 
626
837
  なお、+Rangeary#equiv+ メソッドは、等号とは異なる挙動をすることがあり
627
838
  ます。例えば、
@@ -642,34 +853,23 @@ Rangeからなる配列で、
642
853
  +Rangeary#equiv+ でこの両者を比較すると真を返します。一方、等号は負を
643
854
  返します。
644
855
 
645
- === 無限大
646
-
647
- 正負の無限大は、Rangearyの演算に不可欠です。デフォルトでは、正負の一般無限大は
648
- <tt>RangeExtd::Infinity::POSITIVE</tt> と <tt>RangeExtd::Infinity::NEGATIVE</tt>
649
- が使われます(詳細は {RangeExtd}[http://rubygems.org/gems/range_extd] 参照)。
650
- 但し、例外が、比較可能な Numerics (Integer, Rational, Floatなど)で、
651
- +Float::INFINITY がデフォルトで使われます。しかし、ユーザーは、オブジェ
652
- クト作成の時、+positive:+ および +negative:+ で自分の無限大を定義することもできます。
653
-
654
- なお、一旦、無限大が定義されると、そのオブジェクトと演算を行う全ての
655
- Rangearyオブジェクトも同じ無限大をもつべきです。デフォルトでは、演算す
656
- るオブジェクト間でもし異なる無限大が使われている場合(端的には、一つの
657
- オブジェクトにだけ無限大を定義して他はデフォルトで済ませている場合)は、
658
- 正の無限大には最小の値、負の無限大には最大の値が選ばれます。但し、警告
659
- メッセージが出るかもしれません(組込グローバル変数 +$VERBOSE+が真の時のみ)。
660
- 例外は、複数のRangearyから新しいRangeryオブジェクトを作成の際に、オプ
661
- ションで陽に無限大を指定した時で、その時は、それらRangearyから継承した
662
- 値は考慮されず、指定した値が使われます。
663
-
664
- 上記の最後の例が、指定する例です。
665
856
 
666
857
  == 既知のバグ
667
858
 
859
+ * Rangeary Ver.2 は Beginless/Endless Ranges (Ruby 2.7以降)をサポートしますが、そのために、
860
+ {RangeExtd}[https://rubygems.org/gems/range_extd] Ver.2 以降が必須。
668
861
  * Ruby-2.7 で警告を出さないためには、{RangeExtd}[https://rubygems.org/gems/range_extd] は、Ver.1.1.1 以上であること。
862
+ * +Rangeary#last_element+ はちょっと汚いパッチが入っています(Rangeary
863
+ Ver.1 では例外が発生していた)が、これは、Ruby-2.7以降(3.1.2でもまだ未修正)
864
+ のバグに対処するためです。バグ詳細は、
865
+ {Bug #18994}[https://bugs.ruby-lang.org/issues/18994]。
866
+ すぐに対処されました({patch #6324}[https://github.com/ruby/ruby/pull/6324],
867
+ コミット {bbe5ec7}[https://github.com/ruby/ruby/commit/bbe5ec78463f8d6ef2e1a3571f17357a3d9ec8e4])。
868
+ そもそもごくごく限られたケースでしか問題になることはありませんが。
669
869
  * <tt>Rangeary.new(-6..-5, 2..5, 8..8).last_element(3)</tt> は、
670
870
  Ruby-2.6.x 以前は正しい値を返すが、Ruby-2.7 ではなぜか <tt>[3, 4, 5]</tt> と誤った値を返す。
671
871
 
672
- このライブラリは Ruby 2.1 以上を必要とします。
872
+ このライブラリは Ruby 2.7 以上を必要とします。
673
873
 
674
874
  パッケージに含まれている通り、網羅的なテストが実行されています。
675
875
 
@@ -683,14 +883,15 @@ Rangearyオブジェクトも同じ無限大をもつべきです。デフォル
683
883
 
684
884
  このライブラリは、イアン・スチュワート氏が開発した、XMM-Newton望遠鏡用の
685
885
  SAS解析ソフトウェア・パッケージに含まれる(数値)複数レンジの
686
- Fortran90ライブラリにアイデアを得たものです。彼の仕事に感謝します。
886
+ FORTRAN90ライブラリにアイデアを得たものです。彼の仕事に感謝します。
687
887
 
688
888
  しかし、Rubyへの実装は、一筋縄ではいきませんでした。一つには組込Range
689
- クラスでは始点を除外することができないこと、また一つには Rubyでは数値
690
- を除いて一般オブジェクトに対しての無限大が定義されていないからです。
691
- 小生の開発した RangeExtd によって初めてこのライブラリが可能となりま
692
- した。これにより、今、比較可能な任意のオブジェクトについて、1次元上の
693
- レンジの完全性が実現できたことを嬉しく思います。
889
+ クラスでは始点を除外することができないこと、また一つには Ruby
890
+ では数値を除いて一般オブジェクトに対しての無限大が定義されていないからです
891
+ (後者は、Ruby-2.6と2.7とで一定の解決を見たため、本ライブラリ(Ver.2)でも採り入れました!)。
892
+ 小生の開発した RangeExtd によって初めてこのライブラリが可能となりました。
893
+ これにより、今、比較可能な任意のオブジェクトについて、
894
+ 1次元上のレンジの完全性が実現できたことを嬉しく思います。
694
895
 
695
896
  お楽しみ下さい。
696
897