C# 如何重写复杂的 C++ 代码行(嵌套三元运算符)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18237432/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 11:37:34  来源:igfitidea点击:

How to rewrite complicated lines of C++ code (nested ternary operator)

c#c++c

提问by invisiblerhino

I've been looking through someone else's code for debugging purposes and found this:

我一直在查看其他人的代码以进行调试,发现了这一点:

!m_seedsfilter ? good=true : m_seedsfilter==1 ? good=newClusters(Sp) : good=newSeed(Sp);  

What does this mean? Is there an automated tool that will render this into more comprehensible if/else statements? Any tips for dealing with complicated control structures like this?

这是什么意思?是否有一种自动化工具可以将其转化为更易于理解的 if/else 语句?处理像这样复杂的控制结构有什么技巧吗?

Edit note: I changed this from "unnecessarily complicated" to "complicated" in the title, since it's a matter of opinion. Thanks for all your answers so far.

编辑说明:我在标题中将其从“不必要的复杂”更改为“复杂”,因为这是一个见仁见智的问题。感谢您到目前为止的所有回答。

采纳答案by phonetagger

The statement as written could be improved if rewritten as follows....

如果重写如下,可以改进所写的语句......

good = m_seedsfilter==0 ? true :
       m_seedsfilter==1 ? newClusters(Sp) :
                          newSeed(Sp);

...but in general you should just become familar with the ternary statement. There is nothing inherently evil about either the code as originally posted, or xanatos' version, or mine. Ternary statements are not evil, they're a basic feature of the language, and once you become familiar with them, you'll note that code like this (as I've posted, not as written in your original post) is actually easier to read than a chain of if-else statements. For example, in this code, you can simply read this statement as follows: "Variable goodequals... if m_seedsfilter==0, then true, otherwise, if m_seedsfilter==1, then newClusters(Sp), otherwise, newSeed(Sp)."

...但总的来说,您应该熟悉三元语句。最初发布的代码、xanatos 的版本或我的版本都没有本质上的邪恶。三元语句并不邪恶,它们是该语言的一个基本特性,一旦你熟悉了它们,你会注意到这样的代码(正如我发布的,而不是你原帖中写的)实际上更容易阅读比一系列​​ if-else 语句。例如,在这段代码中,您可以简单地阅读以下语句:“变量good等于...如果m_seedsfilter==0,则true,否则,如果m_seedsfilter==1,则newClusters(Sp),否则,newSeed(Sp)。”

Note that my version above avoids three separate assignments to the variable good, and makes it clear that the goal of the statement is to assign a value to good. Also, written this way, it makes it clear that essentially this is a "switch-case" construct, with the default case being newSeed(Sp).

请注意,我上面的版本避免了对变量进行三个单独的赋值good,并清楚地表明该语句的目标是为 赋值good。此外,以这种方式编写,它清楚地表明本质上这是一个“切换案例”构造,默认情况下为newSeed(Sp).

It should probably be noted that my rewrite above is good as long as operator!()for the type of m_seedsfilteris not overridden. If it is, then you'd have to use this to preserve the behavior of your original version...

可能应该注意的是,只要operator!()类型m_seedsfilter不被覆盖,我上面的重写就很好。如果是,那么您必须使用它来保留原始版本的行为......

good = !m_seedsfilter   ? true :
       m_seedsfilter==1 ? newClusters(Sp) :
                          newSeed(Sp);

