Skip to content

Commit b0a09f7

Browse files
committed
first version
1 parent 333167d commit b0a09f7

File tree

2 files changed

+142
-4
lines changed

2 files changed

+142
-4
lines changed

src/TupleTools.jl

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,114 @@
11
module TupleTools
22

3-
# package code goes here
3+
using Base: tuple_type_head, tuple_type_tail, tuple_type_cons, tail, front, setindex
4+
5+
# don't export anything, import whatever you need
6+
7+
struct StaticLength{N}
8+
end
9+
Base.@pure StaticLength(N::Int) = StaticLength{N}()
10+
Base.@pure Base.:+(::StaticLength{N₁}, ::StaticLength{N₂}) where {N₁,N₂} = StaticLength(N₁+N₂)
11+
Base.@pure Base.:-(::StaticLength{N₁}, ::StaticLength{N₂}) where {N₁,N₂} = StaticLength(max(0,N₁-N₂))
12+
13+
if VERSION < v"0.7.0-DEV.843"
14+
@inline Base.ntuple(f, ::StaticLength{N}) where {N} = ntuple(f, Val{N})
15+
else
16+
@inline Base.ntuple(f, ::StaticLength{N}) where {N} = ntuple(f, Val{N}())
17+
end
18+
19+
if VERSION >= v"0.7-" # to fix type instability in (f)indmin / (f)indmax
20+
Base.Pair(p::Tuple{Any,Any}) = p[1] => p[2]
21+
Base.pairs(collection) = Base.Generator(=>, zip(keys(collection), values(collection)))
22+
end
23+
24+
@inline argtail2(a, b, c...) = c
25+
"""
26+
tail2(t::Tuple) -> ::Tuple
27+
28+
Returns a tuple with the first two elements stripped, equivalent to `tail(tail(t))`
29+
"""
30+
@inline tail2(t::Tuple{Any,Any,Vararg{Any}}) = argtail2(t...)
31+
32+
"""
33+
unsafe_tail(t::Tuple) -> ::Tuple
34+
35+
Returns a tuple with the first element stripped, similar to `tail(t)`, but does
36+
not error on an empty tuple (instead returning an empty tuple again). An empty tuple
37+
is thus the fixed point of this function.
38+
"""
39+
@inline unsafe_tail(t::Tuple{}) = t
40+
@inline unsafe_tail(t::Tuple) = tail(t)
41+
42+
"""
43+
unsafe_front(t::Tuple) -> ::Tuple
44+
45+
Returns a tuple with the last element stripped, similar to `front(t)`, but does
46+
not error on an empty tuple (instead returning an empty tuple again). An empty tuple
47+
is thus the fixed point of this function.
48+
"""
49+
@inline unsafe_front(t::Tuple{}) = t
50+
@inline unsafe_front(t::Tuple) = front(t)
51+
52+
"""
53+
vcat(args...) -> ::Tuple
54+
55+
Like `vcat` for tuples, concatenates a combination of tuple arguments and non-tuple
56+
arguments into a single tuple. Only works one level deep, i.e. tuples in tuples are
57+
not expanded.
58+
"""
59+
@inline vcat(t::Tuple) = t
60+
@inline vcat() = ()
61+
@inline vcat(t) = (t,)
62+
@inline vcat(a, args...) = (vcat(a)..., vcat(args...)...)
63+
64+
"""
65+
deleteat(t::Tuple, i::Int) -> ::Tuple
66+
deleteat(t::Tuple, I::Tuple{Vararg{Int}}) -> ::Tuple
67+
68+
Delete the element at location `i` in `t`; if a list `I` of indices is specified
69+
(again as a tuple), the elements of these different positions are deleted.
70+
"""
71+
@inline deleteat(t::Tuple, I::Tuple{Int}) = deleteat(t, I[1])
72+
@inline deleteat(t::Tuple, I::Tuple{Int, Int, Vararg{Int}}) = deleteat(deleteat(t, I[1]), map(i->(i<I[1] ? i : i-1), tail(I)))
73+
@inline deleteat(t::Tuple, i::Int) = 1 <= i <= length(t) ? _deleteat(t, i) : throw(BoundsError(t, i))
74+
@inline _deleteat(t::Tuple, i::Int) = i == 1 ? tail(t) : (t[1], _deleteat(tail(t), i-1)...)
75+
@inline _deleteat(t::Tuple{}, i::Int) = throw(BoundsError(t, i))
76+
77+
"""
78+
insertat(t::Tuple, i::Int, t2::Tuple) -> ::Tuple
79+
80+
81+
Insert the elements of tuple t2 at location `i` in `t`, i.e. the output tuple will
82+
look as (t[1:i-1]..., t2..., t[i+1:end]). Note that element `t[i]` is deleted. See
83+
`splice` if you would also like to return `t[i]`
84+
"""
85+
@inline insertat(t::Tuple, i::Int, t2::Tuple) = 1 <= i <= length(t) ? _insertat(t, i, t2) : throw(BoundsError(t, i))
86+
@inline _insertat(t::Tuple, i::Int, t2::Tuple) = i == 1 ? (t2..., tail(t)...) : (t[1], _insertat(tail(t), i-1, t2)...)
87+
@inline _insertat(t::Tuple{}, i::Int, t2::Tuple) = throw(BoundsError(t, i))
88+
89+
@inline function sortperm(t::Tuple)
90+
i = indmin(t)
91+
r = map(n->(n < i ? n : n+1), sortperm(deleteat(t, i)))
92+
return (i, r...)
93+
end
94+
@inline sortperm(t::Tuple{Any}) = (1,)
95+
96+
"""
97+
getindices(t::Tuple, I::Tuple{Vararg{Int}}) -> ::Tuple
98+
99+
Get the indices `t[i] for i in I`, again as tuple.
100+
"""
101+
@inline getindices(t::Tuple, ind::Tuple{Vararg{Int}}) = (t[ind[1]], getindices(t, tail(ind))...)
102+
@inline getindices(t::Tuple, ind::Tuple{}) = ()
103+
104+
"""
105+
permute(t::Tuple, p) -> ::Tuple
106+
107+
Permute the elements of tuple `t` according to the permutation in `p`.
108+
"""
109+
permute(t::NTuple{N}, p::NTuple{N,Int}) where {N} = isperm(p) ? getindices(t, p) : throw(ArgumentError("not a valid permutation: $p"))
110+
permute(t::NTuple{N}, p) where {N} = isperm(p) && length(p) == N ? ntuple(n->t[p[n]], StaticLength(N)) : throw(ArgumentError("not a valid permutation: $p"))
111+
112+
invperm(p::Tuple{Vararg{Int}}) = sortperm(p)
4113

