实现Bind()(LINQ中的SelectMany)而不产生收益(在C+;+;) 我尝试在C++中实现 Ana >代码> CATA > BIN < /Cuff>(from)。我管理过Ana和Cata,但是Bind让我迷路了
以下是我对实现Bind()(LINQ中的SelectMany)而不产生收益(在C+;+;) 我尝试在C++中实现 Ana >代码> CATA > BIN < /Cuff>(from)。我管理过Ana和Cata,但是Bind让我迷路了,c++,.net,bind,linq,C++,.net,Bind,Linq,以下是我对Ana和Cata的看法: #define FEnumerable function<function<Option<T>(void)>(void)> #define TResult function<function<Option<R>(void)>(void)> template<typename T> class Option { public: bool HasValue; T
Ana
和Cata
的看法:
#define FEnumerable function<function<Option<T>(void)>(void)>
#define TResult function<function<Option<R>(void)>(void)>
template<typename T> class Option {
public:
bool HasValue;
T value;
Option(T value) : HasValue(true), value(value) { }
Option() : HasValue(false) { }
};
template<typename T> FEnumerable Ana(T seed, function<bool(T)> condition, function<T(T)> next) {
auto result = [condition, next, seed]() -> function<Option<T>()> {
return [condition, next, seed]() -> Option<T> {
static Option<T> value;
value = Option<T>(value.HasValue ? next(value.value) : seed);
if (condition(value.value))
return Option<T>(value.value);
return Option<T>();
};
};
return result;
};
template<typename T, typename R> TResult Ana(T seed, function<bool(T)> condition, function<T(T)> next, function<R(T)> translation) {
auto result = [condition, next, seed, translation]() -> function<Option<R>()> {
return [condition, next, seed, translation]() -> Option<R> {
static Option<T> value;
value = Option<T>(value.HasValue ? next(value.value) : seed);
if (condition(value.value))
return Option<R>(translation(value.value));
return Option<R>();
};
};
return result;
};
template<typename T, typename A> A Cata(FEnumerable source, A seed, function<A(A, T)> fn) {
auto e = source();
A result = seed;
Option<T> value;
while ((value = e()).HasValue)
result = fn(result, value.value);
return result;
};
template<typename T, typename A, typename R> R Cata(FEnumerable source, A seed, function<A(A, T)> fn, function<R(A)> translation) {
auto e = source();
R result = seed;
Option<T> value;
while ((value = e()).HasValue)
result = fn(result, value.value);
return translation(result);
};
换句话说,Bind()
需要接受一个FEnumerable
(惰性序列)和一个选择器函数,该函数接受单个值并返回一个值序列;然后Bind必须为每个输入值调用选择器函数一次,然后在一个大序列中返回选择器返回的所有值但很懒。作为一个FEnumerable
作为参考,以下是C#中的效果:
foreach (var value in source)
foreach (var result in selector(value))
yield return result;
是的,没有收益的话会有点困难。下面是如何在C++中用懒惰的评价来看待:
list<R> results;
while ((auto value = source()).HasValue)
while ((auto result = selector(value)).HasValue)
results.push_back(result);
return results;
列出结果;
while((auto value=source()).HasValue)
while((自动结果=选择器(值)).HasValue)
结果。推回(结果);
返回结果;
但我需要惰性求值,这意味着嵌套lambda。如果有人走到这一步而没有头部爆炸,请帮助我。我们可以尝试创建一个子源,即当我们迭代时,
源
的连续结果,作为关闭状态的一部分:
template<typename T>
TResult Bind(FEnumerable source, function<TResult(T)> selector)
{
return [source, selector]() -> function<Option<R>()>
{
auto e = source();
// Note that std::function is nullable and I am using
// this possible state!
// If this isn't std::function, making subsource an
// Option<function<Option<R>()> is always a possibility
function<Option<R>()> subsource;
return [e, subsource, selector]() -> Option<R>
{
while(!subsource) {
// This means we need to fetch a new subsource
auto candidate = e();
if(!candidate.HasValue) {
// Iteration ends here once `e` has run out
return Option<R>();
} else {
subsource = selector(candidate.value)();
}
auto result = subsource();
if(!result.HasValue) {
// We selected over an empty subsource, so let's
// try again and maybe pick a new fresh one
subsource = nullptr;
continue;
}
return result;
}
};
};
}
模板
TResult绑定(FEnumerable源、函数选择器)
{
返回[源,选择器]()->函数
{
自动e=源();
//请注意,std::function是可为空的,我正在使用
//这种可能的状态!
//如果这不是std::function,则将子源设置为
//选择权
{
而(!子资源){
//这意味着我们需要获取一个新的子资源
自动候选=e();
如果(!candidate.HasValue){
//一旦'e'用完,迭代就在这里结束
返回选项();
}否则{
子资源=选择器(候选值)();
}
自动结果=子资源();
如果(!result.HasValue){
//我们选择了一个空的子资源,所以让我们
//再试一次,也许再挑一个新的
subsource=nullptr;
继续;
}
返回结果;
}
};
};
}
注意,这依赖于重复调用e()
,假设每次迭代结束时都返回空的选项。否则,您可以显式地编码“我们已经用完了子源”状态,例如,使用一个或多个闭包变量
或者,一旦迭代结束,客户机代码就有责任停止对Bind
的结果进行迭代,在这种情况下,您也可以继续,并且source
/e
的结尾仍然只会到达一次。好的,我从Luc Danton那里获取了代码,修复了一些问题,并使其正常工作。以下是寻找解决方案的其他人的代码:
template<typename T, typename R> TResult Bind(FEnumerable source, function<TResult(T)> selector) {
return [source, selector]() -> function<Option<R>()> {
auto e = source();
// Note that std::function is nullable and I am using this possible state!
// If this isn't std::function, making subsource an Option<function<Option<R>()> is always a possibility
function<Option<R>()> subsource;
return [e, subsource, selector]() mutable -> Option<R> {
while (true) {
while(!subsource) { // This means we need to fetch a new subsource
auto candidate = e();
if (!candidate.HasValue)
return Option<R>(); // Iteration ends here once `source` has run out
subsource = selector(candidate.value)();
}
auto result = subsource();
if (result.HasValue)
return result;
subsource = nullptr; // We selected over an empty subsource, so let's try again and maybe pick a new fresh one
}
return Option<R>();
};
};
}
模板TResult绑定(FEnumerable源代码,函数选择器){
返回[源,选择器]()->函数{
自动e=源();
//请注意,std::function是可空的,我使用的是这种可能的状态!
//如果这不是std::function,则将subsource作为选项{
while(true){
而(!subsource){//这意味着我们需要获取一个新的子资源
自动候选=e();
如果(!candidate.HasValue)
return Option();//一旦'source'用完,迭代就在这里结束
子资源=选择器(候选值)();
}
自动结果=子资源();
if(result.HasValue)
返回结果;
subsource=nullptr;//我们选择了一个空的子资源,所以让我们再试一次,也许可以选择一个新的
}
返回选项();
};
};
}
一个可数的
如何不仅仅是一个函数
?您是否希望能够重新启动?我想这是有道理的。顺便说一句,在C++14中,boost::optional
正在进入std
。FEnumerable是一个返回函数的函数。就像std::list可以给你一个迭代器一样,迭代器可以给你值;您可以多次询问FEnumerable的函数值,并获得不同的迭代器实例。@Sodalmaghty我有一个GitHub项目linq cpp,它将linq功能引入C++11。看起来这也是你想要做的(在某种程度上)。如果您感兴趣,请查看并与我联系。有几个bug-您忘记了mutable
,您的while
块包含了太多的代码,但它很接近。我把它们修好了,现在一切正常。非常感谢!我会接受你的答案,但是为任何其他闲逛的人发布一个带有固定代码的新答案。
template<typename T, typename R> TResult Bind(FEnumerable source, function<TResult(T)> selector) {
return [source, selector]() -> function<Option<R>()> {
auto e = source();
// Note that std::function is nullable and I am using this possible state!
// If this isn't std::function, making subsource an Option<function<Option<R>()> is always a possibility
function<Option<R>()> subsource;
return [e, subsource, selector]() mutable -> Option<R> {
while (true) {
while(!subsource) { // This means we need to fetch a new subsource
auto candidate = e();
if (!candidate.HasValue)
return Option<R>(); // Iteration ends here once `source` has run out
subsource = selector(candidate.value)();
}
auto result = subsource();
if (result.HasValue)
return result;
subsource = nullptr; // We selected over an empty subsource, so let's try again and maybe pick a new fresh one
}
return Option<R>();
};
};
}