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

range(; stop) and range(; length). Single keyword args only. Single pos arg not allowed. #39241

Merged
merged 3 commits into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ New library features
Standard library changes
------------------------

* `range` accepts either `stop` or `length` as a sole keyword argument ([#39241])
* The `length` function on certain ranges of certain specific element types no longer checks for integer
overflow in most cases. The new function `checked_length` is now available, which will try to use checked
arithmetic to error if the result may be wrapping. Or use a package such as SaferIntegers.jl when
Expand Down
41 changes: 39 additions & 2 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ Valid invocations of range are:
* Call `range` with any three of `start`, `step`, `stop`, `length`.
* Call `range` with two of `start`, `stop`, `length`. In this case `step` will be assumed
to be one. If both arguments are Integers, a [`UnitRange`](@ref) will be returned.
* Call `range` with one of `stop` or `length`. `start` and `step` will be assumed to be one.

See Extended Help for additional details on the returned type.

# Examples
```jldoctest
Expand Down Expand Up @@ -87,6 +90,15 @@ julia> range(stop=10, step=1, length=5)

julia> range(start=1, step=1, stop=10)
1:1:10

julia> range(; length = 10)
Base.OneTo(10)

julia> range(; stop = 6)
Base.OneTo(6)

julia> range(; stop = 6.5)
1.0:1.0:6.0
```
If `length` is not specified and `stop - start` is not an integer multiple of `step`, a range that ends before `stop` will be produced.
```jldoctest
Expand All @@ -103,6 +115,23 @@ To avoid this induced overhead, see the [`LinRange`](@ref) constructor.
!!! compat "Julia 1.7"
The versions without keyword arguments and `start` as a keyword argument
require at least Julia 1.7.

!!! compat "Julia 1.8"
The versions with `stop` as a sole keyword argument,
or `length` as a sole keyword argument require at least Julia 1.8.


# Extended Help

`range` will produce a `Base.OneTo` when the arguments are Integers and
* Only `length` is provided
* Only `stop` is provided

`range` will produce a `UnitRange` when the arguments are Integers and
* Only `start` and `stop` are provided
* Only `length` and `stop` are provided

A `UnitRange` is not produced if `step` is provided even if specified as one.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the whole extended help section, it is very clear.

"""
function range end

Expand All @@ -115,8 +144,8 @@ range(;start=nothing, stop=nothing, length::Union{Integer, Nothing}=nothing, ste
_range(start, step, stop, length)

_range(start::Nothing, step::Nothing, stop::Nothing, len::Nothing) = range_error(start, step, stop, len)
_range(start::Nothing, step::Nothing, stop::Nothing, len::Any ) = range_error(start, step, stop, len)
_range(start::Nothing, step::Nothing, stop::Any , len::Nothing) = range_error(start, step, stop, len)
_range(start::Nothing, step::Nothing, stop::Nothing, len::Any ) = range_length(len)
_range(start::Nothing, step::Nothing, stop::Any , len::Nothing) = range_stop(stop)
_range(start::Nothing, step::Nothing, stop::Any , len::Any ) = range_stop_length(stop, len)
_range(start::Nothing, step::Any , stop::Nothing, len::Nothing) = range_error(start, step, stop, len)
_range(start::Nothing, step::Any , stop::Nothing, len::Any ) = range_error(start, step, stop, len)
Expand All @@ -131,6 +160,14 @@ _range(start::Any , step::Any , stop::Nothing, len::Any ) = range_start
_range(start::Any , step::Any , stop::Any , len::Nothing) = range_start_step_stop(start, step, stop)
_range(start::Any , step::Any , stop::Any , len::Any ) = range_error(start, step, stop, len)

# Length as the only argument
range_length(len::Integer) = OneTo(len)

# Stop as the only argument
range_stop(stop) = range_start_stop(oneunit(stop), stop)
range_stop(stop::Integer) = range_length(stop)

# Stop and length as the only argument
range_stop_length(a::Real, len::Integer) = UnitRange{typeof(a)}(oftype(a, a-len+1), a)
range_stop_length(a::AbstractFloat, len::Integer) = range_step_stop_length(oftype(a, 1), a, len)
range_stop_length(a, len::Integer) = range_step_stop_length(oftype(a-a, 1), a, len)
Expand Down
20 changes: 19 additions & 1 deletion test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ using Base.Checked: checked_length
# the next ones use ==, because it changes the eltype
@test r == range(first(r), last(r), length(r) )
@test r == range(start=first(r), stop=last(r), length=length(r))
@test r === range( stop=last(r), length=length(r))

r = 1:5
o = Base.OneTo(5)
let start=first(r), step=step(r), stop=last(r), length=length(r)
@test o === range(; stop )
@test o === range(; length)
@test r === range(; start, stop )
@test r === range(; stop, length)
# the next three lines uses ==, because it changes the eltype
@test r == range(; start, stop, length)
@test r == range(; start, step, length)
@test r == range(; stop=Float64(stop))
end

for T = (Int8, Rational{Int16}, UInt32, Float64, Char)
@test typeof(range(start=T(5), length=3)) === typeof(range(stop=T(5), length=3))
Expand Down Expand Up @@ -1508,8 +1522,12 @@ end
@test_throws ArgumentError range(1)
@test_throws ArgumentError range(nothing)
@test_throws ArgumentError range(1, step=4)
@test_throws ArgumentError range(nothing, length=2)
@test_throws ArgumentError range(; step=1, length=6)
@test_throws ArgumentError range(; step=2, stop=7.5)
@test_throws ArgumentError range(1.0, step=0.25, stop=2.0, length=5)
@test_throws ArgumentError range(; stop=nothing)
@test_throws ArgumentError range(; length=nothing)
@test_throws TypeError range(; length=5.5)
end

@testset "issue #23300#issuecomment-371575548" begin
Expand Down