From: Alexey Muranov Date: 2011-11-11T19:51:40+09:00 Subject: [ruby-core:40940] [ruby-trunk - Feature #5534] Redefine Range class and introduce RelativeNumeric and RelativeRange 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