Skip to content

Commit 05d20b6

Browse files
committed
Added docs, sort method and more tests.
1 parent b0a09f7 commit 05d20b6

File tree

5 files changed

+286
-9
lines changed

5 files changed

+286
-9
lines changed

README.md

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,170 @@
1-
# TupleTools
1+
2+
<a id='TupleTools.jl-1'></a>
3+
4+
# TupleTools.jl
5+
26

37
[![Build Status](https://travis-ci.org/jutho/TupleTools.jl.svg?branch=master)](https://travis-ci.org/jutho/TupleTools.jl)
48

9+
510
[![Coverage Status](https://coveralls.io/repos/jutho/TupleTools.jl/badge.svg?branch=master&service=github)](https://coveralls.io/github/jutho/TupleTools.jl?branch=master)
611

12+
713
[![codecov.io](http://codecov.io/github/jutho/TupleTools.jl/coverage.svg?branch=master)](http://codecov.io/github/jutho/TupleTools.jl?branch=master)
14+
15+
16+
A bunch of tools for using tuples (mostly homogeneous tuples `NTuple{N}`) as a collection and performing a number of operations with an inferrable result, typically also an `NTuple{M}` with inferrable length `M`. Type inference breaks down if some of the final or intermediate tuples exceed `MAX_TUPLETYPE_LEN`, meaning inference typically works up to output tuples of length `13` or `14`. Inference also breaks down for most methods in case of inhomogeneous tuples.
17+
18+
19+
<a id='Types-1'></a>
20+
21+
## Types
22+
23+
<a id='TupleTools.StaticLength' href='#TupleTools.StaticLength'>#</a>
24+
**`TupleTools.StaticLength`** &mdash; *Type*.
25+
26+
27+
28+
```
29+
struct StaticLength{N} end
30+
```
31+
32+
Like `Val{N}`, `StaticLength` can be used to construct a tuple of inferrable length using `ntuple(f, StaticLength(N))`. Here, `StaticLength(N)` creates `StaticLength{N}()` using a `Base.@pure` constructor. Furthermore, one can add and subtract `StaticLength` objects, such that
33+
34+
```
35+
StaticLength(N₁) + StaticLength(N₂) == StaticLength(N₁+N₂)
36+
```
37+
38+
and
39+
40+
```
41+
StaticLength(N₁) - StaticLength(N₂) == StaticLength(max(0, N₁-N₂))
42+
```
43+
44+
45+
<a id='Functions-1'></a>
46+
47+
## Functions
48+
49+
<a id='TupleTools.tail2' href='#TupleTools.tail2'>#</a>
50+
**`TupleTools.tail2`** &mdash; *Function*.
51+
52+
53+
54+
```
55+
tail2(t::Tuple) -> ::Tuple
56+
```
57+
58+
Returns a tuple with the first two elements stripped, equivalent to `tail(tail(t))`
59+
60+
<a id='TupleTools.unsafe_tail' href='#TupleTools.unsafe_tail'>#</a>
61+
**`TupleTools.unsafe_tail`** &mdash; *Function*.
62+
63+
64+
65+
```
66+
unsafe_tail(t::Tuple) -> ::Tuple
67+
```
68+
69+
Returns a tuple with the first element stripped, similar to `tail(t)`, but does not error on an empty tuple (instead returning an empty tuple again). An empty tuple is thus the fixed point of this function.
70+
71+
<a id='TupleTools.unsafe_front' href='#TupleTools.unsafe_front'>#</a>
72+
**`TupleTools.unsafe_front`** &mdash; *Function*.
73+
74+
75+
76+
```
77+
unsafe_front(t::Tuple) -> ::Tuple
78+
```
79+
80+
Returns a tuple with the last element stripped, similar to `front(t)`, but does not error on an empty tuple (instead returning an empty tuple again). An empty tuple is thus the fixed point of this function.
81+
82+
<a id='TupleTools.getindices' href='#TupleTools.getindices'>#</a>
83+
**`TupleTools.getindices`** &mdash; *Function*.
84+
85+
86+
87+
```
88+
getindices(t::Tuple, I::Tuple{Vararg{Int}}) -> ::Tuple
89+
```
90+
91+
Get the indices `t[i] for i in I`, again as tuple.
92+
93+
<a id='TupleTools.vcat' href='#TupleTools.vcat'>#</a>
94+
**`TupleTools.vcat`** &mdash; *Function*.
95+
96+
97+
98+
```
99+
vcat(args...) -> ::Tuple
100+
```
101+
102+
Like `vcat` for tuples, concatenates a combination of tuple arguments and non-tuple arguments into a single tuple. Only works one level deep, i.e. tuples in tuples are not expanded.
103+
104+
<a id='TupleTools.deleteat' href='#TupleTools.deleteat'>#</a>
105+
**`TupleTools.deleteat`** &mdash; *Function*.
106+
107+
108+
109+
```
110+
deleteat(t::Tuple, i::Int) -> ::Tuple
111+
deleteat(t::Tuple, I::Tuple{Vararg{Int}}) -> ::Tuple
112+
```
113+
114+
Delete the element at location `i` in `t`; if a list `I` of indices is specified (again as a tuple), the elements of these different positions are deleted.
115+
116+
<a id='TupleTools.insertat' href='#TupleTools.insertat'>#</a>
117+
**`TupleTools.insertat`** &mdash; *Function*.
118+
119+
120+
121+
```
122+
insertat(t::Tuple, i::Int, t2::Tuple) -> ::Tuple
123+
```
124+
125+
Insert the elements of tuple t2 at location `i` in `t`, i.e. the output tuple will look as (t[1:i-1]..., t2..., t[i+1:end]). Note that element `t[i]` is deleted. See `splice` if you would also like to return `t[i]`
126+
127+
<a id='TupleTools.sort' href='#TupleTools.sort'>#</a>
128+
**`TupleTools.sort`** &mdash; *Function*.
129+
130+
131+
132+
```
133+
sort(t::Tuple; lt=isless, by=identity, rev::Bool=false) -> ::Tuple
134+
```
135+
136+
Sorts the tuple `t`.
137+
138+
<a id='TupleTools.sortperm' href='#TupleTools.sortperm'>#</a>
139+
**`TupleTools.sortperm`** &mdash; *Function*.
140+
141+
142+
143+
```
144+
sortperm(t::Tuple; lt=isless, by=identity, rev::Bool=false) -> ::Tuple
145+
```
146+
147+
Computes a tuple that contains the permutation required to sort `t`.
148+
149+
<a id='TupleTools.invperm' href='#TupleTools.invperm'>#</a>
150+
**`TupleTools.invperm`** &mdash; *Function*.
151+
152+
153+
154+
```
155+
invperm(p::NTuple{N,Int}) -> ::NTuple{N,Int}
156+
```
157+
158+
Inverse permutation of a permutation `p`.
159+
160+
<a id='TupleTools.permute' href='#TupleTools.permute'>#</a>
161+
**`TupleTools.permute`** &mdash; *Function*.
162+
163+
164+
165+
```
166+
permute(t::Tuple, p) -> ::Tuple
167+
```
168+
169+
Permute the elements of tuple `t` according to the permutation in `p`.
170+

docs/make.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using Documenter, TupleTools
2+
3+
makedocs(modules=[TupleTools])

docs/src/index.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# TupleTools.jl
2+
3+
[![Build Status](https://travis-ci.org/jutho/TupleTools.jl.svg?branch=master)](https://travis-ci.org/jutho/TupleTools.jl)
4+
5+
[![Coverage Status](https://coveralls.io/repos/jutho/TupleTools.jl/badge.svg?branch=master&service=github)](https://coveralls.io/github/jutho/TupleTools.jl?branch=master)
6+
7+
[![codecov.io](http://codecov.io/github/jutho/TupleTools.jl/coverage.svg?branch=master)](http://codecov.io/github/jutho/TupleTools.jl?branch=master)
8+
9+
A bunch of tools for using tuples (mostly homogeneous tuples `NTuple{N}`) as a collection
10+
and performing a number of operations with an inferrable result, typically also an `NTuple{M}`
11+
with inferrable length `M`. Type inference breaks down if some of the final or intermediate tuples
12+
exceed `MAX_TUPLETYPE_LEN`, meaning inference typically works up to output tuples of
13+
length `13` or `14`. Inference also breaks down for most methods in case of inhomogeneous tuples.
14+
15+
## Types
16+
17+
```@docs
18+
TupleTools.StaticLength
19+
```
20+
21+
## Functions
22+
23+
```@docs
24+
TupleTools.tail2
25+
TupleTools.unsafe_tail
26+
TupleTools.unsafe_front
27+
```
28+
29+
```@docs
30+
TupleTools.getindices
31+
TupleTools.vcat
32+
```
33+
34+
```@docs
35+
TupleTools.deleteat
36+
TupleTools.insertat
37+
```
38+
39+
```@docs
40+
TupleTools.sort
41+
TupleTools.sortperm
42+
```
43+
44+
```@docs
45+
TupleTools.invperm
46+
TupleTools.permute
47+
```

src/TupleTools.jl

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,21 @@ module TupleTools
22

33
using Base: tuple_type_head, tuple_type_tail, tuple_type_cons, tail, front, setindex
44

5-
# don't export anything, import whatever you need
5+
"""
6+
struct StaticLength{N} end
67
8+
Like `Val{N}`, `StaticLength` can be used to construct a tuple of inferrable length
9+
using `ntuple(f, StaticLength(N))`. Here, `StaticLength(N)` creates `StaticLength{N}()`
10+
using a `Base.@pure` constructor. Furthermore, one can add and subtract `StaticLength`
11+
objects, such that
12+
```
13+
StaticLength(N₁) + StaticLength(N₂) == StaticLength(N₁+N₂)
14+
```
15+
and
16+
```
17+
StaticLength(N₁) - StaticLength(N₂) == StaticLength(max(0, N₁-N₂))
18+
```
19+
"""
720
struct StaticLength{N}
821
end
922
Base.@pure StaticLength(N::Int) = StaticLength{N}()
@@ -22,6 +35,7 @@ if VERSION >= v"0.7-" # to fix type instability in (f)indmin / (f)indmax
2235
end
2336

2437
@inline argtail2(a, b, c...) = c
38+
2539
"""
2640
tail2(t::Tuple) -> ::Tuple
2741
@@ -86,12 +100,56 @@ look as (t[1:i-1]..., t2..., t[i+1:end]). Note that element `t[i]` is deleted. S
86100
@inline _insertat(t::Tuple, i::Int, t2::Tuple) = i == 1 ? (t2..., tail(t)...) : (t[1], _insertat(tail(t), i-1, t2)...)
87101
@inline _insertat(t::Tuple{}, i::Int, t2::Tuple) = throw(BoundsError(t, i))
88102

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...)
103+
104+
"""
105+
sort(t::Tuple; lt=isless, by=identity, rev::Bool=false) -> ::Tuple
106+
107+
Sorts the tuple `t`.
108+
"""
109+
@inline function sort(t::Tuple; lt=isless, by=identity, rev::Bool=false)
110+
i = 1
111+
if rev
112+
for k = 2:length(t)
113+
if lt(by(t[i]), by(t[k]))
114+
i = k
115+
end
116+
end
117+
else
118+
for k = 2:length(t)
119+
if lt(by(t[k]), by(t[i]))
120+
i = k
121+
end
122+
end
123+
end
124+
return (t[i], sort(deleteat(t, i); lt = lt, by = by, rev = rev)...)
125+
end
126+
@inline sort(t::Tuple{Any}; lt=isless, by=identity, rev::Bool=false) = t
127+
128+
"""
129+
sortperm(t::Tuple; lt=isless, by=identity, rev::Bool=false) -> ::Tuple
130+
131+
132+
Computes a tuple that contains the permutation required to sort `t`.
133+
"""
134+
@inline function sortperm(t::Tuple; lt=isless, by=identity, rev::Bool=false)
135+
i::Int = 1
136+
if rev
137+
for k = 2:length(t)
138+
if lt(by(t[i]), by(t[k]))
139+
i = k
140+
end
141+
end
142+
else
143+
for k = 2:length(t)
144+
if lt(by(t[k]), by(t[i]))
145+
i = k
146+
end
147+
end
148+
end
149+
r = sortperm(deleteat(t, i); lt = lt, by = by, rev = rev)
150+
return (i, map(n->(n < i ? n : n+1), r)...)
93151
end
94-
@inline sortperm(t::Tuple{Any}) = (1,)
152+
@inline sortperm(t::Tuple{Any}; lt=isless, by=identity, rev::Bool=false) = (1,)
95153

96154
"""
97155
getindices(t::Tuple, I::Tuple{Vararg{Int}}) -> ::Tuple
@@ -109,6 +167,11 @@ Permute the elements of tuple `t` according to the permutation in `p`.
109167
permute(t::NTuple{N}, p::NTuple{N,Int}) where {N} = isperm(p) ? getindices(t, p) : throw(ArgumentError("not a valid permutation: $p"))
110168
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"))
111169

170+
"""
171+
invperm(p::NTuple{N,Int}) -> ::NTuple{N,Int}
172+
173+
Inverse permutation of a permutation `p`.
174+
"""
112175
invperm(p::Tuple{Vararg{Int}}) = sortperm(p)
113176

114177
end # module

test/runtests.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ i = rand(1:n)
2323
@test @inferred(TupleTools.unsafe_tail(())) == ()
2424
@test @inferred(TupleTools.unsafe_front(())) == ()
2525

26+
@test @inferred(TupleTools.getindices(t, (1,2,3))) == t[1:3]
2627
@test @inferred(TupleTools.vcat((1,2,3),4,(5,),(),(6,7,8))) == (1,2,3,4,5,6,7,8)
2728

2829
@test @inferred(TupleTools.deleteat(t, i)) == (deleteat!(copy(p), i)...)
2930
@test @inferred(TupleTools.insertat(t, i, (1,2,3))) == (vcat(p[1:i-1], [1,2,3], p[i+1:n])...)
3031

31-
@test @inferred(TupleTools.getindices(t, (1,2,3))) == t[1:3]
32-
32+
@test @inferred(TupleTools.sort(t; rev = true)) == (sort(p; rev = true)...)
3333
@test @inferred(TupleTools.sortperm(t)) == (sortperm(p)...)
3434
@test @inferred(TupleTools.invperm(t)) == (ip...)
35+
@test @inferred(TupleTools.permute(t, t)) == (p[p]...)

0 commit comments

Comments
 (0)