Issue #5534 has been updated by Alexey Muranov.


I have just discovered that there is  Range#cover?  method which works how i would expect  Range#include?  to work.  I would have preferred that these two behaved identically and that there were only one of them.  Having the both looks to me like a way to cover up inconsistencies between different meaning and uses of Range.

I think that  Rang#to_a  has to accept an argument to tell it which elements of the range to use.  The same when converting a range to an Enumerator with Range#each.  Maybe the Enumerable module can provide some tools to create objects like "all_integers", "all_even_integers", "all_words_in_capital_letters_A_to_Z". Maybe even constants Enumerable::INTEGERS, Enumerable::EVEN_INTEGERS, Enumerable::WORDS_IN_CAPITAL_LETTERS_A_TO_Z would be good enough to begin with:

(1..6).to_a(Enumerable::INTEGERS) # => [1, 2, 3, 4, 5, 6]
(1..6).to_a(Enumerable::EVEN_INTEGERS) # => [2, 4, 6]

I understand better now the difficulties in defining a range as something other than a pair of bounds:  whether ("X".."AB") is empty depends on the order (lex or deglex).
I can think of one possible solution:  allow to specify the order in cases where more than one exists.   Probably an order should be an object which knows its own properties, but in simple cases, to start with, it can be a symbol.  The use would be like this:

Range.new("A", "AB", exclusive=false, order=:lex).include?("X")  # => false
Range.new("A", "AB", exclusive=false, order=:deglex).include?("X")  # => true
Range.new("A", "C", exclusive=false, order=:lex).to_a("Enumerable::WORDS_IN_CAPITAL_LETTERS_A_TO_Z")  # => InfniteArrayError: ["A", "AA", "AAA", ...]
Range.new("A", "C", exclusive=false, order=:deglex).to_a("Enumerable::WORDS_IN_CAPITAL_LETTERS_A_TO_Z")  # => ["A", "B", "C"]

These are very rough ideas.
----------------------------------------
Feature #5534: Redefine Range class and introduce RelativeNumeric and RelativeRange
http://redmine.ruby-lang.org/issues/5534

Author: Alexey Muranov
Status: Open
Priority: Normal
Assignee: 
Category: core
Target version: 


I started by commenting on Feature #4541, but ended up with proposing a new feature myself.

I suggest to redefine the behavior of Range class so that all empty ranges be equal:

(2..1) == (1..-1) and (2..1) == (1...1) and (2..1) == ('z'..'a') # => true

In other fords, ranges `r1` and `r2` should be equal if and only if `r1.include?` and `r2.include?` give identical results for all inputs.  (Why is it not `includes?` by the way?)  Thus Range would simply be a way to store certain infinite sets.

This change will result in not being able to slice an array `a` from beginning and from the end simultaneously with `a[1..-2]`. To resolve this, i propose to introduce `RelativeNumeric` and `RelativeRange` classes.

Each `RelativeNumeric` would be a `Numeric` with an "anchor", which is an arbitrary symbol.  For example:

3.from(:bottom)  # would return a "relative" 3 with "anchor" :bottom

One can define shortcuts `#from_bottom` for `#from(:bottom)` and `#from_top` for `#from_top`.

A `RelativeRange` is a range with relative bounds.  If bounds of a relative range r are relative to the same anchor and the range is seen to be empty, it should be equal to *the* empty relative range with this anchor.  For example:

(3.from(:center)..2.from(:center)) == (0.from(:center)...0.from(:center)) # => true

Now, to do what is currently done by `a[1..-2]`, one can redefine `Array#slice` to use instead:

a[1.from_bottom..(-1).from_top]

What do you think?



-- 
http://redmine.ruby-lang.org