diff --git a/.gitignore b/.gitignore index 8c960ec..3f02ca7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.jl.cov *.jl.*.cov *.jl.mem +Manifest.toml diff --git a/.travis.yml b/.travis.yml index 47fabb8..65c2c89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,9 @@ os: - linux - osx julia: - - 0.6 - - nightly + - 1.0 + - 1 + # - nightly matrix: allow_failures: - julia: nightly diff --git a/Project.toml b/Project.toml new file mode 100644 index 0000000..75b003e --- /dev/null +++ b/Project.toml @@ -0,0 +1,13 @@ +name = "EllipsisNotation" +uuid = "da5c29d0-fa7d-589e-88eb-ea29b0a81949" +authors = ["Chris Rackauckas "] +version = "0.4.0" + +[compat] +julia = "1" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/README.md b/README.md index bfcd4a4..c9b5931 100644 --- a/README.md +++ b/README.md @@ -15,39 +15,48 @@ using EllipsisNotation # Example Usage ```julia -A = Array{Int}(2,4,2) +julia> A = Array{Int}(undef,2,4,2) -A[..,1] = [2 1 4 5 - 2 2 3 6] +julia> A[..,1] = [2 1 4 5 + 2 2 3 6] -A[..,2] = [3 2 6 5 - 3 2 6 6] +julia> A[..,2] = [3 2 6 5 + 3 2 6 6] -A[:,:,1] == [2 1 4 5 - 2 2 3 6] #true +julia> A[:,:,1] == [2 1 4 5 + 2 2 3 6] +true -A[1,..] = reshape([3 4 - 5 6 - 4 5 - 6 7],1,4,2) #v0.4 doesn't drop singleton dimension, v0.5 does +julia> A[1,..] = reshape([3 4 + 5 6 + 4 5 + 6 7],1,4,2) # drops singleton dimension -B = [3 4 - 5 6 - 4 5 - 6 7] +julia> B = [3 4 + 5 6 + 4 5 + 6 7] -B == reshape(A[1,..],4,2) #true +julia> B == reshape(A[1,..],4,2) +true -A[..,1,2] # Can do as many integers as you want on the end! +julia> A[..,1,2] # Can do as many integers as you want on the end! ``` For avoiding squeezing dimensions from slicing. -```julia-repl +```julia julia> C = ones(3,3,3,3,3); julia> size(C[1:1, .., 1:1]) (1, 3, 3, 3, 1) ``` +Note: `..` slurps dimensions greedily, meaning that the first occurrence +of `..` in an index expression creates as many slices as possible. Other +instances of `..` afterwards are treated simply as slices. Usually, you +should only use one instance of `..` in an indexing expression to avoid +possible confusion. + + # Acknowledgements I would like to acknowledge M. Schauer for the `..` notation implementation. diff --git a/src/EllipsisNotation.jl b/src/EllipsisNotation.jl index 7f8f90a..f56c508 100644 --- a/src/EllipsisNotation.jl +++ b/src/EllipsisNotation.jl @@ -1,5 +1,45 @@ __precompile__() +""" +Implements the notation `..` for indexing arrays. It's similar to the Python +`...` in that it means 'all of the columns before (or after)'. + +`..` slurps dimensions greedily, meaning that the first occurrence +of `..` in an index expression creates as many slices as possible. Other +instances of `..` afterwards are treated simply as slices. Usually, you +should only use one instance of `..` in an indexing expression to avoid +possible confusion. + +# Example + +```jldoctest +julia> A = Array{Int}(undef,2,4,2); + +julia> A[..,1] = [2 1 4 5 + 2 2 3 6]; + +julia> A[..,2] = [3 2 6 5 + 3 2 6 6]; + +julia> A[:,:,1] == [2 1 4 5 + 2 2 3 6] +true + +julia> A[1,..] = reshape([3 4 + 5 6 + 4 5 + 6 7],1,4,2) # drops singleton dimension +... + +julia> B = [3 4 + 5 6 + 4 5 + 6 7]; + +julia> B == reshape(A[1,..],4,2) +true +``` +""" module EllipsisNotation import Base: to_indices, tail @@ -7,16 +47,9 @@ import Base: to_indices, tail struct Ellipsis end const .. = Ellipsis() -@inline fillcolons(inds, I) = fillcolons((), inds, I) - -@inline fillcolons(colons, ::Tuple{}, ::Tuple{}) = colons -@noinline fillcolons(colons, ::Tuple{}, ::Tuple) = throw(ArgumentError("too many indices provided")) -@inline fillcolons(colons, t::NTuple{N, <:Any}, ::NTuple{N, <:Any}) where {N} = colons -@inline fillcolons(colons, t::Tuple, s::Tuple) = fillcolons((colons..., :), tail(t), s) - -@inline function to_indices(A, inds, I::Tuple{Ellipsis, Vararg{Any, N}}) where N +@inline function to_indices(A, inds::NTuple{M, Any}, I::Tuple{Ellipsis, Vararg{Any, N}}) where {M,N} # Align the remaining indices to the tail of the `inds` - colons = fillcolons(inds, tail(I)) + colons = ntuple(n->Colon(), M-N) to_indices(A, inds, (colons..., tail(I)...)) end diff --git a/test/basic.jl b/test/basic.jl index 913697e..71f39c8 100644 --- a/test/basic.jl +++ b/test/basic.jl @@ -1,10 +1,10 @@ -A = Array{Int}(2,4,2) +A = Array{Int}(undef,2,4,2) A[..,1] = [2 1 4 5 2 2 3 6] A[..,2] = [3 2 6 5 - 3 2 6 6] + 3 2 6 6] @test A[:,:,1] == [2 1 4 5 2 2 3 6] @@ -12,6 +12,12 @@ A[..,2] = [3 2 6 5 @test A[:,:,2] == [3 2 6 5 3 2 6 6] +@test A[:,..,1] == [2 1 4 5 + 2 2 3 6] + +@test A[:,..,1] == [2 1 4 5 + 2 2 3 6] + A[1,..] = reshape([3 4 5 6 4 5 @@ -27,7 +33,7 @@ B = [3 4 @test A[:,1,2] == A[..,1,2] == @view A[..,1,2] # [..] -C = zeros(B) +C = zero(B) C[:] = B[..] @test B == C diff --git a/test/more_generic.jl b/test/more_generic.jl index ff22601..eacad26 100644 --- a/test/more_generic.jl +++ b/test/more_generic.jl @@ -1,9 +1,9 @@ -B = Array{Int}(2, 3, 4, 5, 6) +B = Array{Int}(undef, 2, 3, 4, 5, 6) n = 0 for i5=1:6, i4=1:5, i3=1:4 - n += 1 - B[.., i3, i4, i5] = n + global n += 1 + B[.., i3, i4, i5] .= n end # B => 2×3×4×5×6 Array{Int64,5}: # [:, :, 1, 1, 1] = @@ -23,12 +23,12 @@ end @test B[1, 1, ..] == B[1, 1, :, :, :] == reshape(1:120, 4, 5, 6) @test B[1, 1, .., 1] == B[1, 1, :, :, 1] == reshape(1:20, 4, 5) -C = Array{Int}(2, 3, 4, 5, 6) +C = Array{Int}(undef, 2, 3, 4, 5, 6) n = 0 for i3=1:4, i2=1:3, i1=1:2 - n += 1 - C[i1, i2, i3, ..] = n + global n += 1 + C[i1, i2, i3, ..] .= n end # C => 2×3×4×5×6 Array{Int64,5}: # [:, :, 1, 1, 1] = @@ -49,5 +49,5 @@ end @test C[1, .., 1, 1] == C[1, :, :, 1, 1] == reshape(1:2:24, 3, 4) D = ones(Int, 1, 2, 3, 4) -D[1, .., 2] = 2 +D[1, .., 2] .= 2 @test D == [i1 == 1 && i4 == 2 ? 2 : 1 for i1=1:1, i2=1:2, i3=1:3, i4=1:4] \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 46b6f93..885a2ce 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,5 @@ using EllipsisNotation -using Base.Test +using Test include("basic.jl") include("more_generic.jl")