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
This commit is contained in:
Fischlurch 2017-11-19 20:36:19 +01:00
parent cbb35d7161
commit d10c5a4f77
2 changed files with 192 additions and 101 deletions

View file

@ -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 <boost/utility/enable_if.hpp> //////////////TODO
#include <stack> ////////////////TODO
#include <utility>
#include <functional>
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 SRC
>
class TreeExplorer
// : public IterStateWrapper<typename SRC::value_type, SRC>
: 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<class CON>
using iterator = typename meta::Strip<CON>::TypeReferred::iterator;
template<class CON>
@ -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 EXP, class SIG, class SEL =void>
class Expander
: public EXP
{
function<SIG> expandChildren_;
public:
Expander() =default;
// inherited default copy operations
template<typename FUN>
Expander (EXP&& parentExplorer, FUN&& expandFunctor)
: EXP{move (parentExplorer)}
, expandChildren_{forward<FUN> (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 SRC
>
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<class FUN>
auto
expand (FUN&& expandFunctor)
{
using FunSig = typename meta::_Fun<FUN>::Sig;
using This = typename meta::Strip<decltype(*this)>::TypeReferred;
return iter_source::Expander<This, FunSig> {move(*this), forward<FUN>(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 ==== */

View file

@ -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<uint, State>
: public IterStateWrapper<uint, CountDown>
{
public:
explicit
NumberSequence(uint end = 0)
: IterStateWrapper<uint,State> (State(0,end))
: IterStateWrapper<uint,CountDown> (CountDown(0,end))
{ }
NumberSequence(uint start, uint end)
: IterStateWrapper<uint,State> (State(start,end))
: IterStateWrapper<uint,CountDown> (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");
}