From d10c5a4f7796f823612b5b0dfd1b295d3d6f8bd4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 19 Nov 2017 20:36:19 +0100 Subject: [PATCH] TreeExplorer: draft the core (explore) operation The plan is to use a monad-like scheme, but allow for a lot of leeway with respect to the src and value types of the expand functor. A key idea is to allow for a *different* state core than used in the source --- src/lib/iter-tree-explorer.hpp | 180 ++++++++++++++-------- tests/library/iter-tree-explorer-test.cpp | 113 +++++++++----- 2 files changed, 192 insertions(+), 101 deletions(-) diff --git a/src/lib/iter-tree-explorer.hpp b/src/lib/iter-tree-explorer.hpp index 31e95f695..d49c24b33 100644 --- a/src/lib/iter-tree-explorer.hpp +++ b/src/lib/iter-tree-explorer.hpp @@ -74,6 +74,7 @@ #include "lib/error.hpp" #include "lib/meta/trait.hpp" #include "lib/meta/duck-detector.hpp" +#include "lib/meta/function.hpp" #include "lib/iter-adapter.hpp" #include "lib/iter-stack.hpp" #include "lib/meta/trait.hpp" ////////////////TODO @@ -83,76 +84,17 @@ //#include //////////////TODO #include ////////////////TODO #include +#include namespace lib { - namespace iter_explorer { - - ////////////TODO - } - - - - /** - * Adapter to build a demand-driven tree expanding and exploring computation - * based on a custom opaque _state core_. TreeExploer adheres to the _Monad_ - * pattern known from functional programming, insofar the _expansion step_ is - * tied into the basic template by means of a function provided at usage site. - * - * @todo WIP -- preliminary draft as of 11/2017 - */ - template - class TreeExplorer -// : public IterStateWrapper - : public SRC - { - - - public: - typedef typename SRC::value_type value_type; - typedef typename SRC::reference reference; - typedef typename SRC::pointer pointer; - - - /** by default create an empty iterator */ - TreeExplorer() { } - - - /** 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. - */ - explicit - TreeExplorer (SRC iterStateCore) - : SRC{std::move (iterStateCore)} - { } - - - private: - }; - - - - - - - - - + using std::move; + using std::forward; + using std::function; namespace iter_explorer { - /////TODO RLY? - -// using util::unConst; -// using lib::meta::enable_if; -// using lib::meta::disable_if; -// using std::function; -// using meta::_Fun; - template using iterator = typename meta::Strip::TypeReferred::iterator; template @@ -182,6 +124,7 @@ namespace lib { }//(End) namespace iter_explorer : predefined policies and configurations + namespace { // TreeExplorer traits using meta::enable_if; @@ -244,6 +187,117 @@ namespace lib { + namespace iter_source { + + template + class Expander + : public EXP + { + function expandChildren_; + + public: + Expander() =default; + // inherited default copy operations + + template + Expander (EXP&& parentExplorer, FUN&& expandFunctor) + : EXP{move (parentExplorer)} + , expandChildren_{forward (expandFunctor)} + { } + + + /** core operation: expand current head element */ + Expander& + expand() + { + UNIMPLEMENTED ("expand-children core operation"); + } + + /** diagnostics: current level of nested child expansion */ + size_t + depth() const + { + UNIMPLEMENTED ("implementation of the expand mechanics"); + } + }; + } + + + + + /** + * Adapter to build a demand-driven tree expanding and exploring computation + * based on a custom opaque _state core_. TreeExploer adheres to the _Monad_ + * pattern known from functional programming, insofar the _expansion step_ is + * tied into the basic template by means of a function provided at usage site. + * + * @todo WIP -- preliminary draft as of 11/2017 + */ + template + class TreeExplorer + : public SRC + { + + + public: + typedef typename SRC::value_type value_type; + typedef typename SRC::reference reference; + typedef typename SRC::pointer pointer; + + + /** by default create an empty iterator */ + TreeExplorer() { } + + // default copy acceptable (unless prohibited by nested state core) + + + /** 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. + */ + explicit + TreeExplorer (SRC iterStateCore) + : SRC{std::move (iterStateCore)} + { } + + + /* ==== Builder functions ==== */ + + template + auto + expand (FUN&& expandFunctor) + { + using FunSig = typename meta::_Fun::Sig; + using This = typename meta::Strip::TypeReferred; + + return iter_source::Expander {move(*this), forward(expandFunctor)}; + } + private: + }; + + + + + + + + + + + namespace iter_explorer { + + /////TODO RLY? + +// using util::unConst; +// using lib::meta::enable_if; +// using lib::meta::disable_if; +// using std::function; +// using meta::_Fun; + } + + /* ==== convenient builder free functions ==== */ diff --git a/tests/library/iter-tree-explorer-test.cpp b/tests/library/iter-tree-explorer-test.cpp index dde2e6fd3..b075b02b9 100644 --- a/tests/library/iter-tree-explorer-test.cpp +++ b/tests/library/iter-tree-explorer-test.cpp @@ -86,30 +86,30 @@ namespace test{ * This iteration _"state core" type_ describes * a sequence of numbers yet to be delivered. */ - class State + class CountDown { uint p,e; public: - State(uint start =0, uint end =0) + CountDown(uint start =0, uint end =0) : p(start) , e(end) { } friend bool - checkPoint (State const& st) + checkPoint (CountDown const& st) { return st.p > st.e; } friend uint& - yield (State const& st) + yield (CountDown const& st) { return util::unConst(checkPoint(st)? st.p : st.e); } friend void - iterNext (State & st) + iterNext (CountDown & st) { if (not checkPoint(st)) return; --st.p; @@ -123,33 +123,19 @@ namespace test{ * The tests will dress up this source sequence in various ways. */ class NumberSequence - : public IterStateWrapper + : public IterStateWrapper { public: explicit NumberSequence(uint end = 0) - : IterStateWrapper (State(0,end)) + : IterStateWrapper (CountDown(0,end)) { } NumberSequence(uint start, uint end) - : IterStateWrapper (State(start,end)) + : IterStateWrapper (CountDown(start,end)) { } }; - inline NumberSequence - seq (uint end) - { - return NumberSequence(end); - } - - inline NumberSequence - seq (uint start, uint end) - { - return NumberSequence(start, end); - } - - NumberSequence NIL_Sequence; - /** Diagnostic helper: "squeeze out" the given iterator @@ -232,9 +218,9 @@ namespace test{ verify_wrappedState(); verify_wrappedIterator(); - verify_mapOperation(); verify_expandOperation(); - verify_expandMapCombination(); + verify_transformOperation(); + verify_combinedExpandTransform(); verify_depthFirstExploration(); demonstrate_LayeredEvaluation(); @@ -248,7 +234,7 @@ namespace test{ void verify_wrappedState() { - auto ii = treeExplore (State{5,0}); + auto ii = treeExplore (CountDown{5,0}); CHECK (!isnil (ii)); CHECK (5 == *ii); ++ii; @@ -260,11 +246,11 @@ namespace test{ VERIFY_ERROR (ITER_EXHAUST, *ii ); VERIFY_ERROR (ITER_EXHAUST, ++ii ); - ii = treeExplore (State{5}); + ii = treeExplore (CountDown{5}); CHECK (materialise(ii) == "5-4-3-2-1"); - ii = treeExplore (State{7,4}); + ii = treeExplore (CountDown{7,4}); CHECK (materialise(ii) == "7-6-5"); - ii = treeExplore (State{}); + ii = treeExplore (CountDown{}); CHECK ( isnil (ii)); CHECK (!ii); } @@ -302,19 +288,70 @@ namespace test{ - /** @test pipe each result through a transformation function - */ - void - verify_mapOperation() - { - UNIMPLEMENTED("map function onto the results"); - } - - /** @test use a preconfigured "expand" functor to recurse into children + * The `expand()` builder function predefines a way how to _expand_ the current + * head element of the iteration. However, expansion does not happen automatically, + * rather, it needs to be invoked by the client, similar to increment of the iterator. + * When expanding, the current head element is consumed and fed into the expand functor; + * the result of this functor invocation is injected instead into the result sequence, + * and consequently this result needs to be again an iterable with compatible value type. + * Conceptually, the evaluation _forks into the children of the expanded element_, before + * continuing with the successor of the expansion point. Obviously, expansion can be applied + * again on the result of the expansion, possibly leading to a tree of side evaluations. */ void verify_expandOperation() + { + auto ii = treeExplore(CountDown{5}) + .expand([](uint j){ return CountDown{j-1}; }) + ; + + CHECK (!isnil (ii)); + CHECK (5 == *ii); + ++ii; + CHECK (4 == *ii); + + CHECK (0 == ii.depth()); + ii.expand(); + CHECK (3 == *ii); + CHECK (1 == ii.depth()); + ++ii; + CHECK (2 == *ii); + CHECK (1 == ii.depth()); + ii.expand(); + CHECK (1 == *ii); + CHECK (2 == ii.depth()); + ++ii; + CHECK (1 == *ii); + CHECK (1 == ii.depth()); + ++ii; + CHECK (3 == *ii); + CHECK (0 == ii.depth()); + CHECK (materialise(ii) == "3-2-1"); + ii.expand(); + CHECK (1 == ii.depth()); + CHECK (materialise(ii) == "2-1-2-1"); + ++++ii; + CHECK (0 == ii.depth()); + CHECK (materialise(ii) == "2-1"); + ii.expand(); + CHECK (1 == ii.depth()); + CHECK (materialise(ii) == "1-1"); + ++ii; + CHECK (0 == ii.depth()); + CHECK (1 == *ii); + CHECK (materialise(ii) == "1"); + ii.expand(); + CHECK (isnil (ii)); + VERIFY_ERROR (ITER_EXHAUST, *ii ); + VERIFY_ERROR (ITER_EXHAUST, ++ii ); + } + + + /** @test pipe each result through a transformation function + */ + void + verify_transformOperation() { UNIMPLEMENTED("expand children"); } @@ -323,7 +360,7 @@ namespace test{ /** @test combie the recursion into children with a tail mapping operation */ void - verify_expandMapCombination() + verify_combinedExpandTransform() { UNIMPLEMENTED("combine child expansion and result mapping"); }