Project

General

Profile

Actions

Bug #15518

closed

good old Infinite range notation behavior

Added by sakuro (Sakuro OZAWA) over 5 years ago. Updated about 5 years ago.

Status:
Closed
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?

Actions #1

Updated by sakuro (Sakuro OZAWA) over 5 years ago

  • Description updated (diff)
Actions #2

Updated by sakuro (Sakuro OZAWA) over 5 years ago

  • Description updated (diff)
Actions #3

Updated by sakuro (Sakuro OZAWA) over 5 years ago

  • Description updated (diff)

Updated by sawa (Tsuyoshi Sawada) over 5 years 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 5 years ago

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

Updated by mrkn (Kenta Murata) over 5 years 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.

Actions #7

Updated by mrkn (Kenta Murata) about 5 years 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) about 5 years 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.

Actions #9

Updated by naruse (Yui NARUSE) about 5 years 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) about 5 years 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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0