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:
parent
cbb35d7161
commit
d10c5a4f77
2 changed files with 192 additions and 101 deletions
|
|
@ -74,6 +74,7 @@
|
||||||
#include "lib/error.hpp"
|
#include "lib/error.hpp"
|
||||||
#include "lib/meta/trait.hpp"
|
#include "lib/meta/trait.hpp"
|
||||||
#include "lib/meta/duck-detector.hpp"
|
#include "lib/meta/duck-detector.hpp"
|
||||||
|
#include "lib/meta/function.hpp"
|
||||||
#include "lib/iter-adapter.hpp"
|
#include "lib/iter-adapter.hpp"
|
||||||
#include "lib/iter-stack.hpp"
|
#include "lib/iter-stack.hpp"
|
||||||
#include "lib/meta/trait.hpp" ////////////////TODO
|
#include "lib/meta/trait.hpp" ////////////////TODO
|
||||||
|
|
@ -83,76 +84,17 @@
|
||||||
//#include <boost/utility/enable_if.hpp> //////////////TODO
|
//#include <boost/utility/enable_if.hpp> //////////////TODO
|
||||||
#include <stack> ////////////////TODO
|
#include <stack> ////////////////TODO
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
namespace lib {
|
namespace lib {
|
||||||
|
|
||||||
namespace iter_explorer {
|
using std::move;
|
||||||
|
using std::forward;
|
||||||
////////////TODO
|
using std::function;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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:
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace iter_explorer {
|
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>
|
template<class CON>
|
||||||
using iterator = typename meta::Strip<CON>::TypeReferred::iterator;
|
using iterator = typename meta::Strip<CON>::TypeReferred::iterator;
|
||||||
template<class CON>
|
template<class CON>
|
||||||
|
|
@ -182,6 +124,7 @@ namespace lib {
|
||||||
|
|
||||||
}//(End) namespace iter_explorer : predefined policies and configurations
|
}//(End) namespace iter_explorer : predefined policies and configurations
|
||||||
|
|
||||||
|
|
||||||
namespace { // TreeExplorer traits
|
namespace { // TreeExplorer traits
|
||||||
|
|
||||||
using meta::enable_if;
|
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 ==== */
|
/* ==== convenient builder free functions ==== */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,30 +86,30 @@ namespace test{
|
||||||
* This iteration _"state core" type_ describes
|
* This iteration _"state core" type_ describes
|
||||||
* a sequence of numbers yet to be delivered.
|
* a sequence of numbers yet to be delivered.
|
||||||
*/
|
*/
|
||||||
class State
|
class CountDown
|
||||||
{
|
{
|
||||||
uint p,e;
|
uint p,e;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
State(uint start =0, uint end =0)
|
CountDown(uint start =0, uint end =0)
|
||||||
: p(start)
|
: p(start)
|
||||||
, e(end)
|
, e(end)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
friend bool
|
friend bool
|
||||||
checkPoint (State const& st)
|
checkPoint (CountDown const& st)
|
||||||
{
|
{
|
||||||
return st.p > st.e;
|
return st.p > st.e;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend uint&
|
friend uint&
|
||||||
yield (State const& st)
|
yield (CountDown const& st)
|
||||||
{
|
{
|
||||||
return util::unConst(checkPoint(st)? st.p : st.e);
|
return util::unConst(checkPoint(st)? st.p : st.e);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend void
|
friend void
|
||||||
iterNext (State & st)
|
iterNext (CountDown & st)
|
||||||
{
|
{
|
||||||
if (not checkPoint(st)) return;
|
if (not checkPoint(st)) return;
|
||||||
--st.p;
|
--st.p;
|
||||||
|
|
@ -123,33 +123,19 @@ namespace test{
|
||||||
* The tests will dress up this source sequence in various ways.
|
* The tests will dress up this source sequence in various ways.
|
||||||
*/
|
*/
|
||||||
class NumberSequence
|
class NumberSequence
|
||||||
: public IterStateWrapper<uint, State>
|
: public IterStateWrapper<uint, CountDown>
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit
|
explicit
|
||||||
NumberSequence(uint end = 0)
|
NumberSequence(uint end = 0)
|
||||||
: IterStateWrapper<uint,State> (State(0,end))
|
: IterStateWrapper<uint,CountDown> (CountDown(0,end))
|
||||||
{ }
|
{ }
|
||||||
NumberSequence(uint start, uint 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
|
/** Diagnostic helper: "squeeze out" the given iterator
|
||||||
|
|
@ -232,9 +218,9 @@ namespace test{
|
||||||
verify_wrappedState();
|
verify_wrappedState();
|
||||||
verify_wrappedIterator();
|
verify_wrappedIterator();
|
||||||
|
|
||||||
verify_mapOperation();
|
|
||||||
verify_expandOperation();
|
verify_expandOperation();
|
||||||
verify_expandMapCombination();
|
verify_transformOperation();
|
||||||
|
verify_combinedExpandTransform();
|
||||||
|
|
||||||
verify_depthFirstExploration();
|
verify_depthFirstExploration();
|
||||||
demonstrate_LayeredEvaluation();
|
demonstrate_LayeredEvaluation();
|
||||||
|
|
@ -248,7 +234,7 @@ namespace test{
|
||||||
void
|
void
|
||||||
verify_wrappedState()
|
verify_wrappedState()
|
||||||
{
|
{
|
||||||
auto ii = treeExplore (State{5,0});
|
auto ii = treeExplore (CountDown{5,0});
|
||||||
CHECK (!isnil (ii));
|
CHECK (!isnil (ii));
|
||||||
CHECK (5 == *ii);
|
CHECK (5 == *ii);
|
||||||
++ii;
|
++ii;
|
||||||
|
|
@ -260,11 +246,11 @@ namespace test{
|
||||||
VERIFY_ERROR (ITER_EXHAUST, *ii );
|
VERIFY_ERROR (ITER_EXHAUST, *ii );
|
||||||
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");
|
CHECK (materialise(ii) == "5-4-3-2-1");
|
||||||
ii = treeExplore (State{7,4});
|
ii = treeExplore (CountDown{7,4});
|
||||||
CHECK (materialise(ii) == "7-6-5");
|
CHECK (materialise(ii) == "7-6-5");
|
||||||
ii = treeExplore (State{});
|
ii = treeExplore (CountDown{});
|
||||||
CHECK ( isnil (ii));
|
CHECK ( isnil (ii));
|
||||||
CHECK (!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
|
/** @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
|
void
|
||||||
verify_expandOperation()
|
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");
|
UNIMPLEMENTED("expand children");
|
||||||
}
|
}
|
||||||
|
|
@ -323,7 +360,7 @@ namespace test{
|
||||||
/** @test combie the recursion into children with a tail mapping operation
|
/** @test combie the recursion into children with a tail mapping operation
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
verify_expandMapCombination()
|
verify_combinedExpandTransform()
|
||||||
{
|
{
|
||||||
UNIMPLEMENTED("combine child expansion and result mapping");
|
UNIMPLEMENTED("combine child expansion and result mapping");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue