| 1 |
Ddoc |
|---|
| 2 |
|
|---|
| 3 |
$(D_S Tuples, |
|---|
| 4 |
|
|---|
| 5 |
$(P A tuple is a sequence of elements. Those elements can |
|---|
| 6 |
be types, expressions, or aliases. |
|---|
| 7 |
The number and elements of a tuple are fixed at compile time; |
|---|
| 8 |
they cannot be changed at run time. |
|---|
| 9 |
) |
|---|
| 10 |
|
|---|
| 11 |
$(P Tuples have characteristics of both |
|---|
| 12 |
structs and arrays. Like structs, the tuple |
|---|
| 13 |
elements can be of different types. Like arrays, |
|---|
| 14 |
the elements can be accessed via indexing. |
|---|
| 15 |
) |
|---|
| 16 |
|
|---|
| 17 |
$(P So how does one construct a tuple? There isn't a specific |
|---|
| 18 |
tuple literal syntax. But since variadic template parameters |
|---|
| 19 |
create tuples, we can define a template to create one: |
|---|
| 20 |
) |
|---|
| 21 |
|
|---|
| 22 |
--- |
|---|
| 23 |
template Tuple(E...) |
|---|
| 24 |
{ |
|---|
| 25 |
alias E Tuple; |
|---|
| 26 |
} |
|---|
| 27 |
--- |
|---|
| 28 |
|
|---|
| 29 |
$(P and it's used like:) |
|---|
| 30 |
|
|---|
| 31 |
--- |
|---|
| 32 |
Tuple!(int, long, float) // create a tuple of 3 types |
|---|
| 33 |
Tuple!(3, 7, 'c') // create a tuple of 3 expressions |
|---|
| 34 |
Tuple!(int, 8) // create a tuple of a type and an expression |
|---|
| 35 |
--- |
|---|
| 36 |
|
|---|
| 37 |
$(P In order to symbolically refer to a tuple, use an alias:) |
|---|
| 38 |
|
|---|
| 39 |
--- |
|---|
| 40 |
alias Tuple!(float, float, 3) TP; // TP is now a tuple of two floats and 3 |
|---|
| 41 |
--- |
|---|
| 42 |
|
|---|
| 43 |
$(P Tuples can be used as arguments to templates, and if so |
|---|
| 44 |
they are $(SINGLEQUOTE flattened) out into a list of arguments. |
|---|
| 45 |
This makes it straightforward to append a new element to |
|---|
| 46 |
an existing tuple or concatenate tuples:) |
|---|
| 47 |
|
|---|
| 48 |
--- |
|---|
| 49 |
alias Tuple!(TP, 8) TR; // TR is now float,float,3,8 |
|---|
| 50 |
alias Tuple!(TP, TP) TS; // TS is float,float,3,float,float,3 |
|---|
| 51 |
--- |
|---|
| 52 |
|
|---|
| 53 |
$(P Tuples share many characteristics with arrays. |
|---|
| 54 |
For starters, the number of elements in a tuple can |
|---|
| 55 |
be retrieved with the $(B .length) property:) |
|---|
| 56 |
|
|---|
| 57 |
--- |
|---|
| 58 |
TP.length // evaluates to 3 |
|---|
| 59 |
--- |
|---|
| 60 |
|
|---|
| 61 |
$(P Tuples can be indexed:) |
|---|
| 62 |
|
|---|
| 63 |
--- |
|---|
| 64 |
TP[1] f = TP[2]; // f is declared as a float and initialized to 3 |
|---|
| 65 |
--- |
|---|
| 66 |
|
|---|
| 67 |
$(P and even sliced:) |
|---|
| 68 |
|
|---|
| 69 |
--- |
|---|
| 70 |
alias TP[0..length-1] TQ; // TQ is now the same as Tuple!(float, float) |
|---|
| 71 |
--- |
|---|
| 72 |
|
|---|
| 73 |
$(P Yes, $(B length) is defined within the [ ]s. |
|---|
| 74 |
There is one restriction: the indices for indexing and slicing |
|---|
| 75 |
must be evaluatable at compile time.) |
|---|
| 76 |
|
|---|
| 77 |
--- |
|---|
| 78 |
void foo(int i) |
|---|
| 79 |
{ |
|---|
| 80 |
TQ[i] x; // error, i is not constant |
|---|
| 81 |
} |
|---|
| 82 |
--- |
|---|
| 83 |
|
|---|
| 84 |
$(P These make it simple to produce the $(SINGLEQUOTE head) and $(SINGLEQUOTE tail) |
|---|
| 85 |
of a tuple. The head is just TP[0], the tail |
|---|
| 86 |
is TP[1 .. length]. |
|---|
| 87 |
Given the head and tail, mix with a little conditional |
|---|
| 88 |
compilation, and we can implement some classic recursive |
|---|
| 89 |
algorithms with templates. |
|---|
| 90 |
For example, this template returns a tuple consisting |
|---|
| 91 |
of the trailing type arguments $(I TL) with the first occurrence |
|---|
| 92 |
of the first type argument $(I T) removed: |
|---|
| 93 |
) |
|---|
| 94 |
--- |
|---|
| 95 |
template Erase(T, TL...) |
|---|
| 96 |
{ |
|---|
| 97 |
static if (TL.length == 0) |
|---|
| 98 |
// 0 length tuple, return self |
|---|
| 99 |
alias TL Erase; |
|---|
| 100 |
else static if (is(T == TL[0])) |
|---|
| 101 |
// match with first in tuple, return tail |
|---|
| 102 |
alias TL[1 .. length] Erase; |
|---|
| 103 |
else |
|---|
| 104 |
// no match, return head concatenated with recursive tail operation |
|---|
| 105 |
alias Tuple!(TL[0], Erase!(T, TL[1 .. length])) Erase; |
|---|
| 106 |
} |
|---|
| 107 |
--- |
|---|
| 108 |
|
|---|
| 109 |
<h3>Type Tuples</h3> |
|---|
| 110 |
|
|---|
| 111 |
$(P If a tuple's elements are solely types, |
|---|
| 112 |
it is called a $(I TypeTuple) |
|---|
| 113 |
(sometimes called a type list). |
|---|
| 114 |
Since function parameter lists are a list of types, |
|---|
| 115 |
a type tuple can be retrieved from them. |
|---|
| 116 |
One way is using an $(ISEXPRESSION): |
|---|
| 117 |
) |
|---|
| 118 |
|
|---|
| 119 |
--- |
|---|
| 120 |
int foo(int x, long y); |
|---|
| 121 |
|
|---|
| 122 |
... |
|---|
| 123 |
static if (is(foo P == function)) |
|---|
| 124 |
alias P TP; |
|---|
| 125 |
// TP is now the same as Tuple!(int, long) |
|---|
| 126 |
--- |
|---|
| 127 |
|
|---|
| 128 |
$(P This is generalized in the template |
|---|
| 129 |
$(LINK2 phobos/std_traits.html, std.traits).ParameterTypeTuple: |
|---|
| 130 |
) |
|---|
| 131 |
|
|---|
| 132 |
--- |
|---|
| 133 |
import std.traits; |
|---|
| 134 |
|
|---|
| 135 |
... |
|---|
| 136 |
alias ParameterTypeTuple!(foo) TP; // TP is the tuple (int, long) |
|---|
| 137 |
--- |
|---|
| 138 |
|
|---|
| 139 |
$(P $(I TypeTuple)s can be used to declare a function:) |
|---|
| 140 |
|
|---|
| 141 |
--- |
|---|
| 142 |
float bar(TP); // same as float bar(int, long) |
|---|
| 143 |
--- |
|---|
| 144 |
|
|---|
| 145 |
$(P If implicit function template instantiation is being done, |
|---|
| 146 |
the type tuple representing the parameter types can be deduced: |
|---|
| 147 |
) |
|---|
| 148 |
--- |
|---|
| 149 |
int foo(int x, long y); |
|---|
| 150 |
|
|---|
| 151 |
void Bar(R, P...)(R function(P)) |
|---|
| 152 |
{ |
|---|
| 153 |
writefln("return type is ", typeid(R)); |
|---|
| 154 |
writefln("parameter types are ", typeid(P)); |
|---|
| 155 |
} |
|---|
| 156 |
|
|---|
| 157 |
... |
|---|
| 158 |
Bar(&foo); |
|---|
| 159 |
--- |
|---|
| 160 |
|
|---|
| 161 |
$(P Prints:) |
|---|
| 162 |
|
|---|
| 163 |
$(CONSOLE |
|---|
| 164 |
return type is int |
|---|
| 165 |
parameter types are (int,long) |
|---|
| 166 |
) |
|---|
| 167 |
|
|---|
| 168 |
$(P Type deduction can be used to create a function that |
|---|
| 169 |
takes an arbitrary number and type of arguments:) |
|---|
| 170 |
|
|---|
| 171 |
--- |
|---|
| 172 |
void Abc(P...)(P p) |
|---|
| 173 |
{ |
|---|
| 174 |
writefln("parameter types are ", typeid(P)); |
|---|
| 175 |
} |
|---|
| 176 |
|
|---|
| 177 |
Abc(3, 7L, 6.8); |
|---|
| 178 |
--- |
|---|
| 179 |
|
|---|
| 180 |
$(P Prints:) |
|---|
| 181 |
|
|---|
| 182 |
$(CONSOLE |
|---|
| 183 |
parameter types are (int,long,double) |
|---|
| 184 |
) |
|---|
| 185 |
|
|---|
| 186 |
$(P For a more comprehensive treatment of this aspect, see |
|---|
| 187 |
$(LINK2 variadic-function-templates.html, Variadic Templates). |
|---|
| 188 |
) |
|---|
| 189 |
|
|---|
| 190 |
|
|---|
| 191 |
<h3>Expression Tuples</h3> |
|---|
| 192 |
|
|---|
| 193 |
$(P If a tuple's elements are solely expressions, |
|---|
| 194 |
it is called an $(I ExpressionTuple). |
|---|
| 195 |
The Tuple template can be used to create one: |
|---|
| 196 |
) |
|---|
| 197 |
|
|---|
| 198 |
--- |
|---|
| 199 |
alias Tuple!(3, 7L, 6.8) ET; |
|---|
| 200 |
|
|---|
| 201 |
... |
|---|
| 202 |
writefln(ET); // prints 376.8 |
|---|
| 203 |
writefln(ET[1]); // prints 7 |
|---|
| 204 |
writefln(ET[1..length]); // prints 76.8 |
|---|
| 205 |
--- |
|---|
| 206 |
|
|---|
| 207 |
$(P It can be used to create an array literal:) |
|---|
| 208 |
|
|---|
| 209 |
--- |
|---|
| 210 |
alias Tuple!(3, 7, 6) AT; |
|---|
| 211 |
|
|---|
| 212 |
... |
|---|
| 213 |
int[] a = [AT]; // same as [3,7,6] |
|---|
| 214 |
--- |
|---|
| 215 |
|
|---|
| 216 |
$(P The data fields of a struct or class can be |
|---|
| 217 |
turned into an expression tuple using the $(B .tupleof) |
|---|
| 218 |
property:) |
|---|
| 219 |
|
|---|
| 220 |
--- |
|---|
| 221 |
struct S { int x; long y; } |
|---|
| 222 |
|
|---|
| 223 |
void foo(int a, long b) |
|---|
| 224 |
{ |
|---|
| 225 |
writefln(a, b); |
|---|
| 226 |
} |
|---|
| 227 |
|
|---|
| 228 |
... |
|---|
| 229 |
S s; |
|---|
| 230 |
s.x = 7; |
|---|
| 231 |
s.y = 8; |
|---|
| 232 |
foo(s.x, s.y); // prints 78 |
|---|
| 233 |
foo(s.tupleof); // prints 78 |
|---|
| 234 |
s.tupleof[1] = 9; |
|---|
| 235 |
s.tupleof[0] = 10; |
|---|
| 236 |
foo(s.tupleof); // prints 109 |
|---|
| 237 |
s.tupleof[2] = 11; // error, no third field of S |
|---|
| 238 |
--- |
|---|
| 239 |
|
|---|
| 240 |
$(P A type tuple can be created from the data fields |
|---|
| 241 |
of a struct using $(B typeof):) |
|---|
| 242 |
|
|---|
| 243 |
--- |
|---|
| 244 |
writefln(typeid(typeof(S.tupleof))); // prints (int,long) |
|---|
| 245 |
--- |
|---|
| 246 |
|
|---|
| 247 |
$(P This is encapsulated in the template |
|---|
| 248 |
$(LINK2 phobos/std_traits.html, std.traits).FieldTypeTuple. |
|---|
| 249 |
) |
|---|
| 250 |
|
|---|
| 251 |
<h3>Looping</h3> |
|---|
| 252 |
|
|---|
| 253 |
$(P While the head-tail style of functional programming works |
|---|
| 254 |
with tuples, it's often more convenient to use a loop. |
|---|
| 255 |
The $(I ForeachStatement) can loop over either $(I TypeTuple)s |
|---|
| 256 |
or $(I ExpressionTuple)s. |
|---|
| 257 |
) |
|---|
| 258 |
|
|---|
| 259 |
--- |
|---|
| 260 |
alias Tuple!(int, long, float) TL; |
|---|
| 261 |
foreach (i, T; TL) |
|---|
| 262 |
writefln("TL[%d] = ", i, typeid(T)); |
|---|
| 263 |
|
|---|
| 264 |
alias Tuple!(3, 7L, 6.8) ET; |
|---|
| 265 |
foreach (i, E; ET) |
|---|
| 266 |
writefln("ET[%d] = ", i, E); |
|---|
| 267 |
--- |
|---|
| 268 |
|
|---|
| 269 |
$(P Prints:) |
|---|
| 270 |
|
|---|
| 271 |
$(CONSOLE |
|---|
| 272 |
TL[0] = int |
|---|
| 273 |
TL[1] = long |
|---|
| 274 |
TL[2] = float |
|---|
| 275 |
ET[0] = 3 |
|---|
| 276 |
ET[1] = 7 |
|---|
| 277 |
ET[2] = 6.8 |
|---|
| 278 |
) |
|---|
| 279 |
|
|---|
| 280 |
<h3>Tuple Declarations</h3> |
|---|
| 281 |
|
|---|
| 282 |
$(P A variable declared with a $(I TypeTuple) becomes an |
|---|
| 283 |
$(I ExpressionTuple):) |
|---|
| 284 |
|
|---|
| 285 |
--- |
|---|
| 286 |
alias Tuple!(int, long) TL; |
|---|
| 287 |
|
|---|
| 288 |
void foo(TL tl) |
|---|
| 289 |
{ |
|---|
| 290 |
writefln(tl, tl[1]); |
|---|
| 291 |
} |
|---|
| 292 |
|
|---|
| 293 |
foo(1, 6L); // prints 166 |
|---|
| 294 |
--- |
|---|
| 295 |
|
|---|
| 296 |
<h3>Putting It All Together</h3> |
|---|
| 297 |
|
|---|
| 298 |
$(P These capabilities can be put together to implement |
|---|
| 299 |
a template that will encapsulate all the arguments to |
|---|
| 300 |
a function, and return a delegate that will call the function |
|---|
| 301 |
with those arguments.) |
|---|
| 302 |
|
|---|
| 303 |
--- |
|---|
| 304 |
import std.stdio; |
|---|
| 305 |
|
|---|
| 306 |
R delegate() CurryAll(Dummy=void, R, U...)(R function(U) dg, U args) |
|---|
| 307 |
{ |
|---|
| 308 |
struct Foo |
|---|
| 309 |
{ |
|---|
| 310 |
typeof(dg) dg_m; |
|---|
| 311 |
U args_m; |
|---|
| 312 |
|
|---|
| 313 |
R bar() |
|---|
| 314 |
{ |
|---|
| 315 |
return dg_m(args_m); |
|---|
| 316 |
} |
|---|
| 317 |
} |
|---|
| 318 |
|
|---|
| 319 |
Foo* f = new Foo; |
|---|
| 320 |
f.dg_m = dg; |
|---|
| 321 |
foreach (i, arg; args) |
|---|
| 322 |
f.args_m[i] = arg; |
|---|
| 323 |
return &f.bar; |
|---|
| 324 |
} |
|---|
| 325 |
|
|---|
| 326 |
R delegate() CurryAll(R, U...)(R delegate(U) dg, U args) |
|---|
| 327 |
{ |
|---|
| 328 |
struct Foo |
|---|
| 329 |
{ |
|---|
| 330 |
typeof(dg) dg_m; |
|---|
| 331 |
U args_m; |
|---|
| 332 |
|
|---|
| 333 |
R bar() |
|---|
| 334 |
{ |
|---|
| 335 |
return dg_m(args_m); |
|---|
| 336 |
} |
|---|
| 337 |
} |
|---|
| 338 |
|
|---|
| 339 |
Foo* f = new Foo; |
|---|
| 340 |
f.dg_m = dg; |
|---|
| 341 |
foreach (i, arg; args) |
|---|
| 342 |
f.args_m[i] = arg; |
|---|
| 343 |
return &f.bar; |
|---|
| 344 |
} |
|---|
| 345 |
|
|---|
| 346 |
|
|---|
| 347 |
void main() |
|---|
| 348 |
{ |
|---|
| 349 |
static int plus(int x, int y, int z) |
|---|
| 350 |
{ |
|---|
| 351 |
return x + y + z; |
|---|
| 352 |
} |
|---|
| 353 |
|
|---|
| 354 |
auto plus_two = CurryAll(&plus, 2, 3, 4); |
|---|
| 355 |
writefln("%d", plus_two()); |
|---|
| 356 |
assert(plus_two() == 9); |
|---|
| 357 |
|
|---|
| 358 |
int minus(int x, int y, int z) |
|---|
| 359 |
{ |
|---|
| 360 |
return x + y + z; |
|---|
| 361 |
} |
|---|
| 362 |
|
|---|
| 363 |
auto minus_two = CurryAll(&minus, 7, 8, 9); |
|---|
| 364 |
writefln("%d", minus_two()); |
|---|
| 365 |
assert(minus_two() == 24); |
|---|
| 366 |
} |
|---|
| 367 |
--- |
|---|
| 368 |
|
|---|
| 369 |
$(P The reason for the $(I Dummy) parameter is that one |
|---|
| 370 |
cannot overload two templates with the same parameter list. |
|---|
| 371 |
So we make them different by giving one a dummy parameter. |
|---|
| 372 |
) |
|---|
| 373 |
|
|---|
| 374 |
<h3>Future Directions</h3> |
|---|
| 375 |
|
|---|
| 376 |
$(UL |
|---|
| 377 |
$(LI Return tuples from functions.) |
|---|
| 378 |
$(LI Use operators on tuples, like =, +=, etc.) |
|---|
| 379 |
$(LI Have tuple properties like $(B .init) which will apply |
|---|
| 380 |
the property to each of the tuple members.) |
|---|
| 381 |
) |
|---|
| 382 |
|
|---|
| 383 |
) |
|---|
| 384 |
|
|---|
| 385 |
Macros: |
|---|
| 386 |
TITLE=Tuples |
|---|
| 387 |
WIKI=Tuples |
|---|
| 388 |
META_KEYWORDS=D Programming Language, template metaprogramming, |
|---|
| 389 |
variadic templates, tuples, currying |
|---|
| 390 |
META_DESCRIPTION=Tuples in the D programming language |
|---|