如何管理自引用跨范围 lambda?
How does one manage a self-referring cross-scope lambda?
假设我有一个我想在计时器结束时调用的功能。我已将那部分功能放入 lambda 函数中。此外,在那个函数中,我可能希望设置另一个计时器以在以后的另一个场合调用同一个 lambda。
void doSetupThingsInSomeDecoupledCodeOrWhatever() {
std::function<void(float)> semiRecursiveFunc;
semiRecursiveFunc = [&semiRecursiveFunc](float deltaT){
if (whatever()) {
// Do something...
}
else {
// Do something else, then:
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS, semiRecursiveFunc);
}
};
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS, fooLambda);
}
现在,显然这行不通,因为 semiRecursiveFunc
绑定到 doSetupThingsInSomeDecoupledCodeOrWhatever
的范围,当计时器系统尝试 运行 时,该功能将不再存在,一切都会分解成壮观的火球。
管理此问题的最佳方法是什么?我不能将 semiRecursiveFunc
存储在指针中,因为据我所知,不能像那样声明 lambda。这种持久性 lambda 用例是否有一些通用工具?什么是最不丑陋的方法,周围的基础设施最少?是否有最佳实践可供遵循,我错过了一些相关工具?任何建议或建议将不胜感激。
您要查找的是a y-combinator, sometimes called a fixed-point combinator。
无论哪种方式,而不是完全使用 std::function
(这会增加不必要的开销),您可以像这样编写回调:
auto semiRecursiveCallback = combinator([](auto self, float deltaT){
if (whatever()) {
// Do something...
}
else {
// Do something else, then:
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
// Gives the timer a duration to wait, and a function to run at the end of it.
// NB: we pass 'self' as the argument
getTimerSystem().setNewTimer(durationMS, self);
}
});
其中 combinator
是我的链接答案的 y_combinator
实现或来自优秀 Boost.HOF 库的 boost::hof::fix
。
组合子确保对象本身可以访问它自己,所以你可以做递归的事情。在上面的代码中,你实际上得到了你自己的副本,但这很好:值语义很酷。
这是一个微型 Y 组合器:
template<class R>
auto Y = [] (auto f) {
auto action = [=] (auto action) {
return [=] (auto&&... args)->R {
return f( action(action),decltype(args)(args)... );
};
};
return action(action);
};
只需这样做:
auto semiRecursiveFunc = Y<void>([](auto&& semiRecursiveFunc, float deltaT){
if (whatever()) {
// Do something...
}
else {
// Do something else, then:
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS, semiRecursiveFunc);
}
);
而且有效。
Y<R>
将一个可调用对象作为第一个参数传递给递归对象。递归时,只需传递其余参数即可。
您可以编写更高级的 Y 组合器。这个复制 lambdas 状态 a lot 并且不挑剔地移动它,以保持其实现简单。它还要求您提供其 return 类型(由于 C++ 类型推导规则,这很难避免)。
这是一种Objective-C引用计数风格的方法。优点是您可以使用与您想要的原始函数相同的 lambda 签名(没有额外的参数)。缺点是它看起来丑陋且冗长,而且你必须始终通过 shared_ptr
; 使用 lambda;不能单独拿出来传。
void doSetupThingsInSomeDecoupledCodeOrWhatever() {
std::shared_ptr<std::weak_ptr<std::function<void(float)>>> weakFuncHolder =
std::make_shared<std::weak_ptr<std::function<void(float)>>>();
std::shared_ptr<std::function<void(float)>> semiRecursiveFunc =
std::make_shared<std::function<void(float)>>([=](float deltaT) {
std::shared_ptr<std::function<void(float)>> strongFunc(*weakFuncHolder);
if (whatever()) {
// Do something...
}
else {
// Do something else, then:
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f);
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS,
[=](float deltaT){ (*strongFunc)(deltaT); });
}
});
*weakFuncHolder = semiRecursiveFunc;
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f);
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS,
[=](float deltaT){ (*semiRecursiveFunc)(deltaT); });
}
假设我有一个我想在计时器结束时调用的功能。我已将那部分功能放入 lambda 函数中。此外,在那个函数中,我可能希望设置另一个计时器以在以后的另一个场合调用同一个 lambda。
void doSetupThingsInSomeDecoupledCodeOrWhatever() {
std::function<void(float)> semiRecursiveFunc;
semiRecursiveFunc = [&semiRecursiveFunc](float deltaT){
if (whatever()) {
// Do something...
}
else {
// Do something else, then:
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS, semiRecursiveFunc);
}
};
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS, fooLambda);
}
现在,显然这行不通,因为 semiRecursiveFunc
绑定到 doSetupThingsInSomeDecoupledCodeOrWhatever
的范围,当计时器系统尝试 运行 时,该功能将不再存在,一切都会分解成壮观的火球。
管理此问题的最佳方法是什么?我不能将 semiRecursiveFunc
存储在指针中,因为据我所知,不能像那样声明 lambda。这种持久性 lambda 用例是否有一些通用工具?什么是最不丑陋的方法,周围的基础设施最少?是否有最佳实践可供遵循,我错过了一些相关工具?任何建议或建议将不胜感激。
您要查找的是a y-combinator, sometimes called a fixed-point combinator。
无论哪种方式,而不是完全使用 std::function
(这会增加不必要的开销),您可以像这样编写回调:
auto semiRecursiveCallback = combinator([](auto self, float deltaT){
if (whatever()) {
// Do something...
}
else {
// Do something else, then:
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
// Gives the timer a duration to wait, and a function to run at the end of it.
// NB: we pass 'self' as the argument
getTimerSystem().setNewTimer(durationMS, self);
}
});
其中 combinator
是我的链接答案的 y_combinator
实现或来自优秀 Boost.HOF 库的 boost::hof::fix
。
组合子确保对象本身可以访问它自己,所以你可以做递归的事情。在上面的代码中,你实际上得到了你自己的副本,但这很好:值语义很酷。
这是一个微型 Y 组合器:
template<class R>
auto Y = [] (auto f) {
auto action = [=] (auto action) {
return [=] (auto&&... args)->R {
return f( action(action),decltype(args)(args)... );
};
};
return action(action);
};
只需这样做:
auto semiRecursiveFunc = Y<void>([](auto&& semiRecursiveFunc, float deltaT){
if (whatever()) {
// Do something...
}
else {
// Do something else, then:
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS, semiRecursiveFunc);
}
);
而且有效。
Y<R>
将一个可调用对象作为第一个参数传递给递归对象。递归时,只需传递其余参数即可。
您可以编写更高级的 Y 组合器。这个复制 lambdas 状态 a lot 并且不挑剔地移动它,以保持其实现简单。它还要求您提供其 return 类型(由于 C++ 类型推导规则,这很难避免)。
这是一种Objective-C引用计数风格的方法。优点是您可以使用与您想要的原始函数相同的 lambda 签名(没有额外的参数)。缺点是它看起来丑陋且冗长,而且你必须始终通过 shared_ptr
; 使用 lambda;不能单独拿出来传。
void doSetupThingsInSomeDecoupledCodeOrWhatever() {
std::shared_ptr<std::weak_ptr<std::function<void(float)>>> weakFuncHolder =
std::make_shared<std::weak_ptr<std::function<void(float)>>>();
std::shared_ptr<std::function<void(float)>> semiRecursiveFunc =
std::make_shared<std::function<void(float)>>([=](float deltaT) {
std::shared_ptr<std::function<void(float)>> strongFunc(*weakFuncHolder);
if (whatever()) {
// Do something...
}
else {
// Do something else, then:
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f);
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS,
[=](float deltaT){ (*strongFunc)(deltaT); });
}
});
*weakFuncHolder = semiRecursiveFunc;
float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f);
// Gives the timer a duration to wait, and a function to run at the end of it.
getTimerSystem().setNewTimer(durationMS,
[=](float deltaT){ (*semiRecursiveFunc)(deltaT); });
}