有没有更优雅的方法来使用带有独立 if() 的 else 语句
Is there a more elegant way to use one else statement with independent if()s
我目前正在使用 C++ 的 SFML 并尝试调整 window 的大小,但是我发现我的问题的解决方案不太合适,我正在寻找是否有更好的方法。
我有一个对象可以多次调整大小,所以它可能适合 window 或不适合,如果 window 较大,我必须扩展它,如果它较小,我必须缩小它。但是,我有 window 的最小尺寸,如果对象适合我想将 window 重置为该尺寸。
这是我现在使用的伪代码:
if (Object.getSize().x > window.getSize().x || Object.getSize().y > window.getSize.y){
if(Object.getSize().x > windowMinSize.x){
window.resize(Object.getSize().x, window.getSize().y)
}
if(Object.getSize().y > windowMinSize.y){
window.resize(window.getSize().x, Object.getSize().y)
}
}
else{
window.resize(windowMinSize);
}
我查看了 switch
和其他选项,但没有找到我要找的东西。
此处使用的正确模式是限制调用外部方法的代码路径的数量,以便只有一个使用点:
你的伪代码变成这样:
auto new_win_size = windowMinSize;
if (Object.getSize().x > window.getSize().x || Object.getSize().y > window.getSize.y){
if(Object.getSize().x > windowMinSize.x){
new_win_size.x = Object.getSize().x;
new_win_size.y = window.getSize().y;
}
if(Object.getSize().y > windowMinSize.y){
new_win_size.x = window.getSize().x;
new_win_size.y = Object.getSize().y;
}
}
window.resize(new_win_size );
但我怀疑你真正想要的是:
auto new_win_size = windowMinSize;
if(Object.getSize().x > windowMinSize.x){
new_win_size.x = Object.getSize().x;
}
if(Object.getSize().y > windowMinSize.y){
new_win_size.y = Object.getSize().y;
}
window.resize(new_win_size );
我有点困惑,因为最后听起来您只想将 window 的大小调整为对象大小,同时保持特定的最小大小。
因此,您只需要简单地调用 std::max()
来确定两个输入中较大的值:
unsigned int width = std::max(min_width, object_width);
unsigned int height = std::max(min_height, object_height);
使用新尺寸时,它基本上会调整 window 的大小以匹配您的对象,除非该对象小于您的最小尺寸。
您可以使用相同的模式来添加最大尺寸,使用 std::min()
作为附加层:
unsigned int width = std::min(max_width, std::max(min_width, object_width));
unsigned int height = std::min(max_height, std::max(min_height, object_height));
编辑: 正如 bolov 正确建议的那样,对于使用新 std::clamp
:
的现代编译器,这可以进一步简化
unsigned int width = std::clamp(object_width, min_width, max_width);
unsigned int height = std::clamp(object_height, min_height, max_height);
您可以使用可变 template
s 来获得您想要的。我这里的版本采用 else
函数(第一个参数),然后是函数对(谓词、块、谓词、块、...)。如果 none 个谓词的计算结果为 true
,则将执行 else
函数。这应该适用于 C++11 或更高版本。
这里可能有一些错误(我没有做过太多测试),但你可以踢轮胎。如果将 #if 0
更改为 #if 1
,您会发现 else
函数不会被调用。
#include <iostream>
template <typename PRED, typename EXEC>
bool multi_branch_detail(PRED pred, EXEC exec) {
if(pred()) {
exec();
return true;
}
return false;
}
template <typename PRED, typename EXEC, typename ...REM>
bool multi_branch_detail(PRED pred, EXEC exec, REM ...rem) {
auto result = false;
if(pred()) {
exec();
result = true;
}
return multi_branch_detail(std::forward<REM>(rem)...) || result;
}
template <typename ELSE, typename ...FNS>
void multi_branch(ELSE el, FNS ...fns) {
if(!multi_branch_detail(std::forward<FNS>(fns)...)) {
el();
}
}
int main() {
multi_branch(
[]() { std::cout << "No cases\n"; },
#if 0
[]() { return 1 < 2; }, []() { std::cout << "first case\n"; },
[]() { return 10 < 20; }, []() { std::cout << "second case\n"; },
#endif
[]() { return 1 > 2; }, []() { std::cout << "bug\n"; }
);
return 0;
}
输出 #if 0
:
No cases
输出 #if 1
:
first case
second case
我将您的问题解释为询问如何重构以下代码:
if ( A )
{
if ( B ) { X } else { Y }
if ( C ) { Z } else { Y }
}
else { Y }
以避免重复 Y
。
一种方法是:
bool A = ....;
bool B = ....;
bool C = ....;
if ( A && B ) { X }
else if ( A && C ) { Z }
else { Y }
尽管与原始代码的短路行为相比,这可能涉及不必要的函数调用。
最"obvious"的解决方案是存储一个变量:
bool updated = false;
if ( A )
{
if ( B ) { X; updated = true; }
if ( C ) { Z; updated = true; }
}
if ( !updated ) { Y }
另一种方法是使用可以突破的控制结构:
do
{
if ( A )
{
if ( B ) { X; break; }
if ( C ) { Z; break; }
}
Y;
} while (0);
有些人不喜欢这个,因为如果你正在阅读代码,它看起来像是我们进入了一个循环,但后来证明它不是一个循环。我个人会做一个函数(break
被 return
代替);但是如果使用 do...while(0)
你应该在开始处添加代码注释以指出这不是一个真正的循环。
我目前正在使用 C++ 的 SFML 并尝试调整 window 的大小,但是我发现我的问题的解决方案不太合适,我正在寻找是否有更好的方法。
我有一个对象可以多次调整大小,所以它可能适合 window 或不适合,如果 window 较大,我必须扩展它,如果它较小,我必须缩小它。但是,我有 window 的最小尺寸,如果对象适合我想将 window 重置为该尺寸。
这是我现在使用的伪代码:
if (Object.getSize().x > window.getSize().x || Object.getSize().y > window.getSize.y){
if(Object.getSize().x > windowMinSize.x){
window.resize(Object.getSize().x, window.getSize().y)
}
if(Object.getSize().y > windowMinSize.y){
window.resize(window.getSize().x, Object.getSize().y)
}
}
else{
window.resize(windowMinSize);
}
我查看了 switch
和其他选项,但没有找到我要找的东西。
此处使用的正确模式是限制调用外部方法的代码路径的数量,以便只有一个使用点:
你的伪代码变成这样:
auto new_win_size = windowMinSize;
if (Object.getSize().x > window.getSize().x || Object.getSize().y > window.getSize.y){
if(Object.getSize().x > windowMinSize.x){
new_win_size.x = Object.getSize().x;
new_win_size.y = window.getSize().y;
}
if(Object.getSize().y > windowMinSize.y){
new_win_size.x = window.getSize().x;
new_win_size.y = Object.getSize().y;
}
}
window.resize(new_win_size );
但我怀疑你真正想要的是:
auto new_win_size = windowMinSize;
if(Object.getSize().x > windowMinSize.x){
new_win_size.x = Object.getSize().x;
}
if(Object.getSize().y > windowMinSize.y){
new_win_size.y = Object.getSize().y;
}
window.resize(new_win_size );
我有点困惑,因为最后听起来您只想将 window 的大小调整为对象大小,同时保持特定的最小大小。
因此,您只需要简单地调用 std::max()
来确定两个输入中较大的值:
unsigned int width = std::max(min_width, object_width);
unsigned int height = std::max(min_height, object_height);
使用新尺寸时,它基本上会调整 window 的大小以匹配您的对象,除非该对象小于您的最小尺寸。
您可以使用相同的模式来添加最大尺寸,使用 std::min()
作为附加层:
unsigned int width = std::min(max_width, std::max(min_width, object_width));
unsigned int height = std::min(max_height, std::max(min_height, object_height));
编辑: 正如 bolov 正确建议的那样,对于使用新 std::clamp
:
unsigned int width = std::clamp(object_width, min_width, max_width);
unsigned int height = std::clamp(object_height, min_height, max_height);
您可以使用可变 template
s 来获得您想要的。我这里的版本采用 else
函数(第一个参数),然后是函数对(谓词、块、谓词、块、...)。如果 none 个谓词的计算结果为 true
,则将执行 else
函数。这应该适用于 C++11 或更高版本。
这里可能有一些错误(我没有做过太多测试),但你可以踢轮胎。如果将 #if 0
更改为 #if 1
,您会发现 else
函数不会被调用。
#include <iostream>
template <typename PRED, typename EXEC>
bool multi_branch_detail(PRED pred, EXEC exec) {
if(pred()) {
exec();
return true;
}
return false;
}
template <typename PRED, typename EXEC, typename ...REM>
bool multi_branch_detail(PRED pred, EXEC exec, REM ...rem) {
auto result = false;
if(pred()) {
exec();
result = true;
}
return multi_branch_detail(std::forward<REM>(rem)...) || result;
}
template <typename ELSE, typename ...FNS>
void multi_branch(ELSE el, FNS ...fns) {
if(!multi_branch_detail(std::forward<FNS>(fns)...)) {
el();
}
}
int main() {
multi_branch(
[]() { std::cout << "No cases\n"; },
#if 0
[]() { return 1 < 2; }, []() { std::cout << "first case\n"; },
[]() { return 10 < 20; }, []() { std::cout << "second case\n"; },
#endif
[]() { return 1 > 2; }, []() { std::cout << "bug\n"; }
);
return 0;
}
输出 #if 0
:
No cases
输出 #if 1
:
first case
second case
我将您的问题解释为询问如何重构以下代码:
if ( A )
{
if ( B ) { X } else { Y }
if ( C ) { Z } else { Y }
}
else { Y }
以避免重复 Y
。
一种方法是:
bool A = ....;
bool B = ....;
bool C = ....;
if ( A && B ) { X }
else if ( A && C ) { Z }
else { Y }
尽管与原始代码的短路行为相比,这可能涉及不必要的函数调用。
最"obvious"的解决方案是存储一个变量:
bool updated = false;
if ( A )
{
if ( B ) { X; updated = true; }
if ( C ) { Z; updated = true; }
}
if ( !updated ) { Y }
另一种方法是使用可以突破的控制结构:
do
{
if ( A )
{
if ( B ) { X; break; }
if ( C ) { Z; break; }
}
Y;
} while (0);
有些人不喜欢这个,因为如果你正在阅读代码,它看起来像是我们进入了一个循环,但后来证明它不是一个循环。我个人会做一个函数(break
被 return
代替);但是如果使用 do...while(0)
你应该在开始处添加代码注释以指出这不是一个真正的循环。