diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index ffc5865bc..a9a939419 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -22,7 +22,7 @@
/** @file iter-explorer.hpp
** Helper template(s) for establishing various evaluation strategies for hierarchical data structures.
- ** Based on the Lumiera Forward Iterators concept and using the basic IterAdaptor templates,
+ ** Based on the Lumiera Forward Iterator concept and using the basic IterAdaptor templates,
** these components allow to implement typical evaluation strategies, like e.g. depth-first or
** breadth-first exploration of a hierarchical structure. Since the access to this structure is
** abstracted through the underlying iterator, what we effectively get is a functional datastructure.
@@ -32,10 +32,10 @@
** \par Iterators as Monad
** The fundamental idea behind the implementation technique used here is the \em Monad pattern
** known from functional programming. A Monad is a (abstract) container created by using some specific functions.
- ** This is an quite abstract concept with a wide variety of applications (things like IO state, parsers, combinators,
- ** calculations with exception handling but also simple data structures like lists or trees). The key point with
- ** any monad is the ability to \em bind a function into the monad; this function will work on the \em internals
- ** of the monad and produce a modified new monad instance. In the simple case of a list, "binding" a function
+ ** This is an rather abstract concept with a wide variety of applications (things like IO state, parsers, combinators,
+ ** calculations with exception handling but also simple data structures like lists or trees). The key point with any
+ ** monad is the ability to \em bind a function into the monad; this function will work on the \em internals of the
+ ** monad and produce a modified new monad instance. In the simple case of a list, "binding" a function
** basically means to map the function onto the elements in the list.
**
** \par Rationale
@@ -168,6 +168,12 @@ namespace lib {
* independent, new result sequence based on this first element).
* Afterwards, the source is \em advanced and then \em copied
* into the result iterator.
+ * @param SRC the source sequence or iterator to wrap
+ * @param _COM_ "Combinator" strategy template. When binding (\c >>= ) a function,
+ * an instance of that strategy becomes the new SRC for the created new
+ * IterExplorer. This instantiation of the strategy gets as type parameters
+ * - this IterExplorer's instance type
+ * - the type of the function bound with \c >>=
*/
template class _COM_ = iter_explorer::DefaultCombinator
@@ -187,7 +193,7 @@ namespace lib {
IterExplorer() { }
- /** wrap an iterator like state representation
+ /** wrap an iterator-like state representation
* to build it into a monad. The resulting entity
* is both an iterator yielding the elements generated
* by the core, and it provides the (monad) bind operator.
@@ -247,29 +253,64 @@ namespace lib {
namespace iter_explorer { ///< predefined policies and configurations
using util::unConst;
-
+
/**
- * a generic "Combinator strategy" for IterExplorer.
- * This fallback solution doesn't assume anything beyond the source
- * and the intermediary result(s) being a Lumiera Forward Iterators.
- * @note the implementation stores the functor into a std::function object,
- * which might cause heap allocations, depending on given function.
- * Besides, the implementation holds one instance of the (intermediary)
- * result iterator (yielded by invoking the function) and a copy of the
- * original IterExplorer source sequence, to get the further elements
- * when the initial results are exhausted.
+ * Building block: evaluating source elements.
+ * This strategy will be tied into a "Combinator"
+ * to hold the actual functor bound into the enclosing
+ * IterExplorer monad to work on the contained elements.
*/
- template
- class DefaultCombinator
+ template
+ struct ExploreByFunction
+ : function
{
- typedef typename _Fun::Ret ResultIter;
- typedef typename SRC::value_type SrcElement;
- typedef function Explorer;
+ template
+ ExploreByFunction(FUN explorationFunctionDefinition)
+ : function(explorationFunctionDefinition)
+ { }
-
- SRC srcSeq_;
+ ExploreByFunction() { } ///< by default initialised to bottom function
+ };
+
+ /**
+ * Support for a special use case: an Iterator of Iterators, joining results.
+ * In this case, already the source produces a sequence of Iterators, which
+ * just need to be passed through to the output buffer unaltered. Using this
+ * within the DefaultCombinator strategy creates a combined, flattened iterator
+ * of all the source iterator's contents.
+ */
+ template
+ struct UnalteredPassThrough;
+
+ template
+ struct UnalteredPassThrough
+ {
+ IT operator() (IT elm) const { return elm; }
+ };
+
+
+ /**
+ * Building block: evaluate and combine a sequence of iterators.
+ * This implementation helper provides two kinds of "buffers" (actually implemented
+ * as iterators): A result buffer (iterator) which holds a sequence of already prepared
+ * result elements, which can be retrieved through iteration right away. And a supply buffer
+ * (iterator) holding raw source elements. When the result buffer is exhausted, the next source
+ * element will be pulled from there and fed through the "evaluation strategy", which typically
+ * is a function processing the source element and producing a new result buffer (iterator).
+ */
+ template class _EXP_ = ExploreByFunction ///< Strategy: how to store and evaluate the function to apply on each element
+ >
+ class CombinedIteratorEvaluation
+ {
+ typedef typename _Fun::Ret ResultIter;
+ typedef typename SRC::value_type SrcElement;
+ typedef _EXP_ Explorer;
+
+
+ SRC srcSeq_;
ResultIter results_;
- Explorer explorer_;
+ Explorer explorer_;
public:
typedef typename ResultIter::value_type value_type;
@@ -277,9 +318,9 @@ namespace lib {
typedef typename ResultIter::pointer pointer;
- DefaultCombinator() { }
+ CombinedIteratorEvaluation() { }
- DefaultCombinator(FUN explorerFunction)
+ CombinedIteratorEvaluation(FUN explorerFunction)
: srcSeq_()
, results_()
, explorer_(explorerFunction)
@@ -289,13 +330,13 @@ namespace lib {
void
- startWith (ResultIter firstExplorationResult)
+ setResultSequence (ResultIter firstExplorationResult)
{
results_ = firstExplorationResult;
}
void
- followUp (SRC & followUpSourceElements)
+ setSourceSequence (SRC & followUpSourceElements)
{
REQUIRE (explorer_);
srcSeq_ = followUpSourceElements;
@@ -316,24 +357,72 @@ namespace lib {
/* === Iteration control API for IterStateWrapper== */
friend bool
- checkPoint (DefaultCombinator const& seq)
+ checkPoint (CombinedIteratorEvaluation const& seq)
{
return unConst(seq).findNextResultElement();
}
friend reference
- yield (DefaultCombinator const& seq)
+ yield (CombinedIteratorEvaluation const& seq)
{
return *(seq.results_);
}
friend void
- iterNext (DefaultCombinator & seq)
+ iterNext (CombinedIteratorEvaluation & seq)
{
++(seq.results_);
}
};
+ /**
+ * a generic "Combinator strategy" for IterExplorer.
+ * This fallback solution doesn't assume anything beyond the source
+ * and the intermediary result(s) being a Lumiera Forward Iterators.
+ * @note the implementation stores the functor into a std::function object,
+ * which might cause heap allocations, depending on given function.
+ * Besides, the implementation holds one instance of the (intermediary)
+ * result iterator (yielded by invoking the function) and a copy of the
+ * original IterExplorer source sequence, to get the further elements
+ * when the initial results are exhausted.
+ */
+ template
+ class DefaultCombinator
+ : public CombinedIteratorEvaluation
+ {
+ typedef typename _Fun::Ret ResultIter;
+
+ public:
+ DefaultCombinator() { }
+
+ DefaultCombinator(FUN explorerFunction)
+ : CombinedIteratorEvaluation(explorerFunction)
+ { }
+
+
+ void
+ startWith (ResultIter firstExplorationResult)
+ {
+ this->setResultSequence (firstExplorationResult);
+ }
+
+ void
+ followUp (SRC & followUpSourceElements)
+ {
+ this->setSourceSequence (followUpSourceElements);
+ }
+ };
+
+
+ /**
+ * Special configuration for combining / flattening the results
+ * of a sequence of iterators
+ */
+ template
+ class ChainedIters
+ {
+ /////////////TODO unimplemented
+ };
/**
* Helper template to bootstrap a chain of IterExplorers.
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 88001f510..070fbbeca 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -61,7 +61,7 @@ namespace test{
/**
* This iteration state type describes
- * a sequence of numbers still to be delivered.
+ * a sequence of numbers yet to be delivered.
*/
class State
{
@@ -385,10 +385,11 @@ namespace test{
* of a tree like system, built on top of the IterExplorer monad.
*
* \par Test data structure
- * We built a functional datastructure here, on the fly, while exploring it.
+ * We build a functional datastructure here, on the fly, while exploring it.
* The \c exploreChildren(m) function generates this tree like datastructure:
* For a given node with number value i, it returns the children (2*i-1) and 2*i
* -- but only up to the given maximum value m.
+ *
* If we start such a tree structure with a root node 1, and maximum = 8, this yields:
* \code
* ( 1 )
@@ -400,9 +401,9 @@ namespace test{
*
* \par How the exploration works
* We use a pre defined Template \link DepthFirstExplorer \endlink, which is built on top of IterExplorer.
- * It contains the depth-first exploration strategy hardwired. Actually this effect is achieved by
- * defining a specific way how to \em combine the results of a \em exploration -- the latter being
- * the function which generates the data structure. To yield a depth-first exploration, all we have to do
+ * It contains the depth-first exploration strategy in a hardwired fashion. Actually this effect is achieved
+ * by defining a specific way how to \em combine the results of an \em exploration -- the latter being the
+ * function which generates the data structure. To yield a depth-first exploration, all we have to do
* is to delve down immediately into the children, right after visiting the node itself.
*
* Now, when creating such a DepthFirstExplorer by wrapping a given source iterator, the result is again
@@ -421,8 +422,8 @@ namespace test{
typedef DepthFirstExplorer DepthFirst;
DepthFirst root (seq(1));
- NumberSequence explorationResult = root >>= exploreChildren(8);
- CHECK (materialise(explorationResult) == "1-1-1-1-2-2-3-4-2-3-5-6-4-7-8");
+ string explorationResult = materialise(root >>= exploreChildren(8));
+ CHECK (explorationResult == "1-1-1-1-2-2-3-4-2-3-5-6-4-7-8");
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
}
@@ -441,8 +442,8 @@ namespace test{
typedef BreadthFirstExplorer BreadthFirst;
BreadthFirst root (seq(1));
- NumberSequence explorationResult = root >>= exploreChildren(8);
- CHECK (materialise(explorationResult) == "1-1-2-1-2-3-4-1-2-3-4-5-6-7-8");
+ string explorationResult = materialise(root >>= exploreChildren(8));
+ CHECK (explorationResult == "1-1-2-1-2-3-4-1-2-3-4-5-6-7-8");
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
}
@@ -504,10 +505,9 @@ namespace test{
{
return seq(0,top);
}
-
-
};
+
LAUNCHER (IterExplorer_test, "unit common");