...and as xanatos' comment below proves, if your newClusters()and newSeed()methods return different types than each other, and if those types are written with carefully-crafted meaningless conversion operators, then you'll have to revert to the original code itself (though hopefully formatted better, as in xanatos' own post) in order to faithfully duplicate the exact same behavior as your original post. But in the real world, nobody's going to do that, so my first version above should be fine.

...正如下面 xanatos 的评论所证明的那样,如果您的newClusters()newSeed()方法返回彼此不同的类型,并且如果这些类型是用精心设计的无意义转换运算符编写的,那么您将不得不恢复到原始代码本身(尽管希望格式更好,就像在 xanatos 自己的帖子中一样),以便忠实地复制与原始帖子完全相同的行为。但在现实世界中,没有人会这样做,所以我上面的第一个版本应该没问题。



UPDATE, two and a half years after the original post/answer: It's interesting that @TimothyShields and I keep getting upvotes on this from time to time, and Tim's answer seems to consistently track at about 50% of this answer's upvotes, more or less (43 vs 22 as of this update).

更新,在原始帖子/答案发布两年半后:有趣的是,@TimothyShields 和我不时不断地对此进行投票,而蒂姆的回答似乎始终保持在该答案投票的 50% 左右,或多或少(截至本次更新,43 对 22)。

I thought I'd add another example of the clarity that the ternary statement can add when used judiciously. The examples below are short snippets from code I was writing for a callstack usage analyzer (a tool that analyzes compiled C code, but the tool itself is written in C#). All three variants accomplish exactly the same objective, at least as far as externally-visible effects go.

我想我会添加另一个例子,说明三元语句在明智地使用时可以添加的清晰度。下面的示例是我为调用堆栈使用分析器(一种分析编译的 C 代码的工具,但该工具本身是用 C# 编写的)编写的代码的简短片段。所有三种变体都实现了完全相同的目标,至少就外部可见效果而言。

1. WITHOUT the ternary operator:

1. 没有三元运算符:

Console.Write(new string(' ', backtraceIndentLevel) + fcnName);
if (fcnInfo.callDepth == 0)
{
   Console.Write(" (leaf function");
}
else if (fcnInfo.callDepth == 1)
{
   Console.Write(" (calls 1 level deeper");
}
else
{
   Console.Write(" (calls " + fcnInfo.callDepth + " levels deeper");
}
Console.WriteLine(", max " + (newStackDepth + fcnInfo.callStackUsage) + " bytes)");

2. WITH the ternary operator, separate calls to Console.Write():

2. 使用三元运算符,单独调用 Console.Write():

Console.Write(new string(' ', backtraceIndentLevel) + fcnName);
Console.Write((fcnInfo.callDepth == 0) ? (" (leaf function") :
              (fcnInfo.callDepth == 1) ? (" (calls 1 level deeper") :
                                         (" (calls " + fcnInfo.callDepth + " levels deeper"));
Console.WriteLine(", max " + (newStackDepth + fcnInfo.callStackUsage) + " bytes)");

3. WITH the ternary operator, collapsed to a single call to Console.Write():

3. 使用三元运算符,折叠为对 Console.Write() 的单个调用:

Console.WriteLine(
   new string(' ', backtraceIndentLevel) + fcnName +
   ((fcnInfo.callDepth == 0) ? (" (leaf function") :
    (fcnInfo.callDepth == 1) ? (" (calls 1 level deeper") :
                               (" (calls " + fcnInfo.callDepth + " levels deeper")) +
   ", max " + (newStackDepth + fcnInfo.callStackUsage) + " bytes)");

One might argue that the difference between the three examples above is trivial, and since it's trivial, why not prefer the simpler (first) variant? It's all about being concise; expressing an idea in "as few words as possible" so that the listener/reader can still remember the beginning of the idea by the time I get to the end of the idea. When I speak to small children, I use simple, short sentences, and as a result it takes more sentences to express an idea. When I speak with adults fluent in my language, I use longer, more complex sentences that express ideas more concisely.

有人可能会争辩说,上面三个例子之间的区别是微不足道的,既然微不足道,为什么不更喜欢更简单的(第一个)变体呢?一切都是为了简洁;用“尽可能少的词”表达一个想法,以便听者/读者在我到达想法的结尾时仍能记住想法的开头。当我和小孩子说话时,我使用简单、简短的句子,因此需要更多的句子来表达一个想法。当我与能说流利的语言的成年人交谈时,我会使用更长、更复杂的句子来更简洁地表达想法。

These examples print a single line of text to the standard output. While the operation they perform is simple, it should be easy to imagine them as a subset of a larger sequence. The more concisely I can clearly express subsets of that sequence, the more of that sequence can fit on my editor's screen. Of course I can easily take that effort too far, making it more difficult to comprehend; the goal is to find the "sweet spot" between being comprehensible and concise. I argue that once a programmer becomes familiar with the ternary statement, comprehending code that uses them becomes easier than comprehending code that does not (e.g. 2and 3above, vs. 1above).

这些示例将单行文本打印到标准输出。虽然它们执行的操作很简单,但很容易将它们想象成更大序列的子集。我越能简洁地表达该序列的子集,该序列中的内容就越能显示在我的编辑器屏幕上。当然,我很容易把这种努力做得太过分,使它更难以理解;目标是在易于理解和简洁之间找到“最佳点”。我认为,一旦程序员熟悉了三元语句,理解使用它们的代码就会比理解不使用它们的代码更容易(例如上面的23,对比上面的1)。

The final reason experienced programmers should feel comfortable using ternary statements is to avoid creating unnecessary temporary variables when making method calls. As an example of that, I present a fourth variant of the above examples, with the logic condensed to a single call to Console.WriteLine(); the result is both lesscomprehensible andlessconcise:

有经验的程序员应该对使用三元语句感到舒服的最后一个原因是在进行方法调用时避免创建不必要的临时变量。作为一个例子,我提出了上述例子的第四个变体,将逻辑浓缩为对Console.WriteLine();的单个调用。结果既不太容易理解,也不太简洁:

4. WITHOUT the ternary operator, collapsed to a single call to Console.Write():

4. 没有三元运算符,折叠为对 Console.Write() 的单个调用:

string tempStr;
if (fcnInfo.callDepth == 0)
{
   tempStr = " (leaf function";
}
else if (fcnInfo.callDepth == 1)
{
   tempStr = " (calls 1 level deeper";
}
else
{
   tempStr = " (calls " + fcnInfo.callDepth + " levels deeper";
}
Console.WriteLine(new string(' ', backtraceIndentLevel) + fcnName + tempStr +
                  ", max " + (newStackDepth + fcnInfo.callStackUsage) + " bytes)");

Before arguing that "condensing the logic to a single call to Console.WriteLine()is unnecessary," consider that this is merely an example: Imagine calls to some other method, one which takes multiple parameters, all of which require temporaries based on the state of other variables. You could create your own temporaries and make the method call with those temporaries, or you could use the ternary operator and let the compiler create its own (unnamed) temporaries. Again I argue that the ternary operator enables far more concise and comprehensiblecode than without. But for it to be comprehensible you'll have to drop any preconceived notions you have that the ternary operator is evil.

在争论“将逻辑浓缩为单个调用Console.WriteLine()是不必要的”之前,请考虑这仅仅是一个示例:想象一下调用其他方法,该方法采用多个参数,所有这些都需要基于其他变量状态的临时变量。您可以创建自己的临时文件并使用这些临时文件进行方法调用,或者您可以使用三元运算符并让编译器创建自己的(未命名的)临时文件。我再次争辩说,三元运算符比没有三元运算符可以实现更简洁和易于理解的代码。但是要使其易于理解,您必须放弃任何先入为主的观念,即三元运算符是邪恶的。

回答by xanatos

This is better?

这个更好?

!m_seedsfilter ? good=true 
               : m_seedsfilter==1 ? good=newClusters(Sp) 
                                  : good=newSeed(Sp);  

I'll add that, while it's theoretically possible to simplify this expression (by why? It's so much clear!), the resulting expression wouldn't probably be 100% equivalent in all the possible cases... And showing if two expressions are really equivalent in C++ is a problem very very very very very complex...

我要补充一点,虽然理论上可以简化这个表达式(为什么?太清楚了!),结果表达式在所有可能的情况下可能不会 100% 等效......并显示两个表达式是否是在 C++ 中真正等效是一个非常非常非常非常复杂的问题......

The degenerate example I have contrived (http://ideone.com/uLpe0L) (note that it isn't very degenerate... It's only based on a small programming error) is based on considering gooda bool, creating two classes UnixDateTimeand SmallUnixDateTime, with newClusters()returning a SmallUnixDateTimeand newSeed()returning a UnixDateTime. They both should be used to contain a Unix datetime in the format of the number of seconds from 1970-01-01 midnight. SmallUnixDateTimeuses an int, while UnixDateTimeuses a long long. Both are implicitly convertible to bool(they return if their inner value is != 0, something "classical"), but UnixDateTimeis even implicitly convertible to SmallUnixDateTime(this is wrong, because there could be a loss of precision... Here it's the small programming error). On failure of the conversion, a SmallUnixDateTimesetted to 0is returned. In the code of this example there will always be a single conversion: between SmallUnixDateTimeto boolor between UnixDateTimeto bool...

我设计的退化示例(http://ideone.com/uLpe0L)(请注意,它不是很退化......它只是基于一个小的编程错误)基于考虑gooda bool,创建两个类UnixDateTimeSmallUnixDateTime,与newClusters()返回 aSmallUnixDateTimenewSeed()返回 a UnixDateTime。它们都应该用于包含格式为 1970-01-01 午夜的秒数格式的 Unix 日期时间。SmallUnixDateTime使用int,而UnixDateTime使用long long。两者都可以隐式转换为bool(如果它们的内部值为 ,则返回!= 0“经典”),但UnixDateTime甚至可以隐式转换为SmallUnixDateTime(这是错误的,因为可能会损失精度......这是小编程错误)。转换失败时,将返回SmallUnixDateTime设置为0。在这个例子的代码中,总会有一个单一的转换:SmallUnixDateTimetobool之间或UnixDateTimeto之间bool……

While in this similar-but-different example:

而在这个相似但不同的例子中:

good = !m_seedsfilter ? true 
                      : m_seedsfilter==1 ? newClusters(Sp) 
                                         : newSeed(Sp);

there are two possible paths: SmallUnixDateTime(newClusters(Sp)) is converted to boolor UnixDateTime(newSeed(Sp))is converted first to SmallUnixDateTimeand then to bool. Clearly the two expressions aren't equivalent.

有两种可能的路径:SmallUnixDateTime( newClusters(Sp)) 转换为boolUnixDateTime( newSeed(Sp)) 先转换为SmallUnixDateTime,然后转换为bool。显然,这两个表达式是不等价的。

To make it work (or "not work"), newSeed(Sp)returns a value that can't be contained in a SmallUnixTime(std::numeric_limits<int>::max() + 1LL).

为了使其工作(或“不工作”),newSeed(Sp)返回一个不能包含在SmallUnixTime( std::numeric_limits<int>::max() + 1LL) 中的值。

回答by Timothy Shields

The equivalent non-evil code is this:

等效的非邪恶代码是这样的:

if (m_seedsfilter == 0)
{
    good = true;
}
else if (m_seedsfilter == 1)
{
    good = newClusters(Sp);
}
else
{
    good = newSeed(Sp);
}

Chained ternary operators - that is, the following

链式三元运算符 - 即以下

condition1 ? A : condition2 ? B : condition3 ? C : D

- are a great way to make your code unreadable.

- 是使您的代码不可读的好方法。

I'll second @phonetagger's suggestion that you become familiar with ternary operators - so that you can eliminate nested ones when you encounter them.

我将第二个@phonetagger 建议您熟悉三元运算符 - 这样您就可以在遇到它们时消除嵌套的运算符。

回答by Nicola Musatti

if ( !m_seedsfilter )
  good = true;
else if ( m_seedsfilter == 1 )
  good = newClusters(Sp);
else
  good = newSeed(Sp);

An expression followed by ?corresponds roughly to if ( expression )while the :introduces something similar to an elseclause. Note that this is an expression rather than a statement, i.e.

后跟的表达式?大致对应于if ( expression )while:引入类似于else子句的内容。请注意,这是一个表达式而不是一个语句,即

<condition> ? <expression-1> : <expression-2>

is an expression whose value is expression-1if conditionis true, otherwise it is expression-2.

是一个表达式,其值为expression-1ifcondition为真,否则为expression-2

回答by Michael

Nested ternary statements make code less readable IMHO. I would use them only when they significantly simplify the rest of the code. The quoted code could be re-written like this:

嵌套的三元语句使代码的可读性降低恕我直言。我只会在它们显着简化其余代码时使用它们。引用的代码可以这样重写:

good = !m_seedsfilter ? true : m_seedsfilter==1 ? newClusters(Sp) : newSeed(Sp); 

or like this:

或者像这样:

if(!m_seedsfilter)
    good = true;
else if(m_seedsfilter==1)
    good = newClusters(Sp);
else
    good = newSeed(Sp);

The 1st choice is more succinct, but less readable for a novice and less debugable.

第一种选择更简洁,但对于新手来说可读性和可调试性较差。

回答by John Bode

To answer your main question, this is an example of a conditional expression:

要回答您的主要问题,这是条件表达式的示例:

conditional-expression:
    logical-OR-expression
    logical-OR-expression ? expression : conditional-expression

If the logical-OR-expressionevaluates to true, then the result of the expression is the expression following the ?, otherwise it's the expression following the :. For example,

如果逻辑或表达式的计算true结果为 ,则表达式的结果为 之后的表达式?,否则为 之后的表达式:。例如,

x = y > 0 ? 1 : 0;

will assign 1 to xif yis greater than 0, otherwise it will assign '0'.

x如果y大于 0则赋值为 1 ,否则赋值为 '0'。

You're right to feel queasy about the example because it's badly written. The author is trying to use the ?:operator as a control structure, which it's not meant for.

你对这个例子感到不安是对的,因为它写得很糟糕。作者试图将?:操作符用作控制结构,这并不适用。

A better way to write this would be

写这个的更好方法是


    good = !m_seedsfilter ? true : 
                            ( m_seedsfilter == 1 ? newClusters(SP) : 
                                                   newSeed(SP) );

If m_seedsfilterequals 0, then goodwill be set to true. If m_seedsfilterequals 1, then goodwill be set to the result of newClusters(SP). Otherwise, goodwill be set to the result of newSeed(SP).

如果m_seedsfilter等于 0,good则将设置为true。如果m_seedsfilter等于 1,good则将设置为 的结果newClusters(SP)。否则,good将设置为 的结果newSeed(SP)

回答by phyrrus9

!m_seedsfilter ? good=true : m_seedsfilter==1 ? good=newClusters(Sp) : good=newSeed(Sp);

Will translate to

将翻译成

if (!m_seedsfilter)
{
     good = true;
}
else
{
     if (m_seedsfilter == 1)
     {
          good = newClusters(Sp);
     }
     else
     {
          good = new Seed(Sp);
     }
}