Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArgumentError when adding opposed ranges #28072

Closed
staticfloat opened this issue Jul 12, 2018 · 3 comments
Closed

ArgumentError when adding opposed ranges #28072

staticfloat opened this issue Jul 12, 2018 · 3 comments

Comments

@staticfloat
Copy link
Member

This is magical:

julia> (1:4) .+ (1:4)
2:2:8

I love that we can be so clever. Unfortunately, it can come back and bite us sometimes:

julia> (1:4) .+ (4:-1:1)
ERROR: ArgumentError: step cannot be zero
Stacktrace:
 [1] steprange_last(::Int64, ::Int64, ::Int64) at ./range.jl:137
 [2] Type at ./range.jl:127 [inlined]
 [3] _rangestyle at ./range.jl:90 [inlined]
 [4] _range at ./range.jl:88 [inlined]
 [5] #range#29 at ./range.jl:73 [inlined]
 [6] #range at ./none:0 [inlined]
 [7] + at ./range.jl:890 [inlined]
 [8] broadcasted at ./broadcast.jl:969 [inlined]
 [9] broadcasted(::Function, ::UnitRange{Int64}, ::StepRange{Int64,Int64}) at ./broadcast.jl:1146
 [10] top-level scope at none:0

This is very clearly due to us trying to construct the range [5:0:5] that is supposed to be 4 elements long, but unfortunately we lose that information since it's supposed to be implicit in start, stop and step. The range abstraction doesn't know what to do here, and so we fail out.

Likely we should have a fallback for this cleverness that simply constructs an array, but I don't like the idea that Range + Range = Union{Range,Vector}. I would rather that we had a different abstraction similar to UnitRange, perhaps called ConstantRange or something, that takes in a value and an explicit length so that we don't have some poor fool who is doing something like:

for stepA in 1:3
  for stepB in -3:-1
    x = (1:stepA:stepA*1000000000) .+ (stepB*-1000000000:stepB:1)
    println(sum(x))
  end
end

and have his memory blow up for very particular values of stepA and stepB. (Note that the code sample above, fanciful as it is, fails on current versions of Julia because it cannot finish the summation of the two ranges)

@mbauman
Copy link
Member

mbauman commented Jul 12, 2018

We do have such a range construct that can handle 0 steps:

julia> StepRangeLen(5,0,4)
5:0:5

julia> StepRangeLen(5,0,4) == collect(1:4) .+ (4:-1:1)
true

The reason we don't return such a thing when adding two StepRanges is because then it'd make all such additions type-unstable — and StepRanges themselves cannot represent such a beast since they compute their length. As such, my preferred angle of approach here wouldn't be to add a special case for addition of canceling StepRanges, but, rather, looking into the performance of StepRangeLens and trying to get that to match (and then replace) StepRanges. This is really performance-sensitive, though, and I don't know how feasible it might be or how far away we are.

Note:

julia> (1.0:4) .+ (4.0:-1:1)
5.0:0.0:5.0

julia> (1.0:4) .+ (4.0:-1:1) == [5, 5, 5, 5]
true

@mbauman
Copy link
Member

mbauman commented Jul 12, 2018

Duplicate of #10391

@mbauman mbauman marked this as a duplicate of #10391 Jul 12, 2018
@mbauman mbauman closed this as completed Jul 12, 2018
@staticfloat
Copy link
Member Author

Thanks for the great explanation Matt!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants