From 1683439b320d67eb2ac6707aaaa1c6ba18fc9ecc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 16 Sep 2018 00:26:46 +0200 Subject: [PATCH] ChainSearch: backtracking verified -- finished --- src/lib/iter-chain-search.hpp | 41 ++++----- src/lib/iter-tree-explorer.hpp | 2 +- tests/library/iter-chain-search-test.cpp | 49 ++++++++--- wiki/thinkPad.ichthyo.mm | 104 ++++++++++++----------- 4 files changed, 117 insertions(+), 79 deletions(-) diff --git a/src/lib/iter-chain-search.hpp b/src/lib/iter-chain-search.hpp index 6b3a13666..d8913318c 100644 --- a/src/lib/iter-chain-search.hpp +++ b/src/lib/iter-chain-search.hpp @@ -24,7 +24,7 @@ /** @file iter-chain-search.hpp ** Evaluation mechanism to apply a sequence of conditions onto a linear search. ** This search algorithm is implemented on top of a tree expanding (monadic) filter pipeline, - ** to allow for backtracking. The intention is not to combine the individual conditions, but + ** to allow for backtracking. The intention is not just to combine the individual conditions, but ** rather to apply them one by one. After finding a match for the first condition, we'll search ** for the next condition _starting at the position of the previous match_. In the most general ** case, this immediate progression down the search chain might be too greedy; it could be that @@ -36,10 +36,10 @@ ** ## Design ** ** The IterChainSearch component is built as a processing pipeline, based on the - ** [Tree-Explorer framework](\ref iter-tree-explorer.hpp). This yields several benefits, yet also - ** imposes some drawbacks. Without much effort, we get a extremely flexible and configurable - ** solution, with acceptable / moderate performance. The result automatically adapts to a wide - ** array of data sources; it is possible (and even intended) to attach it on top of an already + ** [Tree-Explorer framework](\ref iter-tree-explorer.hpp). This yields several benefits, but + ** imposes some drawbacks. Without much effort, we get an extremely flexible and configurable + ** solution, with acceptable / moderate performance. The result automatically adapts to a huge + ** selection of data sources; it is possible (and is even intended) to attach it on top of an ** existing on-demand data processing pipeline. The "source" can itself be a "state core" and ** we may install suitable filter predicates to possibly collaborate with the internals of ** such a state core. Yet we may also confine ourselves to pure functions and value processing. @@ -47,9 +47,9 @@ ** The obvious downside of such an approach is its complexity in terms of code to understand. ** The collaboration between several layers in a filter pipeline can be intricate, leading to ** chains of layered similar functions calling each other. Most of these abstractions and - ** decorations will be removed by the compiler and optimiser, typically leading to long + ** decorations will be resolved by the compiler and optimiser, typically leading to long ** compilation times and rather large size of the generated code (which in debugging mode - ** with full type information can even become excessively huge). + ** with full type information can even become excessively large). ** ** Some caveats ** - the resulting pipeline is copyable, and it is typically moved out from a builder @@ -63,10 +63,11 @@ ** functions still accessible. The only functions meant to be used in ** builder style are the IterChainSearch::search() variations. ** - if you copy, the embedded state is copied alongside, but not - ** any external state referred by it. + ** any further external state referred by it. ** + ** @see IterChainSearch_test ** @see IterCursor_test - ** @see iter-adapter.hpp + ** @see iter-tree-explorer.hpp ** @see [usage example](event-log.hpp) ** */ @@ -110,7 +111,7 @@ namespace iter { buildExplorer (SRC&& dataSource) { return buildSearchFilter (forward (dataSource)) - .expand ([](auto it){ return it; }); // child iterator starts as copy of current level iterator + .expand ([](auto it){ return it; }); // child iterator starts as copy of current level iterator } /** @@ -128,6 +129,7 @@ namespace iter { using Filter = decltype( buildSearchFilter(std::declval()).asIterator() ); using Pipeline = decltype( buildExplorer (std::declval()) ); + /** each step in the chain is a functor to reconfigure the underlying filter */ using StepFunctor = std::function; }; @@ -137,13 +139,13 @@ namespace iter { - /** + /*********************************************************************************************************//** * Iterator based linear search mechanism, with the ability to perform consecutive search with backtracking. * The IterChainSearch can be configured with a sequence of search goals (filter conditions), and will apply * these in succession on the underlying iterator. It will search _by linear search_ for the first hit of the * first condition, and then continue to search _from there_ matching on the second condition, and so on. * After the first combination of matches is exhausted, the search will backtrack and try to evaluate - * the next combination, leading to a tree of search solutions. + * the next combination, leading to a tree of on-demand search solutions. */ template class IterChainSearch @@ -177,7 +179,7 @@ namespace iter { explicit IterChainSearch (SEQ&& srcData) : _Base{buildExplorer (forward (srcData))} - { // mark initial pristine state + { // mark initial pristine state _Base::disableFilter(); } @@ -216,7 +218,7 @@ namespace iter { * to possibly change its configuration. * @note the given functor, lambda or function reference will be wrapped and adapted * to conform to the required function signature. When using a generic lambda, - * the argument type `Filter&` is assumed + * the argument type `Filter&` is used for instantiation * @remarks the additional chained search condition given here will be applied _after_ * matching all other conditions already in the filter chain. Each such condition * is used to _filter_ the underlying source iterator, i.e. pull it until finding @@ -235,7 +237,7 @@ namespace iter { if (_Base::isDisabled()) nextStep (*this); // apply first step immediately - else + else // (implicitly enables the base filter) { stepChain_.emplace_back (move (nextStep)); // append all further steps into the chain... this->iterNext(); // then establish invariant: @@ -295,14 +297,15 @@ namespace iter { /* ==== convenient builder free function ==== */ /** setup a chain search configuration by suitably wrapping the given container. - * @return a TreeEplorer, which is an Iterator to yield all the source elements, - * but may also be used to build an processing pipeline. + * @return an IterChainSearch instantiation, which is an Iterator to yield all the + * source elements, but can be outfitted with a sequence of filter conditions, + * to be applied to the underlying source one by one. + * @param srcData either a »Lumiera Forward Iterator«, a _reference_ to a STL + * container, or a [»State Core«](\ref lib::IterStateWrapper) object. * @warning if you capture the result of this call by an auto variable, * be sure to understand that invoking any further builder operation on * TreeExplorer will invalidate that variable (by moving it into the * augmented iterator returned from such builder call). - * @param srcData either a »Lumiera Forward Iterator«, a _reference_ to a STL - * container, or a [»State Core«](\ref lib::IterStateWrapper) object. */ template inline auto diff --git a/src/lib/iter-tree-explorer.hpp b/src/lib/iter-tree-explorer.hpp index ebba4bbc4..fe896534e 100644 --- a/src/lib/iter-tree-explorer.hpp +++ b/src/lib/iter-tree-explorer.hpp @@ -719,7 +719,7 @@ namespace lib { invariant() const { return not hasChildren() - or *expansions_; + or expansions_->isValid(); } void diff --git a/tests/library/iter-chain-search-test.cpp b/tests/library/iter-chain-search-test.cpp index a42cb8d5a..e76aa14b8 100644 --- a/tests/library/iter-chain-search-test.cpp +++ b/tests/library/iter-chain-search-test.cpp @@ -28,9 +28,9 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" -#include "lib/format-util.hpp" /////////////////////////////TODO necessary? -#include "lib/format-cout.hpp" /////////////////////////////TODO necessary? +#include "lib/format-util.hpp" #include "lib/iter-chain-search.hpp" +#include "lib/iter-cursor.hpp" #include "lib/util.hpp" #include @@ -80,18 +80,22 @@ namespace test{ -///////////////////////////////////////////////////TODO WIP -#define SHOW_TYPE(_TY_) \ - cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::meta::typeStr<_TY_>() <; + + auto search = chainSearch(Cursor{SPAM.begin(), SPAM.end()}) + .search("spam") + .addStep([](auto& filter) + { + filter.switchBackwards(); // switch search direction without moving the cursor + filter.flipFilter(); // flip from match on "spam" to match on non-spam + }) + .addStep([](auto& filter) + { // filter is still configured to search non-spam backwards + ++filter; // just "advance" this filter by one step (backward) + }); + + CHECK (materialise (search) // Layer-0: 1-3 spam fail altogether, too greedy. Try 4rd spam.... + // Layer-1: turn back, toggle to non-spam, find bacon + == "sausage-" // Layer-2: non-spam and one step further backwards yields sausage + // + // BACKTRACK to Layer-0: pick 5th (and last) spam... + // Layer-1: turn back, toggle to non-spam, find "and" + "tomato-bacon-sausage-" // Layer-2: non-spam and one step back yields tomato, next bacon, next sausage. + // BACKTRACK to Layer-1: take previous one, which is tomato + "bacon-sausage-" // Layer-2: non-spam and one step further back yields bacon, then next sausage. + // BACKTRACK to Layer-1: take previous non-spam, which is bacon + "sausage" // Layer-2: non-spam and one step further back yields sausage. + ""); // BACKTRACK to Layer-1: exhausted, BACKTRACK to Layer-0: exhausted. Done. } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 799e1047a..0dbe24d12 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -30764,7 +30764,11 @@ - + + + + + @@ -31259,7 +31263,8 @@ - + + @@ -31709,9 +31714,9 @@ - + - + @@ -31720,8 +31725,8 @@ - - + + @@ -31904,7 +31909,7 @@ - + @@ -32108,7 +32113,7 @@ - + @@ -32172,7 +32177,34 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -32213,8 +32245,8 @@ - - + + @@ -32321,9 +32353,9 @@ - + - + @@ -32462,9 +32494,9 @@ - - - + + + @@ -32502,41 +32534,15 @@ - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - + + @@ -32549,11 +32555,11 @@ - - + + - - + +