From 81c6136509b9e30fc3c146198e91c440a0012c4e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 4 Dec 2017 03:34:27 +0100 Subject: [PATCH] TreeExplorer: define interaction between expand and transform-operation good news: it (almost) works out-of-the-box as expected. There is only one problem: expandChildren() alters the content of the data source, yet downstream decorators aren't aware of that fact and continue to present cached evaluations, until the next iterate() call is issued. Yet unfortunately this iterate already consumes the first of the expanded children, which thus gets shadowed by the cached outcome of parent node already consumed and expanded at that point See the first example: "10-8-expand-8-4-2-6-4-2" should be 6 ^^^ --- tests/library/iter-tree-explorer-test.cpp | 57 ++++++++- wiki/thinkPad.ichthyo.mm | 137 ++++++++++++++++++++-- 2 files changed, 178 insertions(+), 16 deletions(-) diff --git a/tests/library/iter-tree-explorer-test.cpp b/tests/library/iter-tree-explorer-test.cpp index 2dd2d34e4..555a3437e 100644 --- a/tests/library/iter-tree-explorer-test.cpp +++ b/tests/library/iter-tree-explorer-test.cpp @@ -324,7 +324,7 @@ namespace test{ * will try to instantiate the passed lambda by using the concrete source iterator type as argument. * * @note expansion functor may use side-effects and indeed return something entirely different - * than the original sequence, as long as it is iterable and yields compatible values. + * than the original sequence, as long as it is iterable and yields compatible values. */ void verify_expandOperation() @@ -339,7 +339,7 @@ namespace test{ verify_treeExpandingIterator( treeExplore(CountDown{5}) .expand([](uint j){ return NumberSequence{j-1}; }) // expand-functor: Val > Iter - ); + ); // NOTE: different Iterator type than the source! // lambda with side-effect and return type different from source iter vector> childBuffer; @@ -456,7 +456,7 @@ namespace test{ auto multiply = [](int v){ return 2*v; }; // functional map: value -> value _Fmt embrace{"≺%s≻"}; - auto formatify = [&](auto it){ return string{embrace % *it}; }; // generic lambda: assumed to take an Iterator& + auto formatify = [&](auto it){ return string{embrace % *it}; }; // generic lambda: assumed to take an Iterator& auto ii = treeExplore (CountDown{7,4}) @@ -559,12 +559,59 @@ namespace test{ } - /** @test combie the recursion into children with a tail mapping operation + /** @test combine the recursion into children with a tail mapping operation. + * Wile basically this is just the layering structure of TreeExplorer put into action, + * you should note one specific twist: the iter_explorer::Expander::expandChildren() call + * is meant to be issued from ``downstream'', from the consumer side. Yet the consumer at + * that point might well see the items as processed by a transforming step layered on top. + * So what the consumer sees and thinks will be expanded need not actually be what will + * be processed by the _expand functor_. This may look like a theoretical or cosmetic + * issue -- yet in fact it is this tiny detail which is crucial to make abstraction of + * the underlying data source actually work in conjunction with elaborate searching and + * matching algorithms. Even more so, when other operations like filtering are intermingled; + * in that case it might even happen that the downstream consumer does not even see the + * items resulting from child expansion, because they are evaluated and then filtered + * away by a transformer and filter placed in between. + * @note a corollary of the aforementioned is the observation that having several expand + * layers is rather not useful in practice. Due to the nested inheritance structure + * of the assembled TreeExplorer, it is the expansion layer most on top in the chain + * who will receive the `expandChildren()` call. */ void verify_combinedExpandTransform() { - UNIMPLEMENTED("combine child expansion and result mapping"); + auto ii = treeExplore(CountDown{5}) + .expand([](uint j){ return CountDown{j-1}; }) + .transform([](int v){ return 2*v; }) + ; + + CHECK ("int" == meta::typeStr(*ii)); // result type is what the last transformer yields + CHECK (10 == *ii); + ++ii; + CHECK (8 == *ii); + ii.expandChildren(); + cout << materialise(ii) < - + @@ -5327,7 +5327,7 @@ - + @@ -5394,8 +5394,7 @@ TreeExplorer per slicing move entfernen

- - +
@@ -5469,7 +5468,7 @@ - + @@ -5673,6 +5672,7 @@ + @@ -5691,7 +5691,6 @@ -
@@ -5706,12 +5705,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ ...daß man ein Ding komplett in einen Iterator packt, +

+

+ und dieser es dann auch managed +

+ + +
+ + + + + + +

+ ist das #190 +

+ + +
+ + + + + + + + + + +
+ + + + + + +

+ ...ist partiell diese Idee. +

+

+ Nur auch das auf einem etwas anderem Level, +

+

+ und immer mit einem Heap-allozierten vector +

+ + +
+
+
+ + + @@ -5789,7 +5885,7 @@ - + @@ -5869,7 +5965,7 @@ - + @@ -5882,17 +5978,36 @@ ...weil man den konkreten Typ der Core kennen muß

- - +
- + + + + + + + + + + + + + + + + + + + + +