Project

General

Profile

Bug #15518

good old Infinite range notation behavior

Added by sakuro (Sakuro OZAWA) over 1 year ago. Updated over 1 year ago.

Status:
Closed
Priority:
Normal
Target version:
-
ruby -v:
ruby 2.6.0p0 (2018-12-25 revision 66547) [x86_64-darwin18]
[ruby-core:90937]

Description

Ruby 2.5.3's behavior

# without step, it produces integer sequence
(1..Float::INFINITY).first(10) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# with step, it produces floats instead of integers
(1..Float::INFINITY).step(1).first(10) #=> [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]

Ruby 2.6.0's behavior

# endless range
(1..).first(10) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# with step, all numbers are integer now
(1..).step(1).first(10) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# old idiom with Float::INFINITY
(1..Float::INFINITY).first(10) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(1..Float::INFINITY).step(1).first(10) #=> FloatDomainError (Infinity)

Which are intended change and which are not?

#1

Updated by sakuro (Sakuro OZAWA) over 1 year ago

  • Description updated (diff)
#2

Updated by sakuro (Sakuro OZAWA) over 1 year ago

  • Description updated (diff)
#3

Updated by sakuro (Sakuro OZAWA) over 1 year ago

  • Description updated (diff)

Updated by sawa (Tsuyoshi Sawada) over 1 year ago

With finite ranges, the class of the elements in the return value seems to reflect the class of the step parameter as shown below (although, I am not sure why the one with rational has 1 instead of (1/1). A bug, perhaps?):

(1..10).step(1).first(5) # => [1, 2, 3, 4, 5]
(1..10).step(1.0).first(5) # => [1.0, 2.0, 3.0, 4.0, 5.0]
(1..10).step(1/1r).first(5) #=> [1, (2/1), (3/1), (4/1), (5/1)]

Given that, without explicit step, the returned numbers are integers, it should be understood that the default step for a range is the integer 1:

(1..10).first(5) # => [1, 2, 3, 4, 5]

The above claim that the class of the number is determined by the step parameter (and nothing else, such as the end of range) is confirmed by the fact that a range ending with Float::INFINITY that has default step 1 returns integers (and not floats).

(1..Float::INFINITY).first(5) #=> [1, 2, 3, 4, 5]

Given the argument above, I think (1..Float::INFINITY).step(1).first(5) should return integers as follows:

(1..Float::INFINITY).step(1).first(5) # => [1, 2, 3, 4, 5]

Thus, to me, neither Ruby 2.5.3 nor Ruby 2.6.0's behavior makes sense.

Updated by mrkn (Kenta Murata) over 1 year ago

  • Assignee set to mrkn (Kenta Murata)
  • Status changed from Open to Assigned
#6

Updated by mrkn (Kenta Murata) over 1 year ago

At first, it isn't intentional behavior change. I should fix it, but I need to consider what is the correct behavior.

Ruby 2.5's behavior is given below:

$ RBENV_VERSION=2.5 irb --simple-prompt
>> (1 .. 10).first(5)
=> [1, 2, 3, 4, 5]
>> (1 .. 10.0).first(5)
=> [1, 2, 3, 4, 5]
>> (1.0 .. 10).first(5)
Traceback (most recent call last):
        4: from /Users/mrkn/.rbenv/versions/2.5/bin/irb:11:in `<main>'
        3: from (irb):3
        2: from (irb):3:in `first'
        1: from (irb):3:in `each'
TypeError (can't iterate from Float)
>> (1 .. 10r).first(5)
=> [1, 2, 3, 4, 5]
>> (1r .. 10).first(5)
Traceback (most recent call last):
        4: from /Users/mrkn/.rbenv/versions/2.5/bin/irb:11:in `<main>'
        3: from (irb):2
        2: from (irb):2:in `first'
        1: from (irb):2:in `each'
TypeError (can't iterate from Rational)
>> (1 .. 10).step(1).first(5)
=> [1, 2, 3, 4, 5]
>> (1 .. 10.0).step(1).first(5)
=> [1.0, 2.0, 3.0, 4.0, 5.0]
>> (1.0 .. 10).step(1).first(5)
=> [1.0, 2.0, 3.0, 4.0, 5.0]
>> (1 .. 10).step(1.0).first(5)
=> [1.0, 2.0, 3.0, 4.0, 5.0]
>> (1 .. 10r).step(1).first(5)
=> [1, 2, 3, 4, 5]
>> (1r .. 10).step(1).first(5)
=> [(1/1), (2/1), (3/1), (4/1), (5/1)]
>> (1 .. 10).step(1r).first(5)
=> [1, (2/1), (3/1), (4/1), (5/1)]

As you can see, the enumerator from Range#step is different from Range#each.
And, on Range#step, the result of the range with float components consists of Float values only while the result of the range with rational components does not.
The reason for this difference is that Range#step has the specialized implementation for the range with float components.

#7

Updated by mrkn (Kenta Murata) over 1 year ago

  • Status changed from Assigned to Closed

Applied in changeset trunk|r66947.


enumerator.c: fix arith_seq_first for Infinity

  • enumerator.c (arith_seq_first): fix for Float::INFINITY.

  • test/ruby/test_arithmetic_sequence.rb: add tests.

  • numeric.c (ruby_float_step_size): export for internal use.

  • internal.h: add prototype declaration of ruby_float_step_size.

[ruby-core:90937][Bug #15518]

Updated by mrkn (Kenta Murata) over 1 year ago

I fixed the case for ranges whose end is Float::INFINITY.
If you want to continue to discuss for the other cases, please create the new issue.

#9

Updated by naruse (Yui NARUSE) over 1 year ago

  • Backport changed from 2.4: UNKNOWN, 2.5: UNKNOWN, 2.6: UNKNOWN to 2.4: DONTNEED, 2.5: DONTNEED, 2.6: REQUIRED

Updated by naruse (Yui NARUSE) over 1 year ago

  • Backport changed from 2.4: DONTNEED, 2.5: DONTNEED, 2.6: REQUIRED to 2.4: DONTNEED, 2.5: DONTNEED, 2.6: DONE

ruby_2_6 r66949 merged revision(s) 66947.

Also available in: Atom PDF