1 /**
2 Copyright: Copyright (c) 2018 Frank McSherry
3 License: MIT
4 Author: Joakim BrännströmJoakim Brännström (joakim.brannstrom@gmx.com)
5 
6 Port of DataFrog to D.
7 
8 Functionality for mapping a function on a variable.
9 */
10 module datacat.map;
11 
12 import std.traits : hasMember;
13 
14 version (unittest) {
15     import unit_threaded;
16     import datacat : Relation, ThreadStrategy;
17 }
18 
19 // TODO: add constraint on Fn that the param is T1 returning T2.
20 /**
21  *
22  * The task pool from `output` is used if the `ThreadStrategy` is parallel.
23  *
24  * Params:
25  *  output = the result of the join
26  */
27 private void mapInto(alias logicFn, InputT, OutputT)(InputT input, OutputT output) {
28     import std.array : appender;
29 
30     auto results = appender!(OutputT.TT[])();
31 
32     foreach (v; input.recent)
33         results.put(logicFn(v));
34 
35     output.insert(results.data);
36 }
37 
38 /** Adds tuples that result from mapping `input`.
39  */
40 template fromMap(Args...) if (Args.length == 1) {
41     auto fromMap(Self, I1)(Self self, I1 i1) if (is(Self == class)) {
42         import std.functional : unaryFun;
43 
44         alias fn_ = unaryFun!(Args[0]);
45         return mapInto!(fn_)(i1, self);
46     }
47 }
48 
49 /**
50  * This example starts a collection with the pairs (x, x) for x in 0 .. 10. It then
51  * repeatedly adds any pairs (x, z) for (x, y) in the collection, where z is the Collatz
52  * step for y: it is y/2 if y is even, and 3*y + 1 if y is odd. This produces all of the
53  * pairs (x, y) where x visits y as part of its Collatz journey.
54  */
55 @("shall be the tuples that result from applying a function on the input")
56 unittest {
57     import std.algorithm : map, filter;
58     import std.range : iota;
59     import datacat : Iteration, kvTuple;
60 
61     // arrange
62     Iteration iter;
63     auto variable = iter.variable!(int, int)("source");
64     variable.insert(iota(10).map!(x => kvTuple(x, x)));
65 
66     // act
67     while (iter.changed) {
68         variable.fromMap!((a) => a.value % 2 == 0 ? kvTuple(a.key, a.value / 2)
69                 : kvTuple(a.key, 3 * a.value + 1))(variable);
70     }
71 
72     auto result = variable.complete;
73 
74     // assert
75     result.length.should == 74;
76 }