Crystal: Reverse range

Created on 7 Jun 2017  路  5Comments  路  Source: crystal-lang/crystal

I need to create an iterator from a range with either positive or negative steps, where start, stop, step are dependent on input. The problem: Range#step does not work with negative steps (10..5 and Range.new(10, 5).step(-1) are just empty). There is also Number#step but it does not respect exclusive range, which I require.

It's easy to build such an iterator specific for my purpose. But I think it would be nice if this could be in stdlib.

I noticed a few very similar iterators implemented in stdlib, maybe it would be usefull to merge some of them together? I'm nut sure about this, I just wanted to mention it.

  • Number::StepIterator used by Number#step
  • Number#step with block
  • [Int::UptoIterator](https://github.com/crystal-lang/crystal/blob/20655484b34a35ed1e69c8cad0525df8fac22323/src/int.cr#L503) forInt#upto`
  • Int#upto with block
  • [Int::DowntoIterator](https://github.com/crystal-lang/crystal/blob/20655484b34a35ed1e69c8cad0525df8fac22323/src/int.cr#L530) forInt#upto`
  • Int#downto with block
  • Range::StepIterator for Range#step
  • Range#step with block

Most helpful comment

Number#step is not a real solution: a) it only works on numbers b) exclusiveness through -1 only works for integer steps.

I'd like to have it more generalized, that you can take whatever value implements #pred and/or #succ to create ascending and descending ranges.
This does not have to be implemented through Range, if that class is to stay a strictly mathematical interval. Although, for example ranges in Scala and Python can also be descending.

What I have in mind is, if you want to create a sequence from a start element to end element, it should not be limited to start < end. There are plenty of situations where you'd want it the other way around. And not necessarily limited to integers.

I think it should be similar to define a sequence of e.g. characters from g to a as from a to g - or strings or anything else that can form sequences.

sequence('a', 'g').step(3).to_a # => ['a', 'd', 'g']
sequence('g', 'a').step(3).to_a # why would this not be ['g', 'd', 'a'] ?

I'm calling it sequence to not disturbed the argument by a debate if this functionality should be part of Range or something else. The first line would be working if sequence is Range.new.

The basis for this are already there, but the #pred method to access predecessors is only used in Range#reverse_each. This method is no general solution because it has a fixed step size and if it did it'd still be tedious to check if start > stop, and if so, switch values of start and stop and remember to use #reverse_each if you need the values from the range in the required descending order.
I'd like there to be a way to create a sequence from a start element to an end element in a general way, without having to deal with the direction of this and with the possibility to use a custom step size.

All 5 comments

Range goes up. Use Number#step. If you need it exclusive, just subtract -1.

Number#step is not a real solution: a) it only works on numbers b) exclusiveness through -1 only works for integer steps.

I'd like to have it more generalized, that you can take whatever value implements #pred and/or #succ to create ascending and descending ranges.
This does not have to be implemented through Range, if that class is to stay a strictly mathematical interval. Although, for example ranges in Scala and Python can also be descending.

What I have in mind is, if you want to create a sequence from a start element to end element, it should not be limited to start < end. There are plenty of situations where you'd want it the other way around. And not necessarily limited to integers.

I think it should be similar to define a sequence of e.g. characters from g to a as from a to g - or strings or anything else that can form sequences.

sequence('a', 'g').step(3).to_a # => ['a', 'd', 'g']
sequence('g', 'a').step(3).to_a # why would this not be ['g', 'd', 'a'] ?

I'm calling it sequence to not disturbed the argument by a debate if this functionality should be part of Range or something else. The first line would be working if sequence is Range.new.

The basis for this are already there, but the #pred method to access predecessors is only used in Range#reverse_each. This method is no general solution because it has a fixed step size and if it did it'd still be tedious to check if start > stop, and if so, switch values of start and stop and remember to use #reverse_each if you need the values from the range in the required descending order.
I'd like there to be a way to create a sequence from a start element to an end element in a general way, without having to deal with the direction of this and with the possibility to use a custom step size.

@asterite Why Range is supposed to be only ascending? Descending ranges have valid use cases as well, and IMO there's no reason to handicap Range class in such a way.

@asterite hmm, are we ready to discuss the topic again after a year break? :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cjgajard picture cjgajard  路  3Comments

Papierkorb picture Papierkorb  路  3Comments

ArthurZ picture ArthurZ  路  3Comments

oprypin picture oprypin  路  3Comments

asterite picture asterite  路  3Comments