ChainSearch: remould construction to get the logic correct
on closer investigation it turned out that the logic of the first design attempt was broken altogether. It did not properly support backtracking (which was the reason to start this whole exercise) and it caused dangling references within the lambda closure once the produced iterator pipeline was moved out into the target location. Reasoning from first principles then indicated that the only sane way to build such a search evaluation component is to use *two* closely collaborating layers. The actual filter configuration and evaluation logic can not reside and work from within the expander. Rather, it must sit in a layer on top and work in a conventional, imperative way (with a while loop). Sometimes, functional programming is *not* the natural way of doing things, and we should then stop attempting to force matters against their nature.
This commit is contained in:
parent
767156e912
commit
05e6e7f316
4 changed files with 202 additions and 45 deletions
|
|
@ -75,13 +75,12 @@ namespace iter {
|
|||
.mutableFilter();
|
||||
}
|
||||
|
||||
template<class SRC, class FUN>
|
||||
template<class SRC>
|
||||
auto
|
||||
buildExplorer (SRC&& dataSource, FUN&& expandFunctor)
|
||||
buildExplorer (SRC&& dataSource)
|
||||
{
|
||||
return buildSearchFilter (forward<SRC> (dataSource))
|
||||
.expand (forward<FUN> (expandFunctor))
|
||||
.expandAll();
|
||||
.expand ([](auto it){ return it; }); // child iterator starts as copy of current level iterator
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -97,16 +96,9 @@ namespace iter {
|
|||
struct _IterChainSetup
|
||||
{
|
||||
using Filter = decltype( buildSearchFilter(std::declval<SRC>()).asIterator() );
|
||||
using StepFunctor = std::function<Filter(Filter const&)>;
|
||||
|
||||
using Pipeline = decltype( buildExplorer (std::declval<SRC>(), std::declval<StepFunctor>()) );
|
||||
|
||||
static Pipeline
|
||||
configurePipeline (SRC&& dataSource, StepFunctor step)
|
||||
{
|
||||
return buildExplorer(forward<SRC> (dataSource), move (step));
|
||||
}
|
||||
using Pipeline = decltype( buildExplorer (std::declval<SRC>()) );
|
||||
|
||||
using StepFunctor = std::function<void(Filter&)>;
|
||||
};
|
||||
|
||||
}//(End)type construction helpers
|
||||
|
|
@ -134,9 +126,6 @@ namespace iter {
|
|||
using Filter = typename _Trait::Filter;
|
||||
using Step = typename _Trait::StepFunctor;
|
||||
|
||||
/** @internal access embedded filter sub-Pipeline */
|
||||
Filter& filter() { return *this; }
|
||||
|
||||
/** Storage for a sequence of filter configuration functors */
|
||||
std::vector<Step> stepChain_;
|
||||
|
||||
|
|
@ -150,9 +139,7 @@ namespace iter {
|
|||
template<class SEQ>
|
||||
explicit
|
||||
IterChainSearch (SEQ&& srcData)
|
||||
: _Base{_Trait::configurePipeline (forward<SEQ> (srcData)
|
||||
,[this](Filter const& curr){ return configureFilterChain(curr); })}
|
||||
, stepChain_{}
|
||||
: _Base{buildExplorer (forward<SEQ> (srcData))}
|
||||
{ // mark initial pristine state
|
||||
_Base::disableFilter();
|
||||
}
|
||||
|
|
@ -161,13 +148,28 @@ namespace iter {
|
|||
using _Base::_Base;
|
||||
|
||||
|
||||
/* === adapted iteration control API === */
|
||||
void
|
||||
iterNext()
|
||||
{
|
||||
_Base::__throw_if_empty();
|
||||
while (_Base::depth() < stepChain_.size() // Backtracking loop: attempt to establish all conditions
|
||||
and _Base::checkPoint()) // possibly trying further combinations until success:
|
||||
{
|
||||
_Base::expandChildren(); // create copy of current filter embedded into child level
|
||||
stepChain_[_Base::depth()] (_Base::accessCurrentChildIter()); // invoke step functor to reconfigure this filter...
|
||||
_Base::dropExhaustedChildren(); // which thereby might become empty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** configure additional chained search condition.
|
||||
* @param a functor `Filter const& -> filter`, which takes a current filter configuration,
|
||||
* returning a copy from this configuration, possibly configured differently.
|
||||
* @param a manipulation functor `void(Filter&)`, which works on the current filter
|
||||
* 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 const&` is assumed
|
||||
* the argument type `Filter&` is assumed
|
||||
* @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
|
||||
|
|
@ -185,7 +187,7 @@ namespace iter {
|
|||
Step nextStep{forward<FUN> (configureSearchStep)};
|
||||
|
||||
if (_Base::isDisabled())
|
||||
this-> filter() = move (nextStep (*this)); // apply first step immediately
|
||||
nextStep (*this); // apply first step immediately
|
||||
else
|
||||
{
|
||||
stepChain_.emplace_back (move (nextStep)); // append all further steps into the chain...
|
||||
|
|
@ -207,10 +209,9 @@ namespace iter {
|
|||
search (FUN&& filterPredicate)
|
||||
{
|
||||
addStep ([predicate{forward<FUN> (filterPredicate)}]
|
||||
(Filter filter) // note: filter taken by value
|
||||
{
|
||||
(Filter& filter)
|
||||
{ // manipulte current filter configuration
|
||||
filter.setNewFilter (predicate);
|
||||
return filter; // return copy of the original state with changed filter
|
||||
});
|
||||
return move(*this);
|
||||
}
|
||||
|
|
@ -239,17 +240,6 @@ namespace iter {
|
|||
_Base::disableFilter();
|
||||
return move(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
Filter
|
||||
configureFilterChain (Filter const& currentFilterState)
|
||||
{
|
||||
uint depth = this->depth();
|
||||
if (depth < stepChain_.size())
|
||||
return stepChain_[depth](currentFilterState); // augmented copy
|
||||
else
|
||||
return Filter{}; // empty filter indicates recursion end
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -722,12 +722,6 @@ namespace lib {
|
|||
or *expansions_;
|
||||
}
|
||||
|
||||
bool
|
||||
hasChildren() const
|
||||
{
|
||||
return 0 < depth();
|
||||
}
|
||||
|
||||
void
|
||||
incrementCurrent()
|
||||
{
|
||||
|
|
@ -737,12 +731,28 @@ namespace lib {
|
|||
++(*this);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
/** @internal accessor for downstream layers to allow close collaboration */
|
||||
ResIter&
|
||||
accessCurrentChildIter()
|
||||
{
|
||||
REQUIRE (hasChildren());
|
||||
return *expansions_;
|
||||
}
|
||||
|
||||
void
|
||||
dropExhaustedChildren()
|
||||
{
|
||||
while (not invariant())
|
||||
++expansions_;
|
||||
}
|
||||
|
||||
bool
|
||||
hasChildren() const
|
||||
{
|
||||
return 0 < depth();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,6 @@ namespace test{
|
|||
string currVal = *filter;
|
||||
filter.setNewFilter ([=](string const& val){
|
||||
return val != currVal; });
|
||||
return filter;
|
||||
});
|
||||
///////////////////////////////////////////////////TODO WIP
|
||||
cout << materialise (search) <<endl;
|
||||
|
|
|
|||
|
|
@ -31841,7 +31841,66 @@
|
|||
<icon BUILTIN="info"/>
|
||||
<node CREATED="1536857289235" ID="ID_1909570040" MODIFIED="1536857313452" TEXT="in configureFilterChain"/>
|
||||
<node CREATED="1536857326814" ID="ID_149310473" MODIFIED="1536857328426" TEXT="this->depth()"/>
|
||||
<node CREATED="1536857336620" ID="ID_1691329557" MODIFIED="1536857340040" TEXT="this->stepChain_"/>
|
||||
<node CREATED="1536857336620" ID="ID_1691329557" MODIFIED="1536857806626" TEXT="this->stepChain_">
|
||||
<linktarget COLOR="#d22d65" DESTINATION="ID_1691329557" ENDARROW="Default" ENDINCLINATION="147;-23;" ID="Arrow_ID_828211693" SOURCE="ID_396903078" STARTARROW="None" STARTINCLINATION="1;-33;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536857741221" ID="ID_1903828771" MODIFIED="1536958903408" TEXT="bricht das ganze Konzept hier zusammen??">
|
||||
<icon BUILTIN="forward"/>
|
||||
<node COLOR="#435e98" CREATED="1536880103264" HGAP="27" ID="ID_1970250282" MODIFIED="1536958907215" TEXT="so schaugts aus...." VSHIFT="-10">
|
||||
<icon BUILTIN="smily_bad"/>
|
||||
</node>
|
||||
<node CREATED="1536857782632" ID="ID_396903078" MODIFIED="1536857806626" TEXT="stepChain_ ist das Problem">
|
||||
<arrowlink COLOR="#d22d65" DESTINATION="ID_1691329557" ENDARROW="Default" ENDINCLINATION="147;-23;" ID="Arrow_ID_828211693" STARTARROW="None" STARTINCLINATION="1;-33;"/>
|
||||
</node>
|
||||
<node CREATED="1536880086127" ID="ID_1559582643" MODIFIED="1536882591818" TEXT="depth wohl noch ein Größeres">
|
||||
<node CREATED="1536882781354" ID="ID_1998865921" MODIFIED="1536882788421" TEXT="weil daran die Abbruchbedingung hängt"/>
|
||||
<node CREATED="1536882789057" ID="ID_170176785" MODIFIED="1536882873905" TEXT="und depth sich aber automatisch ergibt -> Backtracking"/>
|
||||
</node>
|
||||
<node CREATED="1536882920310" ID="ID_1625878480" MODIFIED="1536882937553" TEXT="eigentlich müßte...">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...nach der »reinen Lehre«
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<node CREATED="1536882938955" ID="ID_475907399" MODIFIED="1536882951373" TEXT="der Baum entweder sich selber entfalten können">
|
||||
<node CREATED="1536883521914" ID="ID_857765324" MODIFIED="1536883531340" TEXT="und dazu den Chain in sich tragen"/>
|
||||
<node CREATED="1536883532120" ID="ID_153639655" MODIFIED="1536883537739" TEXT="und auch seinen eigenen Level kennen"/>
|
||||
</node>
|
||||
<node CREATED="1536882952177" ID="ID_563964508" MODIFIED="1536882961539" TEXT="oder von außerhalb entfaltet werden">
|
||||
<node CREATED="1536883544846" ID="ID_224633668" MODIFIED="1536883607066" TEXT="dann aber auch die Entfaltung von außen bekommen"/>
|
||||
<node CREATED="1536883607966" ID="ID_1870181162" MODIFIED="1536883618816" TEXT="also klassich-imperativ"/>
|
||||
<node CREATED="1536883623932" ID="ID_567889201" MODIFIED="1536883629455" TEXT="nix Monade!"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1536958857294" ID="ID_1302534828" MODIFIED="1536958940320" TEXT="Fazit: nur der zweite Ansatz ist durchführbar">
|
||||
<arrowlink COLOR="#81445a" DESTINATION="ID_1848331022" ENDARROW="Default" ENDINCLINATION="72;-412;" ID="Arrow_ID_1608996048" STARTARROW="None" STARTINCLINATION="672;68;"/>
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1536969411521" HGAP="36" ID="ID_1686255544" MODIFIED="1536969491348" TEXT="und selbst der braucht eine Art Hintertür" VSHIFT="8">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...insofern wir nämlich zwingend auf den jeweilign Kind-<i>Iterator</i> zugreifen müssen.
|
||||
</p>
|
||||
<p>
|
||||
Nicht nur auf den aktuellen Wert (=dereferenzierter Iterator, d.h. die Funktion yield())
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="smily_bad"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -32262,6 +32321,105 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536958590136" ID="ID_1848331022" MODIFIED="1536958932753" TEXT="Refactoring Pipeline-Struktur">
|
||||
<linktarget COLOR="#81445a" DESTINATION="ID_1848331022" ENDARROW="Default" ENDINCLINATION="72;-412;" ID="Arrow_ID_1608996048" SOURCE="ID_1302534828" STARTARROW="None" STARTINCLINATION="672;68;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1536958603857" ID="ID_610488133" MODIFIED="1536958622137" TEXT="das ursprüngliche Implementierungs-Konzept scheitert">
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1536958626605" ID="ID_1277097786" MODIFIED="1536958638227" TEXT="aber ich will an dem Pipeline-Konzept festhalten">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1536958647378" ID="ID_270574796" MODIFIED="1536958666418" TEXT="Konsequenz: Schluß mit der Magie">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536958668327" ID="ID_392267635" MODIFIED="1536958691189" TEXT="keine geheimnisvollen Lambda-Closures mehr">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536958680150" ID="ID_1572201908" MODIFIED="1536958697495" TEXT="zwei Layer verwenden">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536958702987" ID="ID_1313964418" MODIFIED="1536958768827" TEXT="Basis == trivialer Explorer">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536958719665" ID="ID_806425486" MODIFIED="1536958767715" TEXT="macht lediglich eine Kopie des Filters">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node CREATED="1536958727368" ID="ID_1206856632" MODIFIED="1536958732419" TEXT="nichts mehr automatisch"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536958733950" ID="ID_374374485" MODIFIED="1536958769371" TEXT="Dekorator == Steuer-Logik">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536958744325" ID="ID_151094681" MODIFIED="1536958763476" TEXT="hält den stepChain_">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536958772594" ID="ID_365283571" MODIFIED="1536958788657" TEXT="klinkt sich in iterNext ein">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#435e98" CREATED="1536958792215" ID="ID_519507996" MODIFIED="1536973216960" TEXT="TreeExplorer: custom-Layer">
|
||||
<arrowlink COLOR="#61afb3" DESTINATION="ID_686602920" ENDARROW="Default" ENDINCLINATION="-230;613;" ID="Arrow_ID_1710661685" STARTARROW="None" STARTINCLINATION="1241;-1305;"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1536973229630" ID="ID_1567071992" MODIFIED="1536973238830" TEXT="brauche doch keinen Custom-Layer">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1536973239964" ID="ID_1858748865" MODIFIED="1536973282118" TEXT="ist besser ohne....">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
weil ich dann auf dem IterChainSearch unmittelbar die builder-Funktionen definieren kann
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536958815668" ID="ID_327461551" MODIFIED="1536958840692">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
konfiguriert <i>danach direkt</i> den Filter
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536969507092" ID="ID_1318810550" MODIFIED="1536969521859" TEXT="braucht dazu eine Hintertür im Explorer-Layer">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node CREATED="1536969537728" ID="ID_1955805225" MODIFIED="1536969691123" TEXT="kann/darf diese nicht für Basis-Iterator nutzen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...weil der Basis-Iterator (also der Template-Parameter SRC)
|
||||
</p>
|
||||
<p>
|
||||
explizit und absichtlich einen anderen Typ haben <i>könnte,</i> als der expandierte Kind-Iterator.
|
||||
</p>
|
||||
<p>
|
||||
Das ist ein wesentliches Feature dieses Expander-Designs, würde aber leider eine
|
||||
</p>
|
||||
<p>
|
||||
komplett generische Accessor-Funktion unmöglich machen (das Template würde in
|
||||
</p>
|
||||
<p>
|
||||
einem Solchen Fall insgesamt vom Compiler zurückgewiesen)
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1536018420398" ID="ID_41628967" MODIFIED="1536326452308" TEXT="Adaptieren der Quell-Datensequenz">
|
||||
<icon BUILTIN="pencil"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue