Stroika Library 3.0d16
 
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"
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
93namespace 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 */
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>
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_ */
OUT_T Reduce(const function< OUT_T(T, OUT_T)> &do2Each, OUT_T memo=OUT_T())
FunctionalApplicationContext< OUT_T, MAPPER_ENGINE > Map(const function< OUT_T(T)> &do2Each)
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237