Library: RandomDraw - must accept generic arguments

...since the Policy class now defines the function signature,
we can no longer assume that "input" is size_t. Rather, all
invocations must rely on the generic adaptaion scheme.

Getting this correct turns out rather tricky again;
best to rely on a generic function-composition.

Indeed I programmed such a helper several years ago,
with the caveat that at that time we used C++03 and
could not perfect-forward arguments. Today this problem
can be solved much more succinct using generic Lambdas.
This commit is contained in:
Fischlurch 2023-11-21 03:01:33 +01:00
parent 651e28bac9
commit 75dd4210f2
5 changed files with 221 additions and 200 deletions

View file

@ -644,123 +644,6 @@ namespace func{
namespace _composed { // repetitive impl.code for function composition
using std::bind;
using std::function;
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
using std::placeholders::_5;
using std::placeholders::_6;
using std::placeholders::_7;
using std::placeholders::_8;
using std::placeholders::_9;
template<typename RES, typename F1, typename F2, uint n>
struct Build;
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 0 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1)); }
};
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 1 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1)); }
};
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 2 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2)); }
};
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 3 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3)); }
};
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 4 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4)); }
};
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 5 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5)); }
};
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 6 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5,_6)); }
};
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 7 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5,_6,_7)); }
};
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 8 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5,_6,_7,_8)); }
};
template<typename RES, typename F1, typename F2>
struct Build<RES,F1,F2, 9 >
{
static function<RES> func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5,_6,_7,_8,_9)); }
};
} // (End) impl namespace (_composed)
/**
* Functional composition. Create a functor, which
* on invocation will execute two functions chained,
* i.e. fed the result of invoking the first function
* as argument into the second function.
*/
template<typename F1, typename RET>
class FunctionComposition
{
typedef typename _Fun<F1>::Args Args;
typedef typename _Fun<F1>::Ret Ret1;
typedef Types<Ret1> ArgsF2;
typedef typename FunctionTypedef<RET, ArgsF2>::Sig SigF2;
typedef typename FunctionTypedef<RET, Args>::Sig ChainedSig;
enum { ARG_CNT = count<typename Args::List>::value };
public:
static function<ChainedSig>
chain (F1& f1, SigF2& f2)
{
return _composed::Build<ChainedSig,F1,SigF2, ARG_CNT>::func (f1,f2);
}
static function<ChainedSig>
chain (F1& f1, function<SigF2>& f2)
{
return _composed::Build<ChainedSig,F1,function<SigF2>, ARG_CNT>::func (f1,f2);
}
};
/**
* Bind a specific argument to an arbitrary value.
* Especially, this "value" might be another binder.
@ -826,13 +709,13 @@ namespace func{
typedef FunctionClosure<Signature> Type;
};
template<typename SIG1, typename SIG2>
template<typename FUN1, typename FUN2>
struct _Chain
{
typedef typename _Fun<SIG1>::Args Args;
typedef typename _Fun<SIG2>::Ret Ret;
typedef typename FunctionTypedef<Ret, Args>::Sig Chained;
typedef function<Chained> Function;
typedef typename _Fun<FUN1>::Args Args;
typedef typename _Fun<FUN2>::Ret Ret;
typedef typename FunctionTypedef<Ret, Args>::Sig ChainedSig;
typedef function<ChainedSig> Functor;
};
template<typename SIG>
@ -946,14 +829,21 @@ namespace func{
}
/** build a functor chaining the given functions */
template<typename SIG1, typename SIG2>
inline
typename _Chain<SIG1,SIG2>::Function
chained (SIG1& f1, SIG2& f2)
/** build a functor chaining the given functions: feed the result of f1 into f2.
* @note the mathematical notation would be `chained f2f1`
*/
template<typename FUN1, typename FUN2>
inline auto
chained (FUN1&& f1, FUN2&& f2)
{
typedef typename _Chain<SIG1,SIG2>::Ret Ret;
return FunctionComposition<SIG1,Ret>::chain (f1, f2);
using Ret = typename _Chain<FUN1,FUN2>::Ret;
return [functor1 = std::forward<FUN1> (f1)
,functor2 = std::forward<FUN2> (f2)]
(auto&& ...args) -> Ret
{
return functor2 (functor1 (std::forward<decltype(args)> (args)...));
};
}

View file

