Skip to content

Commit 0724bb8

Browse files
mbaumantkelman
authored andcommitted
Support using colon to omit a dimension in reshape (#19919)
* Support using colon to omit a dimension in reshape Closes #16790. * Add NEWS.md
1 parent 271b318 commit 0724bb8

File tree

4 files changed

+94
-33
lines changed

4 files changed

+94
-33
lines changed

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ Library improvements
186186
`cumprod`. Also known as a [scan](https://en.wikipedia.org/wiki/Prefix_sum)
187187
operation ([#18931]).
188188

189+
* `reshape` now allows specifying one dimension with a `Colon()` (`:`) for the new shape, in which case
190+
that dimension's length will be computed such that its product with all the other dimensions is equal
191+
to the length of the original array ([#19919]).
192+
189193
* New `titlecase` function, which capitalizes the first character of each word within a string ([#19469]).
190194

191195
* `any` and `all` now always short-circuit, and `mapreduce` never short-circuits ([#19543]).

base/docs/helpdb/Base.jl

-33
Original file line numberDiff line numberDiff line change
@@ -446,39 +446,6 @@ See also [`zeros`](@ref), [`similar`](@ref).
446446
"""
447447
ones
448448

449-
"""
450-
reshape(A, dims)
451-
452-
Create an array with the same data as the given array, but with different dimensions.
453-
454-
```jldoctest
455-
julia> A = collect(1:16)
456-
16-element Array{Int64,1}:
457-
1
458-
2
459-
3
460-
4
461-
5
462-
6
463-
7
464-
8
465-
9
466-
10
467-
11
468-
12
469-
13
470-
14
471-
15
472-
16
473-
474-
julia> reshape(A, (2, 8))
475-
2×8 Array{Int64,2}:
476-
1 3 5 7 9 11 13 15
477-
2 4 6 8 10 12 14 16
478-
```
479-
"""
480-
reshape
481-
482449
"""
483450
randsubseq!(S, A, p)
484451

base/reshapedarray.jl

+69
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,79 @@ start(R::ReshapedArrayIterator) = start(R.iter)
3636
end
3737
length(R::ReshapedArrayIterator) = length(R.iter)
3838

39+
"""
40+
reshape(A, dims...)
41+
reshape(A, dims)
42+
43+
Return an array with the same data as the given array, but with different dimensions.
44+
45+
The new dimensions may be specified either as a list of arguments or as a shape
46+
tuple. At most one dimension may be specified with a `:`, in which case its
47+
length is computed such that its product with all the specified dimensions is
48+
equal to the length of the original array A.
49+
50+
```jldoctest
51+
julia> A = collect(1:16)
52+
16-element Array{Int64,1}:
53+
1
54+
2
55+
3
56+
4
57+
5
58+
6
59+
7
60+
8
61+
9
62+
10
63+
11
64+
12
65+
13
66+
14
67+
15
68+
16
69+
70+
julia> reshape(A, (4, 4))
71+
4×4 Array{Int64,2}:
72+
1 5 9 13
73+
2 6 10 14
74+
3 7 11 15
75+
4 8 12 16
76+
77+
julia> reshape(A, 2, :)
78+
2×8 Array{Int64,2}:
79+
1 3 5 7 9 11 13 15
80+
2 4 6 8 10 12 14 16
81+
```
82+
83+
"""
84+
reshape
85+
3986
reshape(parent::AbstractArray, dims::IntOrInd...) = reshape(parent, dims)
4087
reshape(parent::AbstractArray, shp::NeedsShaping) = reshape(parent, to_shape(shp))
4188
reshape(parent::AbstractArray, dims::Dims) = _reshape(parent, dims)
4289

90+
# Allow missing dimensions with Colon():
91+
reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims)
92+
reshape(parent::AbstractArray, dims::Union{Int,Colon}...) = reshape(parent, dims)
93+
reshape(parent::AbstractArray, dims::Tuple{Vararg{Union{Int,Colon}}}) = _reshape(parent, _reshape_uncolon(parent, dims))
94+
# Recursively move dimensions to pre and post tuples, splitting on the Colon
95+
@inline _reshape_uncolon(A, dims) = _reshape_uncolon(A, (), nothing, (), dims)
96+
@inline _reshape_uncolon(A, pre, c::Void, post, dims::Tuple{Any, Vararg{Any}}) =
97+
_reshape_uncolon(A, (pre..., dims[1]), c, post, tail(dims))
98+
@inline _reshape_uncolon(A, pre, c::Void, post, dims::Tuple{Colon, Vararg{Any}}) =
99+
_reshape_uncolon(A, pre, dims[1], post, tail(dims))
100+
@inline _reshape_uncolon(A, pre, c::Colon, post, dims::Tuple{Any, Vararg{Any}}) =
101+
_reshape_uncolon(A, pre, c, (post..., dims[1]), tail(dims))
102+
_reshape_uncolon(A, pre, c::Colon, post, dims::Tuple{Colon, Vararg{Any}}) =
103+
throw(DimensionMismatch("new dimensions $((pre..., c, post..., dims...)) may only have at most one omitted dimension specified by Colon()"))
104+
@inline function _reshape_uncolon(A, pre, c::Colon, post, dims::Tuple{})
105+
sz, remainder = divrem(length(A), prod(pre)*prod(post))
106+
remainder == 0 || _throw_reshape_colon_dimmismatch(A, pre, post)
107+
(pre..., sz, post...)
108+
end
109+
_throw_reshape_colon_dimmismatch(A, pre, post) =
110+
throw(DimensionMismatch("array size $(length(A)) must be divisible by the product of the new dimensions $((pre..., :, post...))"))
111+
43112
reshape{T,N}(parent::AbstractArray{T,N}, ndims::Type{Val{N}}) = parent
44113
function reshape{T,AN,N}(parent::AbstractArray{T,AN}, ndims::Type{Val{N}})
45114
reshape(parent, rdims((), indices(parent), Val{N}))

test/arrayops.jl

+21
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,27 @@ end
135135
@test isa(reshape(s, Val{N}), Base.ReshapedArray{Int,N})
136136
end
137137
end
138+
@testset "reshape with colon" begin
139+
# Reshape with an omitted dimension
140+
let A = linspace(1, 60, 60)
141+
@test size(reshape(A, :)) == (60,)
142+
@test size(reshape(A, :, 1)) == (60, 1)
143+
@test size(reshape(A, (:, 2))) == (30, 2)
144+
@test size(reshape(A, 3, :)) == (3, 20)
145+
@test size(reshape(A, 2, 3, :)) == (2, 3, 10)
146+
@test size(reshape(A, (2, :, 5))) == (2, 6, 5)
147+
@test_throws DimensionMismatch reshape(A, 7, :)
148+
@test_throws DimensionMismatch reshape(A, :, 2, 3, 4)
149+
@test_throws DimensionMismatch reshape(A, (:, :))
150+
151+
B = rand(2,2,2,2)
152+
@test size(reshape(B, :)) == (16,)
153+
@test size(reshape(B, :, 4)) == (4, 4)
154+
@test size(reshape(B, (2, 1, :))) == (2, 1, 8)
155+
@test_throws DimensionMismatch reshape(B, 3, :)
156+
@test_throws DimensionMismatch reshape(B, :, :, 2, 2)
157+
end
158+
end
138159

139160
@test reshape(1:5, (5,)) === 1:5
140161
@test reshape(1:5, 5) === 1:5

0 commit comments

Comments
 (0)