为啥要在条件中使用赋值?
Posted
技术标签:
【中文标题】为啥要在条件中使用赋值?【英文标题】:Why would you use an assignment in a condition?为什么要在条件中使用赋值? 【发布时间】:2010-09-14 04:34:03 【问题描述】:在许多语言中,赋值在条件下是合法的。我一直不明白这背后的原因。为什么要写:
if (var1 = var2)
...
代替:
var1 = var2;
if (var1)
...
【问题讨论】:
这个问题真的与语言无关吗?哪些语言会受此影响?我知道 C 和 C++ 是,还有什么? 【参考方案1】:我今天在用 Arduino(C 语言)编程时使用它。
案例
我有一个发射器和一个接收器。 发送器想要发送数据,直到它被接收到。我想在进程完成后设置一个标志。
while (!(newtork_joined = transmitter.send(data)))
Serial.println("Not Joined");
这里:
"transmitter.send" - 传输成功时返回真 “newtork_joined” - 是我成功时设置的标志结果
传输不成功时,不设置标志,while循环为真,一直执行
成功时,设置标志,while循环为假,我们退出
不漂亮吗?
【讨论】:
【参考方案2】:原因是:
性能改进(有时)
更少的代码(总是)
举个例子:有一个方法someMethod()
,在if
条件下你要检查方法的返回值是否为null
。如果没有,您将再次使用返回值。
If(null != someMethod())
String s = someMethod();
......
//Use s
这会影响性能,因为您要调用相同的方法两次。而是使用:
String s;
If(null != (s = someMethod()))
......
//Use s
【讨论】:
这是什么语言?它不会在 C# 或 C++ 中编译(“If”的大写)。【参考方案3】:我发现它对于返回可选项的函数非常有用(boost::optional
或 C++17 中的 std::optional
):
std::optional<int> maybe_int(); // function maybe returns an int
if (auto i = maybe_int())
use_int(*i);
这减少了我的变量的范围,使代码更紧凑并且不妨碍可读性(我发现)。
指针也一样:
int* ptr_int();
if (int* i = ptr_int())
use_int(*i);
【讨论】:
顺便说一句,如果你想限制变量的范围,你可以使用 if with initializers :)【参考方案4】:我发现它在通常涉及错误检测等的操作链中最有用。
if ((rc = first_check(arg1, arg2)) != 0)
report error based on rc
else if ((rc = second_check(arg2, arg3)) != 0)
report error based on new rc
else if ((rc = third_check(arg3, arg4)) != 0)
report error based on new rc
else
do what you really wanted to do
替代方案(不使用条件中的赋值)是:
rc = first_check(arg1, arg2);
if (rc != 0)
report error based on rc
else
rc = second_check(arg2, arg3);
if (rc != 0)
report error based on new rc
else
rc = third_check(arg3, arg4);
if (rc != 0)
report error based on new rc
else
do what you really wanted to do
通过延长错误检查,替代方案可以在页面的 RHS 之外运行,而条件分配版本不会这样做。
错误检查也可以是“操作”——first_action()
、second_action()
、third_action()
——当然,不仅仅是检查。也就是说,可以在功能管理的过程中检查它们的步骤。 (在我使用的代码中,最常见的函数是前置条件检查,或函数工作所需的内存分配,或类似的思路)。
【讨论】:
是的,我确实这样做是为了避免过度嵌套,然后在网上搜索,看看这是否是一种常见的做法。最后我决定反对它,因为我实际上只有一个 else 案例,但这似乎是将作业置于条件中的正当理由。 同事讨厌它!对我来说,至少在大多数情况下,它比一棵巨树或许多回报更易于维护。我更喜欢函数的单次返回... 一个合理的例子,伴随块使模式明显,不言自明。但是,好吧,块内(“伪”)代码看起来有点难看,而反向样式(将 what you really want to do 放在最后)并不是那么好。跨度> 【参考方案5】:简短的回答是Expression-oriented 编程语言允许更简洁的代码。他们不会强迫你separate commands from queries。
【讨论】:
在 if()、while() 和其他类似语句中强制执行没有赋值的编码是一场持久战。 C/C++ 因其副作用而臭名昭著,这通常会变成错误,尤其是当我们添加 ++ 和 -- 运算符时。 第一个链接没用:C或C++好像不是这样的语言。【参考方案6】:另一个好处是在使用 gdb 的过程中。 在下面的代码中,如果我们单步执行,错误代码是未知的。
while (checkstatus() != -1)
// process
相当
while (true)
int error = checkstatus();
if (error != -1)
// process
else
//fail
现在在单步中我们可以知道 checkstatus() 的返回错误代码是什么。
【讨论】:
你的意思是它可以在调试代码时对临时更改有所帮助?【参考方案7】:如果你无意中尝试使用赋值作为真值,GCC 可以帮助你检测(使用 -Wall),以防它建议你写
if ((n = foo()))
...
即使用额外的括号表示这确实是您想要的。
【讨论】:
很高兴知道。我希望你的同事以同样的方式阅读它。 我?天哪,我永远不会尝试将赋值用作真值。我指的是 GCC 的建议。 GCC 也可以编译Fortran。或许要明确说明编程语言?【参考方案8】:例如,在 php 中,它对于遍历 SQL 数据库结果很有用:
while ($row = mysql_fetch_assoc($result))
// Display row
这看起来比:
$row = mysql_fetch_assoc($result);
while ($row)
// Display row
$row = mysql_fetch_assoc($result);
【讨论】:
这还具有 $row 没有超出该循环的简洁副作用,因此可以更快地进行垃圾收集(无论如何都执行 GC 的语言)。【参考方案9】:循环比 if 语句更有用。
while( var = GetNext() )
...do something with var
否则必须写出来
var = GetNext();
while( var )
...do something
var = GetNext();
【讨论】:
我同意 - 但会写: while ((var = GetNext()) != 0) ... 没有三思而后行,部分原因是如果我不使用默认编译,GCC 会抱怨选项。 是的。假设您正在使用一种支持 for 循环声明的语言,并且您还没有在循环之外使用 var。 在某些语言中,例如 ANSI C,范围规则不允许这种方法,@KerrekSB @wirrbel:你的意思是 1989 年的 ANSI C?就像他们有蒸汽机和打孔卡的时候一样?可能是这样,但这是否相关? 该警告并不是因为在一段时间内分配有任何问题,而是因为在您真正打算进行比较时进行分配是一个常见的错误。如果您想摆脱警告,您可以执行与 @Jonathan Leffler 建议的 JS 等效的操作。就我个人而言,我可能会这样做,因为我非常不信任 javascript 的真实规则:) 在 C# 和 Java 等某些语言中也应该注意这是唯一的方法。【参考方案10】:当您编写while
循环而不是if
语句时,该习语更有用。对于if
声明,您可以按照您的描述将其分解。但如果没有这个结构,你要么不得不重复自己:
c = getchar();
while (c != EOF)
// ...
c = getchar();
或使用循环半结构:
while (true)
c = getchar();
if (c == EOF) break;
// ...
我通常更喜欢循环半形式。
【讨论】:
对于while
循环,最好使用:do c = getchar(); ... while (c != EOF);
可以使用for
循环代替:for (char c = getchar(); c != EOF; c= getchar()) /* do something with c */
——这是最简洁的,for
总是对我说“循环”,风格上。小缺点是您必须指定返回 c
两次的函数。【参考方案11】:
如果你调用一个函数会更有用:
if (n = foo())
/* foo returned a non-zero value, do something with the return value */
else
/* foo returned zero, do something else */
当然,你可以把 n = foo();在单独的语句 then if (n),但我认为上面是一个相当易读的习语。
【讨论】:
我认为你应该把它括在括号中,否则 gcc 会抱怨:警告:建议在赋值周围使用括号作为真值 [-W括号] 像这样:if ((n = foo())) // 对返回值做一些事情 else // foo 返回零/NULL,做其他事情 【参考方案12】:如果您调用的函数返回要处理的数据或指示错误(或您已完成)的标志,这将很有用。
类似:
while ((c = getchar()) != EOF)
// process the character
// end of file reached...
就我个人而言,这是一个我不太喜欢的成语,但有时替代方案更难看。
【讨论】:
以上是关于为啥要在条件中使用赋值?的主要内容,如果未能解决你的问题,请参考以下文章
有人可以解释为啥条件运算符和赋值运算符一起使用时表现奇怪吗?
为啥 Java 没有条件与和条件或运算符的复合赋值版本? (&&=, ||=)