@@ -47,25 +47,16 @@ function _colon(start::T, step, stop::T) where T
47
47
end
48
48
49
49
"""
50
- range(start[, stop]; length, stop, step=1)
50
+ range(start, stop; length, step)
51
+ range(start; length, stop, step)
52
+ range(;start, length, stop, step)
51
53
52
- Given a starting value, construct a range either by length or from `start` to `stop`,
53
- optionally with a given step (defaults to 1, a [`UnitRange`](@ref)).
54
- One of `length` or `stop` is required. If `length`, `stop`, and `step` are all specified, they must agree.
55
-
56
- If `length` and `stop` are provided and `step` is not, the step size will be computed
57
- automatically such that there are `length` linearly spaced elements in the range.
58
-
59
- If `step` and `stop` are provided and `length` is not, the overall range length will be computed
60
- automatically such that the elements are `step` spaced.
61
-
62
- Special care is taken to ensure intermediate values are computed rationally.
63
- To avoid this induced overhead, see the [`LinRange`](@ref) constructor.
64
-
65
- `stop` may be specified as either a positional or keyword argument.
66
-
67
- !!! compat "Julia 1.1"
68
- `stop` as a positional argument requires at least Julia 1.1.
54
+ Construct a specialized array with evenly spaced elements and optimized storage (an [`AbstractRange`](@ref)) from the arguments.
55
+ Mathematically a range is uniquely determined by any three of `start`, `step`, `stop` and `length`.
56
+ Valid invocations of range are:
57
+ * Call `range` with any three of `start`, `step`, `stop`, `length`.
58
+ * Call `range` with two of `start`, `stop`, `length`. In this case `step` will be assumed
59
+ to be one. If both arguments are Integers, a [`UnitRange`](@ref) will be returned.
69
60
70
61
# Examples
71
62
```jldoctest
@@ -86,51 +77,139 @@ julia> range(1, 10, length=101)
86
77
87
78
julia> range(1, 100, step=5)
88
79
1:5:96
80
+
81
+ julia> range(stop=10, length=5)
82
+ 6:10
83
+
84
+ julia> range(stop=10, step=1, length=5)
85
+ 6:1:10
86
+
87
+ julia> range(start=1, step=1, stop=10)
88
+ 1:1:10
89
89
```
90
+ If `length` is not specified and `stop - start` is not an integer multiple of `step`, a range that ends before `stop` will be produced.
91
+ ```jldoctest
92
+ julia> range(1, 3.5, step=2)
93
+ 1.0:2.0:3.0
94
+ ```
95
+
96
+ Special care is taken to ensure intermediate values are computed rationally.
97
+ To avoid this induced overhead, see the [`LinRange`](@ref) constructor.
98
+
99
+ Both `start` and `stop` may be specified as either a positional or keyword arguments.
100
+ If both are specified as positional arguments, one of `step` or `length` must also be provided.
101
+
102
+ !!! compat "Julia 1.1"
103
+ `stop` as a positional argument requires at least Julia 1.1.
104
+
105
+ !!! compat "Julia 1.7"
106
+ `start` as a keyword argument requires at least Julia 1.7.
90
107
"""
91
- range (start; length:: Union{Integer,Nothing} = nothing , stop= nothing , step= nothing ) =
108
+ function range end
109
+
110
+ range (start; stop= nothing , length:: Union{Integer,Nothing} = nothing , step= nothing ) =
92
111
_range (start, step, stop, length)
93
112
94
- range (start, stop; length:: Union{Integer,Nothing} = nothing , step= nothing ) =
95
- _range2 (start, step, stop, length)
113
+ function range (start, stop; length:: Union{Integer,Nothing} = nothing , step= nothing )
114
+ # For code clarity, the user must pass step or length
115
+ # See https://github.com/JuliaLang/julia/pull/28708#issuecomment-420034562
116
+ if step === length === nothing
117
+ msg = """
118
+ Neither `step` nor `length` was provided. To fix this do one of the following:
119
+ * Pass one of them
120
+ * Use `$(start) :$(stop) `
121
+ * Use `range($start , stop=$stop )`
122
+ """
123
+ throw (ArgumentError (msg))
124
+ end
125
+ _range (start, step, stop, length)
126
+ end
96
127
97
- _range2 ( start, :: Nothing , stop, :: Nothing ) =
98
- throw ( ArgumentError ( " At least one of `length` or ` step` must be specified " ) )
128
+ range (; start= nothing , stop= nothing , length :: Union{Integer, Nothing} = nothing , step = nothing ) =
129
+ _range (start, step, stop, length )
99
130
100
- _range2 (start, step, stop, length) = _range (start, step, stop, length)
131
+ _range (start:: Nothing , step:: Nothing , stop:: Nothing , len:: Nothing ) = range_error (start, step, stop, len)
132
+ _range (start:: Nothing , step:: Nothing , stop:: Nothing , len:: Any ) = range_error (start, step, stop, len)
133
+ _range (start:: Nothing , step:: Nothing , stop:: Any , len:: Nothing ) = range_error (start, step, stop, len)
134
+ _range (start:: Nothing , step:: Nothing , stop:: Any , len:: Any ) = range_stop_length (stop, len)
135
+ _range (start:: Nothing , step:: Any , stop:: Nothing , len:: Nothing ) = range_error (start, step, stop, len)
136
+ _range (start:: Nothing , step:: Any , stop:: Nothing , len:: Any ) = range_error (start, step, stop, len)
137
+ _range (start:: Nothing , step:: Any , stop:: Any , len:: Nothing ) = range_error (start, step, stop, len)
138
+ _range (start:: Nothing , step:: Any , stop:: Any , len:: Any ) = range_step_stop_length (step, stop, len)
139
+ _range (start:: Any , step:: Nothing , stop:: Nothing , len:: Nothing ) = range_error (start, step, stop, len)
140
+ _range (start:: Any , step:: Nothing , stop:: Nothing , len:: Any ) = range_start_length (start, len)
141
+ _range (start:: Any , step:: Nothing , stop:: Any , len:: Nothing ) = range_start_stop (start, stop)
142
+ _range (start:: Any , step:: Nothing , stop:: Any , len:: Any ) = range_start_stop_length (start, stop, len)
143
+ _range (start:: Any , step:: Any , stop:: Nothing , len:: Nothing ) = range_error (start, step, stop, len)
144
+ _range (start:: Any , step:: Any , stop:: Nothing , len:: Any ) = range_start_step_length (start, step, len)
145
+ _range (start:: Any , step:: Any , stop:: Any , len:: Nothing ) = range_start_step_stop (start, step, stop)
146
+ _range (start:: Any , step:: Any , stop:: Any , len:: Any ) = range_error (start, step, stop, len)
147
+
148
+ range_stop_length (stop, length) = (stop- length+ 1 ): stop
149
+
150
+ range_step_stop_length (step, stop, length) = reverse (range_start_step_length (stop, - step, length))
151
+
152
+ range_start_length (a:: Real , len:: Integer ) = UnitRange {typeof(a)} (a, oftype (a, a+ len- 1 ))
153
+ range_start_length (a:: AbstractFloat , len:: Integer ) = range_start_step_length (a, oftype (a, 1 ), len)
154
+ range_start_length (a, len:: Integer ) = range_start_step_length (a, oftype (a- a, 1 ), len)
155
+
156
+ range_start_stop (start, stop) = start: stop
157
+
158
+ function range_start_step_length (a:: AbstractFloat , step:: AbstractFloat , len:: Integer )
159
+ range_start_step_length (promote (a, step)... , len)
160
+ end
101
161
102
- # Range from start to stop: range(a, [ step=s,] stop=b), no length
103
- _range (start, step, stop, :: Nothing ) = (:)(start , step, stop )
104
- _range (start, :: Nothing , stop, :: Nothing ) = (:)(start, stop)
162
+ function range_start_step_length (a :: Real , step:: AbstractFloat , len :: Integer )
163
+ range_start_step_length ( float (a) , step, len )
164
+ end
105
165
106
- # Range of a given length: range(a, [step=s,] length=l), no stop
107
- _range (a:: Real , :: Nothing , :: Nothing , len:: Integer ) = UnitRange {typeof(a)} (a, oftype (a, a+ len- 1 ))
108
- _range (a:: AbstractFloat , :: Nothing , :: Nothing , len:: Integer ) = _range (a, oftype (a, 1 ), nothing , len)
109
- _range (a:: AbstractFloat , st:: AbstractFloat , :: Nothing , len:: Integer ) = _range (promote (a, st)... , nothing , len)
110
- _range (a:: Real , st:: AbstractFloat , :: Nothing , len:: Integer ) = _range (float (a), st, nothing , len)
111
- _range (a:: AbstractFloat , st:: Real , :: Nothing , len:: Integer ) = _range (a, float (st), nothing , len)
112
- _range (a, :: Nothing , :: Nothing , len:: Integer ) = _range (a, oftype (a- a, 1 ), nothing , len)
166
+ function range_start_step_length (a:: AbstractFloat , step:: Real , len:: Integer )
167
+ range_start_step_length (a, float (step), len)
168
+ end
113
169
114
- _range (a:: T , step:: T , :: Nothing , len:: Integer ) where {T <: AbstractFloat } =
170
+ function range_start_step_length (a:: T , step:: T , len:: Integer ) where {T <: AbstractFloat }
115
171
_rangestyle (OrderStyle (T), ArithmeticStyle (T), a, step, len)
116
- _range (a:: T , step, :: Nothing , len:: Integer ) where {T} =
172
+ end
173
+
174
+ function range_start_step_length (a:: T , step, len:: Integer ) where {T}
117
175
_rangestyle (OrderStyle (T), ArithmeticStyle (T), a, step, len)
176
+ end
177
+
118
178
_rangestyle (:: Ordered , :: ArithmeticWraps , a:: T , step:: S , len:: Integer ) where {T,S} =
119
179
StepRange {typeof(a+zero(step)),S} (a, step, a+ step* (len- 1 ))
120
180
_rangestyle (:: Any , :: Any , a:: T , step:: S , len:: Integer ) where {T,S} =
121
181
StepRangeLen {typeof(a+zero(step)),T,S} (a, step, len)
122
182
123
- # Malformed calls
124
- _range (start, step, :: Nothing , :: Nothing ) = # range(a, step=s)
125
- throw (ArgumentError (" At least one of `length` or `stop` must be specified" ))
126
- _range (start, :: Nothing , :: Nothing , :: Nothing ) = # range(a)
127
- throw (ArgumentError (" At least one of `length` or `stop` must be specified" ))
128
- _range (:: Nothing , :: Nothing , :: Nothing , :: Nothing ) = # range(nothing)
129
- throw (ArgumentError (" At least one of `length` or `stop` must be specified" ))
130
- _range (start:: Real , step:: Real , stop:: Real , length:: Integer ) = # range(a, step=s, stop=b, length=l)
131
- throw (ArgumentError (" Too many arguments specified; try passing only one of `stop` or `length`" ))
132
- _range (:: Nothing , :: Nothing , :: Nothing , :: Integer ) = # range(nothing, length=l)
133
- throw (ArgumentError (" Can't start a range at `nothing`" ))
183
+ range_start_step_stop (start, step, stop) = start: step: stop
184
+
185
+ function range_error (start, step, stop, length)
186
+ hasstart = start != = nothing
187
+ hasstep = step != = nothing
188
+ hasstop = stop != = nothing
189
+ haslength = start != = nothing
190
+
191
+ hint = if hasstart && hasstep && hasstop && haslength
192
+ " Try specifying only three arguments"
193
+ elseif ! hasstop && ! haslength
194
+ " At least one of `length` or `stop` must be specified."
195
+ elseif ! hasstep && ! haslength
196
+ " At least one of `length` or `step` must be specified."
197
+ elseif ! hasstart && ! hasstop
198
+ " At least one of `start` or `stop` must be specified."
199
+ else
200
+ " Try specifying more arguments."
201
+ end
202
+
203
+ msg = """
204
+ Cannot construct range from arguments:
205
+ start = $start
206
+ step = $step
207
+ stop = $stop
208
+ length = $length
209
+ $hint
210
+ """
211
+ throw (ArgumentError (msg))
212
+ end
134
213
135
214
# # 1-dimensional ranges ##
136
215
@@ -434,13 +513,13 @@ function LinRange(start, stop, len::Integer)
434
513
LinRange {T} (start, stop, len)
435
514
end
436
515
437
- function _range (start:: T , :: Nothing , stop:: S , len:: Integer ) where {T,S}
516
+ function range_start_stop_length (start:: T , stop:: S , len:: Integer ) where {T,S}
438
517
a, b = promote (start, stop)
439
- _range (a, nothing , b, len)
518
+ range_start_stop_length (a , b, len)
440
519
end
441
- _range (start:: T , :: Nothing , stop:: T , len:: Integer ) where {T<: Real } = LinRange {T} (start, stop, len)
442
- _range (start:: T , :: Nothing , stop:: T , len:: Integer ) where {T} = LinRange {T} (start, stop, len)
443
- _range (start:: T , :: Nothing , stop:: T , len:: Integer ) where {T<: Integer } =
520
+ range_start_stop_length (start:: T , stop:: T , len:: Integer ) where {T<: Real } = LinRange {T} (start, stop, len)
521
+ range_start_stop_length (start:: T , stop:: T , len:: Integer ) where {T} = LinRange {T} (start, stop, len)
522
+ range_start_stop_length (start:: T , stop:: T , len:: Integer ) where {T<: Integer } =
444
523
_linspace (float (T), start, stop, len)
445
524
# # for Float16, Float32, and Float64 we hit twiceprecision.jl to lift to higher precision StepRangeLen
446
525
# for all other types we fall back to a plain old LinRange
0 commit comments