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