5114
end # module

test/runtests.jl

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
1+
if VERSION < v"0.7.0-DEV.2005"
2+
const Test = Base.Test
3+
end
4+
5+
using Test
16
using TupleTools
2-
using Base.Test
37

4-
# write your own tests here
5-
@test 1 == 2
8+
using Base: tail, front
9+
10+
n = 10
11+
12+
p = randperm(n)
13+
ip = invperm(p)
14+
15+
t = (p...)
16+
17+
18+
i = rand(1:n)
19+
20+
@test @inferred(TupleTools.tail2(t)) == t[3:n]
21+
@test @inferred(TupleTools.unsafe_tail(t)) == t[2:n]
22+
@test @inferred(TupleTools.unsafe_front(t)) == t[1:n-1]
23+
@test @inferred(TupleTools.unsafe_tail(())) == ()
24+
@test @inferred(TupleTools.unsafe_front(())) == ()
25+
26+
@test @inferred(TupleTools.vcat((1,2,3),4,(5,),(),(6,7,8))) == (1,2,3,4,5,6,7,8)
27+
28+
@test @inferred(TupleTools.deleteat(t, i)) == (deleteat!(copy(p), i)...)
29+
@test @inferred(TupleTools.insertat(t, i, (1,2,3))) == (vcat(p[1:i-1], [1,2,3], p[i+1:n])...)
30+
31+
@test @inferred(TupleTools.getindices(t, (1,2,3))) == t[1:3]
32+
33+
@test @inferred(TupleTools.sortperm(t)) == (sortperm(p)...)
34+
@test @inferred(TupleTools.invperm(t)) == (ip...)

0 commit comments

Comments
 (0)