Scheduler-test: extract a generic grouping iterator
...so IterExplorer got yet another processing layer, which uses the grouping mechanics developed yesterday, but is freely configurable through λ-Functions. At actual usage sit in TestChainLoad, now only the actual aggregation computation must be supplied, and follow-up computations can now be chained up easily as further transformation layers.
This commit is contained in:
parent
fec117039e
commit
409a60238a
5 changed files with 266 additions and 77 deletions
|
|
@ -117,6 +117,7 @@
|
|||
#include "lib/util.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
|
||||
|
|
@ -420,6 +421,7 @@ namespace lib {
|
|||
using Sig = typename FunDetector<FUN>::Sig;
|
||||
using Arg = typename _Fun<Sig>::Args::List::Head; // assuming function with a single argument
|
||||
using Res = typename _Fun<Sig>::Ret;
|
||||
static_assert (meta::is_UnaryFun<Sig>());
|
||||
|
||||
|
||||
|
||||
|
|
@ -971,6 +973,107 @@ namespace lib {
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* @internal Decorator for IterExplorer to group consecutive elements controlled by some
|
||||
* grouping value \a GRP and compute an aggregate value \a AGG for each such group as
|
||||
* iterator yield. The first group is consumed eagerly, each further group on iteration;
|
||||
* thus when the aggregate for the last group appears as result, the source iterator has
|
||||
* already been exhausted. The aggregate is default-initialised at start of each group
|
||||
* and then the computation functor \a FAGG is invoked for each consecutive element marked
|
||||
* with the same _grouping value_ — and this grouping value itself is obtained by invoking
|
||||
* the functor \a FGRP on each source value. No capturing or even reordering of the source
|
||||
* elements takes place, rather groups are formed based on the changes of the grouping value
|
||||
* over the source iterator's result sequence.
|
||||
* @tparam AGG data type to collect the aggregate; must be default constructible and assignable
|
||||
* @tparam GRP value type to indicate a group
|
||||
* @note while `groupFun` is adapted, the `aggFun` is _not adapted_ to the source iterator,
|
||||
* but expected always to take the _value type_ of the preceding iterator, i.e. `*srcIter`.
|
||||
* This limitation was deemed acceptable (adapting a function with several arguments would
|
||||
* require quite some nasty technicalities). The first argument of this `aggFun` refers
|
||||
* to the accumulator by value, and thereby also implititly defines the aggregate result type.
|
||||
*/
|
||||
template<class SRC, typename AGG, class GRP>
|
||||
class GroupAggregator
|
||||
: public SRC
|
||||
{
|
||||
static_assert(can_IterForEach<SRC>::value, "Lumiera Iterator required as source");
|
||||
|
||||
protected:
|
||||
using SrcValue = typename meta::ValueTypeBinding<SRC>::value_type;
|
||||
using Grouping = function<GRP(SRC&)>;
|
||||
using Aggregator = function<void(AGG&, SrcValue&)>;
|
||||
|
||||
std::optional<AGG> agg_{};
|
||||
|
||||
Grouping grouping_;
|
||||
Aggregator aggregate_;
|
||||
|
||||
public:
|
||||
using value_type = typename meta::RefTraits<AGG>::Value;
|
||||
using reference = typename meta::RefTraits<AGG>::Reference;
|
||||
using pointer = typename meta::RefTraits<AGG>::Pointer;
|
||||
|
||||
GroupAggregator() =default;
|
||||
// inherited default copy operations
|
||||
|
||||
template<class FGRP, class FAGG>
|
||||
GroupAggregator (SRC&& dataSrc, FGRP&& groupFun, FAGG&& aggFun)
|
||||
: SRC{move (dataSrc)}
|
||||
, grouping_{_FunTraits<FGRP,SRC>::adaptFunctor (forward<FGRP> (groupFun))}
|
||||
, aggregate_{forward<FAGG> (aggFun)}
|
||||
{
|
||||
pullGroup(); // initially pull to establish the invariant
|
||||
}
|
||||
|
||||
|
||||
public: /* === Iteration control API for IterableDecorator === */
|
||||
bool
|
||||
checkPoint() const
|
||||
{
|
||||
return bool(agg_);
|
||||
}
|
||||
|
||||
reference
|
||||
yield() const
|
||||
{
|
||||
return *unConst(this)->agg_;
|
||||
}
|
||||
|
||||
void
|
||||
iterNext()
|
||||
{
|
||||
if (srcIter())
|
||||
pullGroup();
|
||||
else
|
||||
agg_ = std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
SRC&
|
||||
srcIter() const
|
||||
{
|
||||
return unConst(*this);
|
||||
}
|
||||
|
||||
/** @note establishes the invariant:
|
||||
* source has been consumed up to the beginning of next group */
|
||||
void
|
||||
pullGroup()
|
||||
{
|
||||
GRP group = grouping_(srcIter());
|
||||
agg_ = AGG{};
|
||||
do{
|
||||
aggregate_(*agg_, *srcIter());
|
||||
++ srcIter();
|
||||
}
|
||||
while (srcIter() and group == grouping_(srcIter()));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @internal Decorator for IterExplorer to filter elements based on a predicate.
|
||||
* Similar to the Transformer, the given functor is adapted as appropriate. However,
|
||||
|
|
@ -1581,7 +1684,7 @@ namespace lib {
|
|||
}
|
||||
|
||||
|
||||
/** adapt this IterExplorer group result elements into fixed size chunks, packaged as std::array.
|
||||
/** adapt this IterExplorer to group result elements into fixed size chunks, packaged as std::array.
|
||||
* The first group of elements is pulled eagerly at construction, while further groups are formed
|
||||
* on consecutive iteration. Iteration ends when no further full group can be formed; this may
|
||||
* leave out some leftover elements, which can then be retrieved by iteration through the
|
||||
|
|
@ -1601,6 +1704,47 @@ namespace lib {
|
|||
}
|
||||
|
||||
|
||||
/** adapt this IterExplorer to group elements by a custom criterium and aggregate the group members.
|
||||
* The first group of elements is pulled eagerly at construction, further groups are formed on each
|
||||
* iteration. Aggregation is done by a custom functor, which takes an _aggregator value_ as first
|
||||
* argument and the current element (or iterator) as second argument. Downstream, the aggregator
|
||||
* value computed for each group is yielded on iteration.
|
||||
* @param groupFun a functor to derive a grouping criterium from the source sequence; consecutive elements
|
||||
* yielding the same grouping value will be combined / aggregated
|
||||
* @param aggFun a functor to compute contribution to the aggregate value. Signature `void(AGG&, Val&)`,
|
||||
* where the type AGG implicitly also defines the _value_ to use for accumulation and the result value,
|
||||
* while Val must be assignable from the _source value_ provided by the preceding iterator in the pipeline.
|
||||
*/
|
||||
template<class FGRP, class FAGG>
|
||||
auto
|
||||
groupedBy (FGRP&& groupFun, FAGG&& aggFun)
|
||||
{
|
||||
using GroupVal = typename iter_explorer::_FunTraits<FGRP,SRC>::Res;
|
||||
|
||||
static_assert (meta::is_BinaryFun<FAGG>());
|
||||
using ArgType1 = typename _Fun<FAGG>::Args::List::Head;
|
||||
using Aggregate = typename meta::RefTraits<ArgType1>::Value;
|
||||
|
||||
using ResCore = iter_explorer::GroupAggregator<SRC, Aggregate, GroupVal>;
|
||||
using ResIter = typename _DecoratorTraits<ResCore>::SrcIter;
|
||||
|
||||
return IterExplorer<ResIter> (ResCore {move(*this)
|
||||
,forward<FGRP> (groupFun)
|
||||
,forward<FAGG> (aggFun)});
|
||||
}
|
||||
|
||||
/** simplified grouping to sum / combine all values in a group */
|
||||
template<class FGRP>
|
||||
auto
|
||||
groupedBy (FGRP&& groupFun)
|
||||
{
|
||||
using Value = typename meta::ValueTypeBinding<SRC>::value_type;
|
||||
return groupedBy (forward<FGRP> (groupFun)
|
||||
,[](Value& agg, Value const& val){ agg += val; }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/** adapt this IterExplorer to iterate only as long as a condition holds true.
|
||||
* @return processing pipeline with attached [stop condition](\ref iter_explorer::StopTrigger)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
#include <limits>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
|
@ -279,6 +280,7 @@ namespace test{
|
|||
verify_expand_rootCurrent();
|
||||
verify_transformOperation();
|
||||
verify_elementGroupingOperation();
|
||||
verify_aggregatingGroupItration();
|
||||
verify_combinedExpandTransform();
|
||||
verify_customProcessingLayer();
|
||||
verify_scheduledExpansion();
|
||||
|
|
@ -668,6 +670,7 @@ namespace test{
|
|||
}
|
||||
|
||||
|
||||
|
||||
/** @test package elements from the source pipeline into fixed-sized groups.
|
||||
* These groups are implemented as std::array and initialised with the values
|
||||
* yielded consecutively from the underlying source pipeline. The main iterator
|
||||
|
|
@ -729,6 +732,47 @@ namespace test{
|
|||
}
|
||||
|
||||
|
||||
/** @test another form of grouping, where groups are formed by a derived property
|
||||
* thereby passing each element in the group to an aggregator function, working on
|
||||
* an accumulator per group. Downstream, the resulting, accumulated value is exposed
|
||||
* for each group, while consuming all source values belonging to this group.
|
||||
* - in the simple form, all members of a group are "added" together
|
||||
* - the elaborate form allows to provide a custom aggregation function, which takes
|
||||
* the »accumulator« as first argument by reference; the type of this argument
|
||||
* implicitly defines what is instantiated for each group and yielded as result.
|
||||
*/
|
||||
void
|
||||
verify_aggregatingGroupItration()
|
||||
{
|
||||
CHECK (materialise (
|
||||
explore(CountDown{10})
|
||||
.groupedBy(std::ilogbf)
|
||||
)
|
||||
== "27-22-5-1"_expect); // 10+9+8|7+6+5+4|3+2|1
|
||||
|
||||
CHECK (materialise (
|
||||
explore(CountDown{10})
|
||||
.transform(util::toString<uint>)
|
||||
.groupedBy([](auto& it) { return std::ilogbf (it.p); }) // note trickery: takes not the value, rather the iterator and
|
||||
) // accesses internals of CountDown, bypassing the transform layer above
|
||||
== "1098-7654-32-1"_expect); // `+` does string concatenation
|
||||
|
||||
|
||||
auto showGroup = [](auto it){ return "["+util::join(*it)+"]"; };
|
||||
// elaborate form with custom aggregation...
|
||||
CHECK (materialise (
|
||||
explore(CountDown{10})
|
||||
.groupedBy(std::ilogbf
|
||||
,[](vector<uint>& accum, uint val)
|
||||
{
|
||||
accum.push_back (val);
|
||||
})
|
||||
.transform(showGroup)
|
||||
)
|
||||
== "[10, 9, 8]-[7, 6, 5, 4]-[3, 2]-[1]"_expect);
|
||||
}
|
||||
|
||||
|
||||
/** @test combine the recursion into children with a tail mapping operation.
|
||||
* Wile basically this is just the layering structure of IterExplorer put into action,
|
||||
* you should note one specific twist: the iter_explorer::Expander::expandChildren() call
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ SHOW_EXPR(micros);
|
|||
CHECK (micros < 550);
|
||||
CHECK (micros > 450);
|
||||
|
||||
std::vector<TestChainLoad<>::Agg> levelWeights = testLoad.allNodes().processingLayer<TestChainLoad<>::WeightAggregator>().effuse();
|
||||
std::vector<LevelWeight> levelWeights = testLoad.allLevelWeights().effuse();
|
||||
SHOW_EXPR(levelWeights.size())
|
||||
for (auto& w : levelWeights)
|
||||
{
|
||||
|
|
@ -175,7 +175,7 @@ SHOW_EXPR(levelWeights.size())
|
|||
.effuse();
|
||||
for (auto& n : nodeData)
|
||||
cout<<"level:"<<n.first<<" w="<<n.second<<endl;
|
||||
auto getWeight = [&](uint level) { return std::get<2> (levelWeights[level]); };
|
||||
auto getWeight = [&](uint level) { return levelWeights[level].weight; };
|
||||
CHECK (nodeData[22].first == 16);
|
||||
CHECK (nodeData[23].first == 17); CHECK (nodeData[23].second == 3); CHECK (getWeight(17) == 3);
|
||||
|
||||
|
|
|
|||
|
|
@ -170,6 +170,12 @@ namespace test {
|
|||
const Duration SCHEDULE_PLAN_STEP{_uTicks(100us)}; ///< time budget to reserve for each node to be planned and scheduled
|
||||
}
|
||||
|
||||
struct LevelWeight
|
||||
{
|
||||
size_t level{0};
|
||||
size_t nodes{0};
|
||||
size_t weight{0};
|
||||
};
|
||||
|
||||
struct Statistic;
|
||||
|
||||
|
|
@ -748,74 +754,19 @@ namespace test {
|
|||
}
|
||||
|
||||
|
||||
using Agg = std::tuple<size_t,size_t,size_t>;
|
||||
|
||||
template<class IT>
|
||||
class WeightAggregator
|
||||
: public IT
|
||||
{
|
||||
std::optional<Agg> agg_{};
|
||||
|
||||
IT&
|
||||
srcIter() const
|
||||
{
|
||||
return unConst(*this);
|
||||
}
|
||||
|
||||
static void
|
||||
account (Agg& agg, IT const& item)
|
||||
{
|
||||
auto& [level,nodes,weight] = agg;
|
||||
auto& n = *item;
|
||||
level = n.level;
|
||||
weight += n.weight;
|
||||
++nodes;
|
||||
}
|
||||
void
|
||||
pullGroup()
|
||||
{
|
||||
size_t group = srcIter()->level;
|
||||
agg_ = Agg{};
|
||||
do{
|
||||
account (*agg_, srcIter());
|
||||
++ srcIter();
|
||||
}
|
||||
while (srcIter() and group == srcIter()->level);
|
||||
}
|
||||
public:
|
||||
WeightAggregator() =default;
|
||||
WeightAggregator (IT&& src)
|
||||
: IT{move (src)}
|
||||
{
|
||||
if (srcIter())
|
||||
pullGroup();
|
||||
}
|
||||
bool
|
||||
checkPoint() const
|
||||
{
|
||||
return bool(agg_);
|
||||
}
|
||||
|
||||
Agg&
|
||||
yield() const
|
||||
{
|
||||
return *unConst(this)->agg_;
|
||||
}
|
||||
|
||||
void
|
||||
iterNext()
|
||||
{
|
||||
if (srcIter())
|
||||
pullGroup();
|
||||
else
|
||||
agg_ = std::nullopt;
|
||||
}
|
||||
};
|
||||
/** calculate node weights aggregated per level */
|
||||
auto
|
||||
allLevelWeights()
|
||||
{
|
||||
// return allNodes().processingLayer<WeightAggregator>();
|
||||
return allNodes()
|
||||
.groupedBy([](Node& n){ return n.level; }
|
||||
,[](LevelWeight& lw, Node const& n)
|
||||
{
|
||||
lw.level = n.level;
|
||||
lw.weight += n.weight;
|
||||
++lw.nodes;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -107419,7 +107419,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node CREATED="1703797597576" ID="ID_496053162" MODIFIED="1703797638501" TEXT="liefert für jeden Level: (levelNr, Σw, Σn)"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1703801391911" ID="ID_1010028454" MODIFIED="1703807940482" TEXT="Implementierung">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#5b280f" CREATED="1703801398285" ID="ID_1449254847" MODIFIED="1703804125235" TEXT="Iter-Explorer mit gruppierendem Processing-Layer">
|
||||
<node COLOR="#5b280f" CREATED="1703801398285" FOLDED="true" ID="ID_1449254847" MODIFIED="1703979248329" TEXT="Iter-Explorer mit gruppierendem Processing-Layer">
|
||||
<arrowlink COLOR="#5874c0" DESTINATION="ID_1740211471" ENDARROW="Default" ENDINCLINATION="-136;-9;" ID="Arrow_ID_443078033" STARTARROW="None" STARTINCLINATION="53;91;"/>
|
||||
<linktarget COLOR="#f53565" DESTINATION="ID_1449254847" ENDARROW="Default" ENDINCLINATION="-19;118;" ID="Arrow_ID_669204155" SOURCE="ID_1512834256" STARTARROW="None" STARTINCLINATION="-120;-17;"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1703801415915" ID="ID_1892218840" MODIFIED="1703801420014" TEXT="Invariante">
|
||||
|
|
@ -107489,7 +107490,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1703882997602" ID="ID_1587426873" MODIFIED="1703883014897" TEXT="nachfolgend wäre aber eine Pipeline doch nicht schlecht">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1703883016888" ID="ID_1740211471" MODIFIED="1703895721626" TEXT="also einen zweiten Anlauf versuchen">
|
||||
<node COLOR="#435e98" CREATED="1703883016888" ID="ID_1740211471" MODIFIED="1703979248329" TEXT="also einen zweiten Anlauf versuchen">
|
||||
<linktarget COLOR="#5874c0" DESTINATION="ID_1740211471" ENDARROW="Default" ENDINCLINATION="-136;-9;" ID="Arrow_ID_443078033" SOURCE="ID_1449254847" STARTARROW="None" STARTINCLINATION="53;91;"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1703895722656" ID="ID_527352372" MODIFIED="1703895731221" TEXT="sschwere Geburt">
|
||||
<icon BUILTIN="smiley-angry"/>
|
||||
|
|
@ -107518,16 +107520,64 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1703895976686" ID="ID_233747473" MODIFIED="1703895999350" TEXT="dann diese Lösung extrahieren und verallgemeinern">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1703896008082" ID="ID_77845528" MODIFIED="1703896027228" TEXT="erst mal in TestChainLoad mit Lambdas konfigurieren">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1703895976686" ID="ID_233747473" MODIFIED="1703979202514" TEXT="dann diese Lösung extrahieren und verallgemeinern">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#5b280f" CREATED="1703896008082" ID="ID_77845528" MODIFIED="1703974649167" TEXT="erst mal in TestChainLoad mit Lambdas konfigurieren">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1703974651311" ID="ID_1612095932" MODIFIED="1703974659788" TEXT="geht so nicht...."/>
|
||||
<node CREATED="1703974660365" ID="ID_515087640" MODIFIED="1703974706210" TEXT="das Interface für IterExplorer::processingLayer() ist festgelegt auf ein Unäres Template"/>
|
||||
<node CREATED="1703974710699" ID="ID_1069454746" MODIFIED="1703974722984" TEXT="daher sofort den Sprung auf einen generischen Aggregator machen"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1703896028383" ID="ID_373477842" MODIFIED="1703896035228" TEXT="dann in den IterExplorer übernehmen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1703896028383" ID="ID_373477842" MODIFIED="1703979137992" TEXT="dann für den IterExplorer verallgemeinern">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1703974886502" ID="ID_1266426996" MODIFIED="1703974905655" TEXT="Adaptierbarkeit für Src-Iterator-Argument"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1703974906379" FOLDED="true" ID="ID_1063698965" MODIFIED="1703979193326" TEXT="Problem: das Framework stellt auf Unäre Funktionen ab">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
<node CREATED="1703974924785" ID="ID_694048108" MODIFIED="1703974936830" TEXT="war bisher immer der Fall (Transformer, Filter,...)"/>
|
||||
<node CREATED="1703974943335" ID="ID_1332119101" MODIFIED="1703974954521" TEXT="nun müßte aber das 2.Argument einer binären Funktion adaptiert werden"/>
|
||||
<node CREATED="1703974985226" ID="ID_1314783596" MODIFIED="1703974993803" TEXT="Curry bzw. partial application...">
|
||||
<node CREATED="1703975001782" ID="ID_669651191" MODIFIED="1703975022711" TEXT="als Vorbereitung an den Akkumulator binden"/>
|
||||
<node COLOR="#5b280f" CREATED="1703975058314" ID="ID_1623063408" MODIFIED="1703975149057" TEXT="Binden per Referenz widerspricht IterExplorer Nutzungs-Muster">
|
||||
<icon BUILTIN="closed"/>
|
||||
<node CREATED="1703975150947" ID="ID_1414201479" MODIFIED="1703975162108" TEXT="IterExploer wird als Builder aufgebaut"/>
|
||||
<node CREATED="1703975162649" ID="ID_1096493459" MODIFIED="1703975169196" TEXT="und jeweils in das Ziel geschoben"/>
|
||||
<node CREATED="1703975170072" ID="ID_981130825" MODIFIED="1703975193265" TEXT="⟹ this ändert sich unterwegs"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1703896035966" ID="ID_707528864" MODIFIED="1703896039984" TEXT="Test dafür schreiben">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node COLOR="#5b280f" CREATED="1703975817543" ID="ID_323717828" MODIFIED="1703976031049" TEXT="andere Auswege?">
|
||||
<icon BUILTIN="help"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1703975869658" ID="ID_990729285" MODIFIED="1703975904370" TEXT="die _FunTraits::adaptFunctor könnte man verallgemeinern...">
|
||||
<node CREATED="1703975905557" ID="ID_93452190" MODIFIED="1703975907289" TEXT="zOMG"/>
|
||||
<node CREATED="1703975907709" ID="ID_399985710" MODIFIED="1703975918872" TEXT="wer will sich das antun..!?"/>
|
||||
</node>
|
||||
<node CREATED="1703976194479" ID="ID_435449867" MODIFIED="1703976231726" TEXT="man könnte die Funktor-Tools ausbauen, und compose auf beliebige Argumente ermöglichen">
|
||||
<node CREATED="1703976233813" ID="ID_783321602" MODIFIED="1703976246676" TEXT="hiermit gäbe es also den ersten Anwendungsfall"/>
|
||||
<node CREATED="1703976247383" ID="ID_1631613099" MODIFIED="1703976255602" TEXT="...den im Moment noch niemand braucht"/>
|
||||
<node CREATED="1703976256262" ID="ID_1431849596" MODIFIED="1703976263095" TEXT="gut daß wir darüber geredet haben">
|
||||
<icon BUILTIN="ksmiletris"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1703975970540" ID="ID_768475065" MODIFIED="1703975994429" TEXT="man könnte die Anpassung hier „zu fuß“ nochmal programmieren"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1703976043723" ID="ID_1128153890" MODIFIED="1703976061431" TEXT="also erst mal nicht anpassbar machen">
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1703976063609" ID="ID_1594454385" MODIFIED="1703976067252" TEXT="YAGNI"/>
|
||||
<node CREATED="1703976067872" ID="ID_174383133" MODIFIED="1703976079554" TEXT="ist im Moment nur „nice-to-have“"/>
|
||||
<node CREATED="1703976093596" ID="ID_1279220583" MODIFIED="1703976114709" TEXT="und ich werde wieder ein Stück komplexen Code los..."/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1703979125116" ID="ID_381984711" MODIFIED="1703979137009" TEXT="im übrigen... tut wie erwartet">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1703896035966" ID="ID_707528864" MODIFIED="1703979141744" TEXT="Test dafür schreiben">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#435e98" CREATED="1703979142762" ID="ID_901668854" MODIFIED="1703979183660" TEXT="einfache Summation"/>
|
||||
<node COLOR="#435e98" CREATED="1703979146985" ID="ID_1998259803" MODIFIED="1703979183661" TEXT="Summation mit String-Konkatenation und Durchgriff auf die Iterator-Core"/>
|
||||
<node COLOR="#435e98" CREATED="1703979167940" ID="ID_117779918" MODIFIED="1703979183661" TEXT="komplexe Gruppierung mit Custom-Allokator (Beispiel: ein vector)"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue