Stroika Library
3.0d16
Help-Home
Loading...
Searching...
No Matches
FunctionalApplication.h
Go to the documentation of this file.
1
/*
2
* Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3
*/
4
#ifndef _Stroika_Foundation_Traversal_FunctionalApplication_h_
5
#define _Stroika_Foundation_Traversal_FunctionalApplication_h_ 1
6
7
#include "Stroika/Foundation/StroikaPreComp.h"
8
9
#include <limits>
10
#include <optional>
11
12
#include "Stroika/Foundation/Common/Common.h"
13
#include "
Stroika/Foundation/Traversal/Iterator.h
"
14
15
/**
16
* \file
17
*
18
* STATUS: PRELIMINARY DRAFT.
19
*
20
* NOTES:
21
*
22
* TODO:
23
*
24
* @todo add demo project so shows how to use (cleaned up versions of the regtest code)
25
* Maybe something using bignums (don't exist yet) and the multithreaded threadpool
26
* mapper (doesn't exist yet) - showing how to run multiple IsPrime's at a time in the
27
* context of this mapping stuff.
28
*
29
* @todo Consider that we COULD implement something like http://underscorejs.org/#pluck
30
* if we used VariantValue - instead of "T" types. That would have some merrits.
31
*
32
* Also - using that style (along with ObjectVariantMapper) could allow us to have
33
* the mapping-engine's not be template based - which might make them easier to
34
* implement (private/.cpp file).
35
*
36
* @todo do impl of pull-based iterator stuff
37
*
38
* (or at least document deisgn basics)
39
* .. mujst have filter etc funtions not do anything but acuulate lambdas.
40
* then construct inverted compuation as iterator.
41
*
42
* MAYBE can do by having each piece that reutrns an iterator do the work
43
* of priducing apull based iteraotr?
44
* THAT SEEM BEST!
45
*
46
* @todo DOCUMENT/think through the design choice if Map, and Filter etc - MUST return
47
* items IN-ORDER. By allowing them to return stuff out of order, they CAN
48
* by implemented much more efficiently. Probaly have two functions - Map and Map_UO
49
* where they do the same thing but Map_UO is not necessarily ordreed.
50
*
51
* @todo NOTE that DirectPushMapEngine is bad, and we want a pull-based one ASAP.
52
*
53
* @todo Mapper's must be written in such a way that they are smart-pointers nad
54
* pass data from first call to last.
55
*
56
* @todo Need to simplify usage further. Example usage is too wordy. See if we can better use templates
57
* and defaulting to make more brief basic filter usage.
58
*
59
* @todo See http://underscorejs.org/#filter - and add filter, and probably others - but not sure what
60
* to call these (functional helpers?)
61
* Same for http://underscorejs.org/#pluck
62
*
63
* @todo CONCEPTS!
64
* o use std::function<>... with particular arguments to advertise the
65
* required type signature,
66
* o OR use typename FUNCTION - better performance performance - TEST.
67
*
68
* We should verify if there is truely a performance difference, and if there is, then find
69
*
70
* \em Design Overview
71
* This code is based in large part on
72
* o conversations with kdj 2013-10-05
73
* o @see http://en.wikipedia.org/wiki/MapReduce
74
* o @see http://www.ruby-doc.org/core-2.0/Array.html#method-i-map
75
* o @see http://underscorejs.org/#reduce
76
* o Python/SQL Alchemy (filter chains).
77
* o The code is in part based on ideas for pre-release java 8 streams.
78
* o http://java.dzone.com/articles/exploring-java8-lambdas-part-1
79
*
80
* The gist is to have a MAPPING_ENGINE - which can either do direct applicaiton and buffering, or
81
* pull based compuation (where calls are done in final iterator as you pull results)
82
* or done with the first case - applicaiton context/buffering - but via thread pools, or some other strategy.
83
*
84
* All this is to do the computation for traditional basic functional programming algorithms, like map/reduce, and filters
85
* etc.
86
*
87
* To use, you simple instantiate a FunctionalApplicationContext object, and then chain together a series of filters.
88
* typically, the end result - last filter step - will be to produce an iterable (which can be trivially converted to another
89
* container type).
90
*
91
*/
92
93
namespace
Stroika::Foundation::Traversal {
94
95
/**
96
* This class should not be used directly, but as a parameter to FunctionalApplicationContext.
97
*
98
* The DirectPushMapEngine implements a very simple, basic functional computation strategy, where results are
99
* immediately computed, and within a single thread. The results are buffered, and the various map
100
* functions return iterables which pull out pre-computed and cached results.
101
*
102
* @see FunctionalApplicationContext
103
*/
104
struct
DirectPushMapEngine
{
105
template
<
typename
IN_T,
typename
OUT_T>
106
Iterable<OUT_T>
Map (
const
Iterable<IN_T>
& from,
const
function<OUT_T (IN_T)>& do2Each);
107
108
template
<
typename
IN_T,
typename
OUT_T>
109
OUT_T Reduce (
const
Iterable<IN_T>
& from,
const
function<OUT_T (IN_T, OUT_T)>& do2Each, OUT_T memo);
110
111
template
<
typename
IN_OUT_T>
112
Iterable<IN_OUT_T>
Filter (
const
Iterable<IN_OUT_T>
& from,
const
function<
bool
(IN_OUT_T)>& includeTest);
113
114
template
<
typename
IN_OUT_T>
115
optional<IN_OUT_T> Find (
const
Iterable<IN_OUT_T>
& from,
const
function<
bool
(IN_OUT_T)>& thatPassesThisTest);
116
};
117
118
/**
119
* \em Design Overview
120
* The gist is to have a MAPPING_ENGINE - which can either do direct applicaiton and buffering, or
121
* pull based compuation (where calls are done in final iterator as you pull results)
122
* or done with the first case - applicaiton context/buffering - but via thread pools, or some other strategy.
123
*
124
* All this is to do the computation for traditional basic functional programming algorithms, like map/reduce, and filters
125
* etc.
126
*
127
* To use, you simple instantiate a FunctionalApplicationContext object, and then chain together a series of filters.
128
* typically, the end result - last filter step - will be to produce an iterable (which can be trivially converted to another
129
* container type).
130
*
131
* \par Example Usage
132
* \code
133
* COMMON/SETUP:
134
* Sequence<int> s = { 1, 2, 3 };
135
* \endcode
136
*
137
* \par DANGEROUS/BAD EXAMPLE
138
* \code
139
* {
140
* int countSoFar = 0;
141
* int answer =
142
* FunctionalApplicationContext<int>(s).
143
* Filter ([&countSoFar] (int) -> bool { ++countSoFar; return countSoFar & 1; }).
144
* Map<int> ([] (int s) { return s + 5; }).
145
* Reduce<size_t> ([] (int s, size_t memo) { return memo + 1; })
146
* ;
147
* EXPECT_TRUE (answer == 2);
148
* }
149
* \endcode
150
*
151
* This will work, but ONLY because all resulting objects from the compuation will be destroyed before
152
* the countSoFar goes out of scope. Since FunctionalApplicationContext<> is often used to produce an
153
* iterable that is 'returned' - its best to use a smart-pointer to store any catpured values.
154
*
155
* \par Example Usage
156
* \code
157
* {
158
* shared_ptr<int> countSoFar = shared_ptr<int> (new int (0));
159
* int answer =
160
* FunctionalApplicationContext<int>(s).
161
* Filter ([countSoFar] (int) -> bool { ++(*countSoFar); return (*countSoFar) & 1; }).
162
* Map<int> ([] (int s) { return s + 5; }).
163
* Reduce<size_t> ([] (int s, size_t memo) { return memo + 1; })
164
* ;
165
* EXPECT_TRUE (answer == 2);
166
* }
167
* \endcode
168
* \par Example Usage
169
* \code
170
* {
171
* shared_ptr<int> countSoFar = shared_ptr<int> (new int (0));
172
* Sequence<int> r = Sequence<int> (
173
* FunctionalApplicationContext<int>(s).
174
* Filter ([countSoFar] (int) -> bool { ++(*countSoFar); return (*countSoFar) & 1; }).
175
* Map<int> ([] (int s) { return s + 5; })
176
* );
177
* EXPECT_TRUE (r.length () == 2);
178
* EXPECT_TRUE (r[0] == 6 and r[1] == 8);
179
* }
180
* \endcode
181
*
182
* So you create a FunctionalApplicationContext<> - with the template parameter refering to the type of
183
* the input container.
184
*
185
* Then you chain together as many steps as you want (methods of FunctionalApplicationContext)
186
* and the final result is the end of the computation.
187
*
188
* You can optionally create the initial FunctionalApplicationContext with a selected 'mapping engine' that
189
* will use thread pools, or whatever mechanism you choose to orchestrate the computation.
190
*
191
* Note that we use std::function<> intead of typename FUNCTION - though I think this will perform
192
* the same or slower - just because I think using the explicit types will produce more
193
* comprehensible error messages from the compiler. We can always relax the definition to
194
* use typename FUNCTION in the future - hopefully - without breaking existing using code.
195
*
196
* \em What Methods and why?
197
* Much of this list of methods is based on @see http://underscorejs.org/#reduce
198
* But much doesn't make sense in stroika. So here is a rough partial summary of what
199
* is NOT included from there and why
200
*
201
* > each() - not reason to include because Iterable has 'Apply' - use that instead.
202
* > reject() - not included cuz its the same as filter() - except with NOT on lambda
203
*/
204
template
<
typename
T,
typename
MAPPER_ENGINE = DirectPushMapEngine>
205
class
FunctionalApplicationContext
:
public
Iterable
<T> {
206
private
:
207
using
inherited
=
Iterable<T>
;
208
209
private
:
210
MAPPER_ENGINE fMappingEngine_;
211
212
public
:
213
/**
214
*/
215
FunctionalApplicationContext
(
Iterable<T>
i, MAPPER_ENGINE m = MAPPER_ENGINE ());
216
217
public
:
218
/**
219
* o @see http://en.wikipedia.org/wiki/MapReduce
220
* o @see http://www.ruby-doc.org/core-2.0/Array.html#method-i-map
221
*/
222
template
<
typename
OUT_T>
223
FunctionalApplicationContext<OUT_T, MAPPER_ENGINE>
Map
(
const
function<OUT_T (T)>& do2Each);
224
225
public
:
226
/**
227
* o @see http://en.wikipedia.org/wiki/MapReduce
228
* o @see http://underscorejs.org/#reduce
229
*/
230
template
<
typename
OUT_T>
231
OUT_T
Reduce
(
const
function<OUT_T (T, OUT_T)>& do2Each, OUT_T memo = OUT_T ());
232
233
public
:
234
/**
235
*/
236
template
<
typename
INOUT_T>
237
FunctionalApplicationContext<INOUT_T, MAPPER_ENGINE>
Filter (
const
function<
bool
(INOUT_T)>& includeTest);
238
239
public
:
240
/**
241
*/
242
template
<
typename
INOUT_T>
243
optional<INOUT_T> Find (
const
function<
bool
(INOUT_T)>& that);
244
};
245
246
}
247
248
/*
249
********************************************************************************
250
******************************* Implementation Details *************************
251
********************************************************************************
252
*/
253
#include "FunctionalApplication.inl"
254
255
#endif
/*_Stroika_Foundation_Traversal_FunctionalApplication_h_ */
Iterator.h
Stroika::Foundation::Traversal::FunctionalApplicationContext
Definition
FunctionalApplication.h:205
Stroika::Foundation::Traversal::FunctionalApplicationContext::Reduce
OUT_T Reduce(const function< OUT_T(T, OUT_T)> &do2Each, OUT_T memo=OUT_T())
Definition
FunctionalApplication.inl:74
Stroika::Foundation::Traversal::FunctionalApplicationContext::Map
FunctionalApplicationContext< OUT_T, MAPPER_ENGINE > Map(const function< OUT_T(T)> &do2Each)
Definition
FunctionalApplication.inl:68
Stroika::Foundation::Traversal::Iterable
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition
Iterable.h:237
Stroika::Foundation::Traversal::DirectPushMapEngine
Definition
FunctionalApplication.h:104
Library
Sources
Stroika
Foundation
Traversal
FunctionalApplication.h
Generated by
1.9.8