@ -52,6 +52,7 @@
#include "lib/meta/function.hpp"
#include "lib/meta/function-closure.hpp"
#include "lib/util.hpp"
#include <functional>
@ -138,10 +139,11 @@ namespace lib {
if (probability_ == 0.0 or val == 0.0)
return Tar{0};
double q = (1.0 - probability_);
auto org = Tar::minVal();
val -= q; // [0 .. [q .. 1[
val /= probability_; // [0 .. 1[
val *= maxResult_; // [0 .. m[
val += 1; // [1 .. m]
val *= maxResult_ - org; // [0 .. m[
val += org+1; // [1 .. m]
val += CAP_EPSILON; // round down yet absorb dust
return Tar{val};
}
@ -156,6 +158,13 @@ namespace lib {
return double(hash % QUANTISER) / QUANTISER;
}
/** @internal core operation: draw and quantise into limited value */
Tar
drawLimited (size_t hash)
{
return limited (asRand (hash));
}
public:
/** Drawing is _disabled_ by default, always yielding "zero" */
@ -190,7 +199,7 @@ namespace lib {
}
RandomDraw&&
maxVal (uint m)
maxVal (Tar m)
{
maxResult_ = m;
return move (*this);
@ -228,33 +237,25 @@ namespace lib {
static_assert (lib::meta::_Fun<FUN>(), "Need something function-like.");
using Res = typename lib::meta::_Fun<FUN>::Ret;
using lib::meta::func::chained;
if constexpr (std::is_same_v<Res, Tar>)
if constexpr (std::is_same_v<Res, Tar>)// ◁──────────────────────────┨ function produces result directly
return std::forward<FUN>(fun);
else
if constexpr (std::is_same_v<Res, size_t>)
return [functor=std::forward<FUN>(fun), this]
(size_t rawHash)
{
size_t hash = functor(rawHash);
double randomNum = asRand (hash);
return limited (randomNum);
};
if constexpr (std::is_same_v<Res, size_t>)// ◁───────────────────────┨ function yields random source to draw value
return chained (std::forward<FUN>(fun)
,[this](size_t hash){ return drawLimited(hash); });
else
if constexpr (std::is_same_v<Res, double>)
return [functor=std::forward<FUN>(fun), this]
(size_t rawHash)
{
double randomNum = functor(rawHash);
return limited (randomNum);
};
if constexpr (std::is_same_v<Res, double>)// ◁───────────────────────┨ function yields random value to be quantised
return chained (std::forward<FUN>(fun)
,[this](double rand){ return limited(rand); });
else
if constexpr (std::is_same_v<Res, RandomDraw>)
if constexpr (std::is_same_v<Res, RandomDraw>)// ◁────────────────────┨ function yields parametrised RandomDraw to invoke
return [functor=std::forward<FUN>(fun), this]
(size_t rawHash)
{
RandomDraw parametricDraw = functor(rawHash);
return parametricDraw (rawHash);
(auto&& ...inArgs)
{ // invoke with copy
RandomDraw parametricDraw = functor(inArgs...);
return parametricDraw (forward<decltype(inArgs)> (inArgs)...);
};
else
static_assert (not sizeof(Res), "unable to adapt / handle result type");

View file

@ -396,18 +396,16 @@ namespace wrapper {
/**
* Extension of ItemWrapper: a function remembering
* the result of the last invocation. Initially, the "value"
* is bottom (undefined, NIL), until the function is invoked
* for the first time. After that, the result of the last
* invocation can be accessed by `operator* ()`
*
* @note non-copyable. (removing this limitation would
* require a much more expensive implementation,
* by implementing operator() ourselves)
* Extension of ItemWrapper: a function remembering the result of the
* last invocation. Initially, the "value" is bottom (undefined, NIL),
* until the function is invoked for the first time. After that, the
* result of the last invocation can be accessed by `operator* ()`
* @note deliberately non-copyable, since we capture a reference
* to `this` in order to write to the embedded ItemWrapper.
* (to alleviate, we'd have to re-link after copying/moving)
*/
template<typename SIG>
class FunctionResult
struct FunctionResult
: public function<SIG>
, util::NonCopyable
{
@ -416,43 +414,28 @@ namespace wrapper {
ResWrapper lastResult_;
Res
captureResult (Res res)
{
lastResult_ = res;
return res;
}
public:
/** default ctor yields an object
* locked to \em invalid state */
FunctionResult () { }
/** by default locked to _invalid state_ */
FunctionResult() = default;
/** Create result-remembering functor
* by binding the given function. Explanation:
* - `*this` is a _function_
* - initially it is defined as invalid
* - then we build the function composition of
* the target function, and a function storing
* the result value into the ResWrapper member
* - define ourselves by assigning the resulting
* composite function
/**
* Create result-remembering functor by outfitting a _copy_
* of the given function with an adaptor to _capture_ each
* produced result.
* @warning if function result is a value, it is copied.
*/
template<typename FUN>
FunctionResult (FUN targetFunction)
{
using std::bind;
using std::placeholders::_1;
using lib::meta::func::chained;
// note: binding "this" mandates noncopyable
function<Res(Res)> doCaptureResult = bind (&FunctionResult::captureResult, this, _1 );
function<SIG> chainedWithResCapture = chained (targetFunction, doCaptureResult);
function<SIG>::operator= (chainedWithResCapture); // define the function (baseclass)
}
FunctionResult (FUN&& targetFunction)
: function<SIG>{lib::meta::func::chained
( std::forward<FUN> (targetFunction)
, [this](Res res) -> Res
{
lastResult_ = res;
return std::forward<Res> (res);
})}
{ }
/** retrieve the last function result observed */
Res& operator*() const { return *lastResult_; }
bool isValid () const { return lastResult_.isValid(); }

View file

@ -30,6 +30,7 @@
#include "lib/test/run.hpp"
//#include "lib/test/test-helper.hpp"
#include "lib/random-draw.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/test/diagnostic-output.hpp"////////////////////TODO
//#include "lib/util.hpp"
@ -42,6 +43,8 @@ namespace test{
// using util::isSameObject;
// using std::rand;
using lib::time::FSecs;
using lib::time::TimeVar;
// namespace error = lumiera::error;
// using error::LUMIERA_ERROR_FATAL;
@ -154,8 +157,21 @@ SHOW_EXPR (int(draw(256)));
void
verify_policy()
{
// auto d1 = RandomDraw<random_draw::LimitedRandomGenerate<5>>().probability(1.0);
//SHOW_EXPR (uint(d1()))
auto d1 = RandomDraw<random_draw::LimitedRandomGenerate<5>>().probability(1.0);
uint v1 = d1();
CHECK (0 < v1 and v1 <=5);
struct SpecialPolicy
: function<Limited<char, 'Z','A'>(char,uint)>
{
static double defaultSrc (char b, uint off) { return fmod ((b-'A'+off)/double('Z'-'A'), 1.0); }
};
auto d2 = RandomDraw<SpecialPolicy>().probability(1.0);
CHECK (d2('A', 2) == 'D');
CHECK (d2('M',10) == 'X');
CHECK (d2('Y', 0) == 'Z');
CHECK (d2('Y',15) == 'P');
}

View file

@ -96642,10 +96642,82 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1700515519514" ID="ID_1957414628" MODIFIED="1700515559845" TEXT="&#x27f9; dann m&#xfc;&#xdf;te aber die Policy einen Default-Eingang liefern..."/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1700515561328" ID="ID_408143188" MODIFIED="1700515586492" TEXT="brauche Funktions-Komposition">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1700515591564" ID="ID_9942626" MODIFIED="1700515612341" TEXT="&#xbb;beliebige Funktion&#xab; kann nicht als Lambda geschrieben werden"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1700515624424" ID="ID_124826134" MODIFIED="1700515633951" TEXT="hatte ich da nicht schon mal was...?">
<icon BUILTIN="help"/>
<node COLOR="#435e98" CREATED="1700515591564" ID="ID_9942626" MODIFIED="1700536064914" TEXT="&#xbb;beliebige Funktion&#xab; kann nicht einfach so als Lambda geschrieben werden">
<icon BUILTIN="messagebox_warning"/>
</node>
<node COLOR="#435e98" CREATED="1700515624424" ID="ID_124826134" MODIFIED="1700535846007" TEXT="hatte ich da nicht schon mal was...?">
<icon BUILTIN="help"/>
<node CREATED="1700535702492" ID="ID_1908131428" MODIFIED="1700535708739" TEXT="function-composition.hpp"/>
<node CREATED="1700535710200" ID="ID_1837337426" MODIFIED="1700535728417" TEXT="ja mei... da hab ich mich damals aber geplagt..."/>
<node CREATED="1700535729775" ID="ID_1473128844" MODIFIED="1700535747481" TEXT="und obendrein wird da &#xfc;berall noch unterwegs kopiert"/>
<node CREATED="1700535573341" ID="ID_853243392" MODIFIED="1700536001123">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
geht heutzutage <i>relativ kompakt </i>mit generischen Lambdas
</p>
</body>
</html>
</richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...ich hab nicht &#8222;lesbar&#8220; gesagt...
</p>
</body>
</html>
</richcontent>
<arrowlink COLOR="#7d8ebd" DESTINATION="ID_1908683822" ENDARROW="Default" ENDINCLINATION="-700;170;" ID="Arrow_ID_268440367" STARTARROW="None" STARTINCLINATION="-2105;102;"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#520f69" CREATED="1700535778319" ID="ID_1551608043" MODIFIED="1700535833317" TEXT="(...wenn man&apos;s denn auch richtig macht..)">
<font NAME="SansSerif" SIZE="11"/>
<icon BUILTIN="smiley-angry"/>
<node COLOR="#435e98" CREATED="1700535872361" ID="ID_1665961777" MODIFIED="1700535896456" TEXT="ich sage blo&#xdf;: Referenzen als R&#xfc;ckgabewert...">
<font NAME="SansSerif" SIZE="8"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1700535872361" ID="ID_1926879979" MODIFIED="1700535933707" TEXT="...und das RECHTFERTIGT eine Library-Funktion">
<font NAME="SansSerif" SIZE="9"/>
</node>
</node>
<node COLOR="#338800" CREATED="1700535940171" ID="ID_80963960" MODIFIED="1700535962800" TEXT="lib::meta::func::chained() modernisiert">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1700542206019" ID="ID_1976162819" MODIFIED="1700542219117" TEXT="damit adaptOut umformulieren">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1700542247014" ID="ID_185624626" MODIFIED="1700542286224" TEXT="die &#xbb;gesamt-Kette&#xab; als drawLimited() &#x2261; limited (asRand (hash));">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#435e98" CREATED="1700542287784" ID="ID_438421800" MODIFIED="1700542320316" TEXT="der &#xbb;parametrised Draw&#xab; ist komplex">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1700542321812" ID="ID_1608379732" MODIFIED="1700542336725">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
mu&#223; die Funktionsargumente <b>kopieren</b>
</p>
</body>
</html>
</richcontent>
</node>
<node COLOR="#338800" CREATED="1700542337681" ID="ID_1192170625" MODIFIED="1700542352927" TEXT="also in dem Fall dann doch ausschreiben">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1700542362630" ID="ID_730331755" MODIFIED="1700542375501" TEXT="mit speziellem Test ausleuchten...">
<icon BUILTIN="pencil"/>
<node CREATED="1700542382819" ID="ID_1832228113" MODIFIED="1700542395074" TEXT="eine Policy die zwei Eingangs-Argumente nimmt"/>
</node>
</node>
</node>
@ -103542,6 +103614,65 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node CREATED="1700523608170" ID="ID_1908683822" MODIFIED="1700535684359" TEXT="auch m&#xf6;glich mit generischen &#x3bb;">
<linktarget COLOR="#7d8ebd" DESTINATION="ID_1908683822" ENDARROW="Default" ENDINCLINATION="-700;170;" ID="Arrow_ID_268440367" SOURCE="ID_853243392" STARTARROW="None" STARTINCLINATION="-2105;102;"/>
<icon BUILTIN="idea"/>
<node CREATED="1700523633130" ID="ID_139668487" MODIFIED="1700523644209" TEXT="decltype mit pack-expansion kombinieren"/>
<node CREATED="1700534903724" ID="ID_1622776918" MODIFIED="1700535389459">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
[](auto&amp;&amp; ...<font color="#6e2237">args</font><font color="#000000">) -&gt; RetType </font>{ return fun (forward&lt;<font color="#5d19b8"><b>decltype(</b></font><font color="#6e2237">args</font><font color="#5d19b8"><b>)</b></font>&gt; (<font color="#6e2237">args</font>) <b><font color="#c50101">...</font></b>); }
</p>
</body>
</html>
</richcontent>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1700535233362" ID="ID_767885543" MODIFIED="1700535264581" TEXT="Warnung: bei solchen &#x3bb; mu&#xdf; man oft den Return-Typ explizit deklarieren">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1700535294817" ID="ID_1372454543" MODIFIED="1700535550594">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
weil bei normaler Typinferenz ein <i>decay </i>stattfindet
</p>
</body>
</html>
</richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
&#8222;zum Beispiel...&#8220; wenn die im Lamba verwendete Funktion eine Referenz zur&#252;ckliefert, dann passiert ein decay auf den unterliegenden Objekttyp, d.h. man erzeugt eine schwebende Kopie....
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1700535322845" ID="ID_1105790015" MODIFIED="1700535376993" TEXT="kann ziemlich heimt&#xfc;ckisch sein...">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
hab mich grad geschnitten und 5 Stunden nach der Ursache gesucht
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="smiley-oh"/>
</node>
</node>
</node>
</node>
<node CREATED="1622386272743" ID="ID_1645901699" MODIFIED="1622386277362" TEXT="I/O">
<node CREATED="1622386278646" ID="ID_359102162" MODIFIED="1622386285472" TEXT="sehr gro&#xdf;e Dateien">