From b6c519e95d8693d511b5afa125efa4400ebff484 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Sat, 2 Jan 2021 00:05:30 -0500 Subject: [PATCH 1/6] range(start => stop, length) --- base/range.jl | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/base/range.jl b/base/range.jl index b275e95c7c266..14184a44da967 100644 --- a/base/range.jl +++ b/base/range.jl @@ -94,6 +94,44 @@ range(start; length::Union{Integer,Nothing}=nothing, stop=nothing, step=nothing) range(start, stop; length::Union{Integer,Nothing}=nothing, step=nothing) = _range2(start, step, stop, length) +""" + range(start => stop[, length=2]) + +Given a `start` to `stop` [`Pair`](@ref) and an optional integer `length`, +construct a range iterator of `length` elements whose first element is `start` +and last element is `stop`. If `length` is not provided, the range will consist +of two elements. + +This is distinct from `start:stop` where `stop` may not be the last +element. Also, in this form `step` is not assumed to be 1. `length` cannot be +`nothing`. + +No keywords are accepted when `start` and `stop` are provided as a [`Pair`](@ref). + +Special care is taken to ensure intermediate values are computed rationally. +To avoid this induced overhead, see the [`LinRange`](@ref) constructor. + +!!! compat "Julia 1.7" + Providing `start => stop` as a `Pair` requires at least Julia 1.7. + +# Examples +```jldoctest +julia> range(1 => 5, 3) +1.0:2.0:5.0 + +julia> range(0 => 100, 101) +0.0:1.0:100.0) + +julia> range(1 => 10) +1.0:9.0:10.0 + +julia> range(1 => 1.5) +1.0:0.5:1.5 +``` +""" +range(start_stop::Pair, length::Integer=2) = + _range(start_stop.first, nothing, start_stop.second, length) + _range2(start, ::Nothing, stop, ::Nothing) = throw(ArgumentError("At least one of `length` or `step` must be specified")) From c5674514de341acb31d947deb3ddc4f4fff75010 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Sat, 2 Jan 2021 06:59:50 -0500 Subject: [PATCH 2/6] range(start => stop, length) fix + expand doctest --- base/range.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/base/range.jl b/base/range.jl index 14184a44da967..23523dbc95459 100644 --- a/base/range.jl +++ b/base/range.jl @@ -120,13 +120,19 @@ julia> range(1 => 5, 3) 1.0:2.0:5.0 julia> range(0 => 100, 101) -0.0:1.0:100.0) +0.0:1.0:100.0 julia> range(1 => 10) 1.0:9.0:10.0 julia> range(1 => 1.5) 1.0:0.5:1.5 + +julia> range(5 => 3) +5.0:-2.0:3.0 + +julia> range(5 => 3, 3) +5.0:-1.0:3.0 ``` """ range(start_stop::Pair, length::Integer=2) = From 9ba4b4f78740e24d352e8f29da35b01bfc2da703 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Sun, 3 Jan 2021 11:07:57 -0500 Subject: [PATCH 3/6] range(start => stop, length) Remove default length --- base/range.jl | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/base/range.jl b/base/range.jl index 23523dbc95459..1fa57e1cde208 100644 --- a/base/range.jl +++ b/base/range.jl @@ -95,16 +95,15 @@ range(start, stop; length::Union{Integer,Nothing}=nothing, step=nothing) = _range2(start, step, stop, length) """ - range(start => stop[, length=2]) + range(start => stop, length) -Given a `start` to `stop` [`Pair`](@ref) and an optional integer `length`, +Given a `start` to `stop` [`Pair`](@ref) and an integer `length`, construct a range iterator of `length` elements whose first element is `start` -and last element is `stop`. If `length` is not provided, the range will consist -of two elements. +and last element is `stop`. This is distinct from `start:stop` where `stop` may not be the last -element. Also, in this form `step` is not assumed to be 1. `length` cannot be -`nothing`. +element. Also, in this form `step` is not assumed to be 1. `length` must be an +`Integer` and cannot be `nothing`. No keywords are accepted when `start` and `stop` are provided as a [`Pair`](@ref). @@ -122,22 +121,16 @@ julia> range(1 => 5, 3) julia> range(0 => 100, 101) 0.0:1.0:100.0 -julia> range(1 => 10) -1.0:9.0:10.0 - -julia> range(1 => 1.5) -1.0:0.5:1.5 - -julia> range(5 => 3) -5.0:-2.0:3.0 - julia> range(5 => 3, 3) 5.0:-1.0:3.0 ``` """ -range(start_stop::Pair, length::Integer=2) = +range(start_stop::Pair, length::Integer) = _range(start_stop.first, nothing, start_stop.second, length) +range(start_stop::Pair) = + throw(ArgumentError("`length` must be specified after $start_stop")) + _range2(start, ::Nothing, stop, ::Nothing) = throw(ArgumentError("At least one of `length` or `step` must be specified")) From 83f5d534cb0a1b3a84d5e5cc0ca22622be7ff1b0 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Sun, 3 Jan 2021 12:03:58 -0500 Subject: [PATCH 4/6] range(start => stop, length) Provide userful error if length is nothing --- base/range.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/range.jl b/base/range.jl index 1fa57e1cde208..5b95de5da7f44 100644 --- a/base/range.jl +++ b/base/range.jl @@ -128,7 +128,7 @@ julia> range(5 => 3, 3) range(start_stop::Pair, length::Integer) = _range(start_stop.first, nothing, start_stop.second, length) -range(start_stop::Pair) = +range(start_stop::Pair, length::Nothing = nothing) = throw(ArgumentError("`length` must be specified after $start_stop")) _range2(start, ::Nothing, stop, ::Nothing) = From 042ba38898bd3a10e85630fc0421a618432bee6f Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Sun, 3 Jan 2021 18:07:38 -0500 Subject: [PATCH 5/6] Add tests for range(start => stop, length) --- test/ranges.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/ranges.jl b/test/ranges.jl index 6fcfc18b50529..3323e22ddeea0 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1610,6 +1610,18 @@ end @test_throws ArgumentError range(1, 100) end +@testset "range with start => stop Pair argument" begin + for starts in [-1, 0, 1, 10] + for stops in [-2, 0, 2, 100] + for lengths in [2, 10, 100] + @test range( starts => stops, lengths) === range(starts, stops; length = lengths) + end + end + end + @test_throws ArgumentError range( 1 => 5 ) + @test_throws ArgumentError range( -1 => -5 , nothing ) +end + @testset "Reverse empty ranges" begin @test reverse(1:0) === 0:-1:1 @test reverse(Base.OneTo(0)) === 0:-1:1 From fbb23ffaaef26eea826fa320084f05f793ecdd5e Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Mon, 4 Jan 2021 13:18:38 -0500 Subject: [PATCH 6/6] Update base/range.jl Co-authored-by: Matt Bauman --- base/range.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/base/range.jl b/base/range.jl index 5b95de5da7f44..da9501856dc29 100644 --- a/base/range.jl +++ b/base/range.jl @@ -101,11 +101,10 @@ Given a `start` to `stop` [`Pair`](@ref) and an integer `length`, construct a range iterator of `length` elements whose first element is `start` and last element is `stop`. -This is distinct from `start:stop` where `stop` may not be the last -element. Also, in this form `step` is not assumed to be 1. `length` must be an -`Integer` and cannot be `nothing`. - -No keywords are accepted when `start` and `stop` are provided as a [`Pair`](@ref). +In contrast to the [`:`](@ref) range syntaxes `start:stop` and `start:step:stop`, +this method ensures the endpoints are hit exactly and a `length` must be provided. +The `step` is always computed based upon the provided endpoints and length. +In contrast to the other `range` methods, no keyword arguments are accepted. Special care is taken to ensure intermediate values are computed rationally. To avoid this induced overhead, see the [`LinRange`](@ref) constructor.