From 314f3fd049904c2ff633d3ae95e7de947f421c68 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Wed, 13 Jan 2021 03:03:37 -0500 Subject: [PATCH 1/6] range: Assume start=1 when not given. Use OneTo --- base/range.jl | 43 ++++++++++++++++++++++++++++++++++++++----- test/ranges.jl | 16 ++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/base/range.jl b/base/range.jl index 6e5a86f9c863f..eef5f085d66cd 100644 --- a/base/range.jl +++ b/base/range.jl @@ -50,6 +50,7 @@ end range(start, stop; length, step) range(start; length, stop, step) range(;start, length, stop, step) + range(length::Integer) Construct a specialized array with evenly spaced elements and optimized storage (an [`AbstractRange`](@ref)) from the arguments. Mathematically a range is uniquely determined by any three of `start`, `step`, `stop` and `length`. @@ -57,6 +58,8 @@ 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 `step` and either `stop` or `length`. `start` will be assumed to be one. +* Call `range` with one of `stop` or `length`. `start` and `step` will be assumed to be one. # Examples ```jldoctest @@ -86,6 +89,15 @@ julia> range(stop=10, step=1, length=5) julia> range(start=1, step=1, stop=10) 1:1:10 + +julia> range(5) +Base.OneTo(5) + +julia> range(; length = 10) +Base.OneTo(10) + +julia> range(; stop = 5) +1:5 ``` 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 @@ -103,11 +115,27 @@ If both are specified as positional arguments, one of `step` or `length` must al `stop` as a positional argument requires at least Julia 1.1. !!! compat "Julia 1.7" - `start` as a keyword argument requires at least Julia 1.7. + Assuming `start` is one when not provided, + `start` as a keyword argument, or + `length` as a positional argument requires at least Julia 1.7. + +# 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 specified even if specified as one. """ function range end range(start; stop=nothing, length::Union{Integer,Nothing}=nothing, step=nothing) = + stop === length === step === nothing ? + range_stop(start) : _range(start, step, stop, length) function range(start, stop; length::Union{Integer,Nothing}=nothing, step=nothing) @@ -128,13 +156,15 @@ end range(;start=nothing, stop=nothing, length::Union{Integer, Nothing}=nothing, step=nothing) = _range(start, step, stop, length) +range(length::Integer) = OneTo(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 ) = OneTo(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) -_range(start::Nothing, step::Any , stop::Any , len::Nothing) = range_error(start, step, stop, len) +_range(start::Nothing, step::Any , stop::Nothing, len::Any ) = range_start_step_length(oneunit(step), step, len) +_range(start::Nothing, step::Any , stop::Any , len::Nothing) = range_start_step_stop(oneunit(stop), step, stop) _range(start::Nothing, step::Any , stop::Any , len::Any ) = range_step_stop_length(step, stop, len) _range(start::Any , step::Nothing, stop::Nothing, len::Nothing) = range_error(start, step, stop, len) _range(start::Any , step::Nothing, stop::Nothing, len::Any ) = range_start_length(start, len) @@ -145,6 +175,9 @@ _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) +range_stop(stop) = oneunit(stop):stop +range_stop(stop::Integer) = Base.OneTo(stop) + range_stop_length(stop, length) = (stop-length+1):stop range_step_stop_length(step, stop, length) = reverse(range_start_step_length(stop, -step, length)) diff --git a/test/ranges.jl b/test/ranges.jl index b9f77f211193d..992e7e5211cc7 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -17,6 +17,22 @@ # the next one uses ==, because it changes the eltype @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 = OneTo(5) + let start=first(r), step=step(r), stop=last(r), length=length(r) + @test o === range( length) + @test o === range(; stop ) + @test o === range(; length) + @test r === range(; start, stop ) + @test r === range(; stop, length) + # the next four uses ==, because it changes the eltype + @test r == range(; step, stop ) + @test r == range(; step, length) + @test r == range(; start, stop, length) + @test r == range(; start, step, length) + @test r == range(Float64(stop)) + end end end From 8b8718c9432a828022fc2872c3c60dc3575e18a3 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Wed, 13 Jan 2021 03:12:07 -0500 Subject: [PATCH 2/6] range: Document single positional argument as stop --- base/range.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/range.jl b/base/range.jl index eef5f085d66cd..29fdd1e8c014c 100644 --- a/base/range.jl +++ b/base/range.jl @@ -50,7 +50,7 @@ end range(start, stop; length, step) range(start; length, stop, step) range(;start, length, stop, step) - range(length::Integer) + range(stop) Construct a specialized array with evenly spaced elements and optimized storage (an [`AbstractRange`](@ref)) from the arguments. Mathematically a range is uniquely determined by any three of `start`, `step`, `stop` and `length`. From 94439224299f74cab551194b3a1018dd76f5b07a Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Wed, 13 Jan 2021 03:29:03 -0500 Subject: [PATCH 3/6] NEWS.md for range: `start=1`, one pos arg as stop --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index e4c88c5b25177..47f13faff56a4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -41,6 +41,7 @@ Standard library changes * `count` and `findall` now accept an `AbstractChar` argument to search for a character in a string ([#38675]). * `range` now supports `start` as an optional keyword argument ([#38041]). +* `range` now accepts a single positional argument as `stop` and may assume `start = 1` when `start` is not provided ([#39223]) * `islowercase` and `isuppercase` are now compliant with the Unicode lower/uppercase categories ([#38574]). * `iseven` and `isodd` functions now support non-`Integer` numeric types ([#38976]). * `escape_string` can now receive a collection of characters in the keyword From ea5e2811613a5441c6b7d7f6f5144a09c9700d7b Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Wed, 13 Jan 2021 09:52:35 -0500 Subject: [PATCH 4/6] Fix typo in ranges.jl test --- test/ranges.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ranges.jl b/test/ranges.jl index 992e7e5211cc7..4c5c23ef1a2fd 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -19,7 +19,7 @@ @test r === range( stop=last(r), length=length(r)) r = 1:5 - o = OneTo(5) + o = Base.OneTo(5) let start=first(r), step=step(r), stop=last(r), length=length(r) @test o === range( length) @test o === range(; stop ) From 5566ec1fb2b7addf804eb3b94495de907601cf47 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Wed, 13 Jan 2021 12:46:30 -0500 Subject: [PATCH 5/6] Clarify documentation --- base/range.jl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/base/range.jl b/base/range.jl index 29fdd1e8c014c..de41266923c31 100644 --- a/base/range.jl +++ b/base/range.jl @@ -56,10 +56,10 @@ Construct a specialized array with evenly spaced elements and optimized storage Mathematically a range is uniquely determined by any three of `start`, `step`, `stop` and `length`. 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 `step` and either `stop` or `length`. `start` will be assumed to be one. -* Call `range` with one of `stop` or `length`. `start` and `step` will be assumed to be one. +* Call `range` with fewer than three parameters. Either `stop` or `length` must be specified. +If not provided, `step` and/or `start` are assumed to be one in that order of precedence. + +See Extended Help for additional details on the returned type. # Examples ```jldoctest @@ -117,10 +117,13 @@ If both are specified as positional arguments, one of `step` or `length` must al !!! compat "Julia 1.7" Assuming `start` is one when not provided, `start` as a keyword argument, or - `length` as a positional argument requires at least Julia 1.7. + `stop` as a sole positional argument requires at least Julia 1.7. # Extended Help +If only `length` and `stop` are provided as arguments, `step` rather than `start` is assumed +to be one. + `range` will produce a `Base.OneTo` when the arguments are Integers and * Only `length` is provided * Only `stop` is provided @@ -129,7 +132,7 @@ If both are specified as positional arguments, one of `step` or `length` must al * Only `start` and `stop` are provided * Only `length` and `stop` are provided -A `UnitRange` is not produced if `step` is specified even if specified as one. +A `UnitRange` is not produced if `step` is provided even if specified as one. """ function range end From a63eeadcaa538708264e79c7bccdf89a1e74a069 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Wed, 13 Jan 2021 13:46:06 -0500 Subject: [PATCH 6/6] range: Address test failures --- base/range.jl | 9 ++++++--- test/ranges.jl | 2 -- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/base/range.jl b/base/range.jl index de41266923c31..39383e8b67d37 100644 --- a/base/range.jl +++ b/base/range.jl @@ -96,8 +96,11 @@ Base.OneTo(5) julia> range(; length = 10) Base.OneTo(10) -julia> range(; stop = 5) -1:5 +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 @@ -138,7 +141,7 @@ function range end range(start; stop=nothing, length::Union{Integer,Nothing}=nothing, step=nothing) = stop === length === step === nothing ? - range_stop(start) : + _range(nothing, nothing, start, nothing) : _range(start, step, stop, length) function range(start, stop; length::Union{Integer,Nothing}=nothing, step=nothing) diff --git a/test/ranges.jl b/test/ranges.jl index 4c5c23ef1a2fd..b7f2ee2a7225e 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1459,10 +1459,8 @@ end end @testset "Bad range calls" begin - @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(1.0, step=0.25, stop=2.0, length=5) end