From 8ced6758fbd95b39618f57e400d6d57723e5cfdf Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 24 Jun 2012 19:30:25 +0200 Subject: [PATCH] initial draft for recursive evaluating iterator monad tricky design problem, because nothing is known about the source and result sequences to be built. --- src/lib/iter-explorer.hpp | 153 +++++++++++++++++++++++++++---- tests/lib/iter-explorer-test.cpp | 33 ++++++- 2 files changed, 165 insertions(+), 21 deletions(-) diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index 569f9050b..d62cbe793 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -68,6 +68,7 @@ #include "lib/iter-adapter.hpp" #include "lib/iter-stack.hpp" #include "lib/meta/trait.hpp" +#include "lib/null-value.hpp" #include "lib/util.hpp" //#include @@ -393,7 +394,7 @@ namespace lib { }; - + /** Metafunction to detect an iterator yielding an iterator sequence */ template struct _is_iterator_of_iterators { @@ -473,7 +474,7 @@ namespace lib { - /** + /** * A "Combinator strategy" allowing to expand and evaluate a * (functional) data structure successively and recursively. * Contrary to the DefaultCombinator, here the explorer is evaluated @@ -484,8 +485,12 @@ namespace lib { * is the requirement of the source sequence's element type to be compatible * to the result sequence's element type -- we can't \em transform the contents * of the source sequence into another data type, just explore and expand those - * contents into sub-sequences based on the same data type. + * contents into sub-sequences based on the same data type. (While this contradicts + * the full requirements for building a Monad, we can always work around that kind + * of restriction by producing the element type of the target sequence by implicit + * type conversion) * + * \par strategy requirements * To build a concrete combinator a special strategy template is required to define * the actual implementation logic how to proceed with the evaluation (i.e. how to * find the feed of the "next elements" and how to re-integrate the results of an @@ -493,6 +498,10 @@ namespace lib { * Moreover, these implementation strategy pattern is used as a data buffer * to hold those intermediary results. Together, this allows to create * various expansion patterns, e.g. depth-first or breadth-first. + * - \c Strategy::getFeed() accesses the point from where + * to pull the next element to be expanded. This function must not + * yield an empty sequence, \em unless the overall exploration is exhausted + * - \c Strategy::feedBack() re-integrates the results of an expansion step */ template class _BUF_ @@ -501,37 +510,65 @@ namespace lib { { typedef typename _Fun::Ret ResultIter; typedef typename SRC::value_type Val; - typedef typename SRC::reference reference; typedef function Explorer; - typedef _BUF_ Buffer; + typedef _BUF_ Buffer; - SRC srcSequence_; Buffer resultBuf_; Explorer explore_; public: - RecursiveExhaustingEvaluation() { }; + typedef typename ResultIter::value_type value_type; + typedef typename ResultIter::reference reference; + typedef typename ResultIter::pointer pointer; RecursiveExhaustingEvaluation (Explorer fun, SRC const& src) - : srcSequence_(src) - , resultBuf_() + : resultBuf_() , explore_(fun) - { } + { + resultBuf_.feedBack( + initEvaluation (src)); + } + + RecursiveExhaustingEvaluation() { }; // standard copy operations private: - ResultIter & - feed() + /** Extension point: build the initial evaluation state + * based on the source sequence (typically an IterExplorer). + * This is a tricky problem, since the source sequence is not + * necessarily assignment compatible to the ResultIter type + * and there is no general method to build a ResultIter. + * The solution is to rely on a "builder trait", which + * needs to be defined alongside with the concrete + * ResultIter type. The actual builder trait will + * be picked up through a free function (ADL). */ + ResultIter + initEvaluation (SRC const& initialElements) { - return resultBuf_.getFeed (srcSequence_); + ResultIter startSet; + return build(startSet).usingSequence(initialElements); + } // extension point: free function build (...) + + + /** @note \c _BUF_::getFeed is required to yield a non-empty sequence, + * until everything is exhausted. Basically the buffer- and re-integrated + * result sequences can be expected to be pulled until finding the next + * non-empty supply. This is considered hidden internal state and thus + * concealed within this \em const and \em idempotent function. + */ + ResultIter & + feed() const + { + return unConst(this)->resultBuf_.getFeed(); } void iterate () { + REQUIRE (feed()); ResultIter nextStep = explore_(*feed()); ++ feed(); resultBuf_.feedBack (nextStep); @@ -543,13 +580,13 @@ namespace lib { friend bool checkPoint (RecursiveExhaustingEvaluation const& seq) { - return bool(unConst(seq).feed()); + return bool(seq.feed()); } friend reference yield (RecursiveExhaustingEvaluation const& seq) { - return *(unConst(seq).feed()); + return *(seq.feed()); } friend void @@ -560,6 +597,70 @@ namespace lib { }; + /** + * Strategy for recursive exhausting \em depth-first evaluation. + * Implemented using a heap-allocated stack of partially evaluated iterators. + * The next evaluation step will happen at the iterator returned by #getFeed, + * which is the most recently pushed intermediary result. + * @warning uses an empty-iterator marker object to signal exhaustion + * - this marker \c IT() may be re-initialised concurrently + * - accessing this marker during app shutdown might access + * an already defunct object + */ + template + class DepthFirstEvaluationBuffer + { + IterStack intermediaryResults_; + + + /** @return default constructed (=empty) iterator + * @remarks casting away const is safe here, since all + * you can do with an NIL iterator is to test for emptiness. + */ + IT & + emptySequence() + { + return unConst(NullValue::get()); + } // unsafe during shutdown + + + public: + IT & + getFeed () + { + // fast forward to find the next non-empty result sequence + while (intermediaryResults_ && ! *intermediaryResults_) + ++intermediaryResults_; + + if (intermediaryResults_) + return *intermediaryResults_; + else + return emptySequence(); + } + + void + feedBack (IT const& newEvaluationResults) + { + intermediaryResults_.push (newEvaluationResults); + } + }; + + + /** + * preconfigured IterExplorer "state core" resulting in depth-first exhaustive evaluation + */ + template + struct DepthFirstEvaluationConbinator + : RecursiveExhaustingEvaluation + { + DepthFirstEvaluationConbinator() { } + + DepthFirstEvaluationConbinator(FUN explorerFunction, SRC const& sourceElements) + : RecursiveExhaustingEvaluation (explorerFunction,sourceElements) + { } + }; + + /** * Helper template to bootstrap a chain of IterExplorers. * This is a "state core", which basically just wraps a given @@ -603,7 +704,19 @@ namespace lib { ++sequence; } }; - + + + template + struct DepthFirst + : IterExplorer, DepthFirstEvaluationConbinator> + { + DepthFirst() { }; + DepthFirst(SRC const& srcSeq) + : IterExplorer, DepthFirstEvaluationConbinator> (srcSeq) + { } + }; + + }//(End) namespace iter_explorer : predefined policies and configurations @@ -617,6 +730,14 @@ namespace lib { } + template + inline iter_explorer::DepthFirst + depthFirst (IT const& srcSeq) + { + return iter_explorer::DepthFirst (srcSeq); + } + + template inline iter_explorer::ChainedIters diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp index 150a3c050..4cf15ae23 100644 --- a/tests/lib/iter-explorer-test.cpp +++ b/tests/lib/iter-explorer-test.cpp @@ -134,6 +134,31 @@ namespace test{ NumberSequence NIL_Sequence; + + struct NumberSequenceBuilder + { + template + NumberSequence + usingSequence (IT const& src) + { + uint justOne = src? *src : 0; + return NumberSequence(justOne); + } + }; + + inline NumberSequenceBuilder + build (NumberSequence const&) + { + return NumberSequenceBuilder(); + } + + + inline NumberSequence + exploreChildren (uint top) + { + return seq(0,top/2); + } + /** Diagnostic helper: "squeeze out" the given iterator * and join all the elements yielded into a string @@ -398,14 +423,12 @@ namespace test{ void verifyDepthFirstExploration () { -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892 - typedef DepthFirstExplorer DepthFirst; - DepthFirst root (seq(30)); - - string explorationResult = materialise(root >>= exploreChildren); + string explorationResult = materialise(depthFirst(seq(4,5)) >>= exploreChildren); CHECK (explorationResult == "30-6-2-3-10-2-5-15-3-5"); +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892 #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892 } +