为什么要在条件中使用作业?


79

在许多语言中,条件分配是合法的。我从不了解背后的原因。你为什么写:

if (var1 = var2) {
  ...
}

代替:

var1 = var2;
if (var1) {
  ...
}

这个问题真的与语言无关吗?哪些语言受此影响?我知道C和C ++,还有什么?

Answers:


113

对于循环而言,它比if语句更有用。

while( var = GetNext() )
{
  ...do something with var 
}

否则必须写

var = GetNext();
while( var )
{
 ...do something
 var = GetNext();
}

14
我同意-但会这样写:while((var = GetNext())!= 0){...}无需三思,部分原因是GCC抱怨如果我不使用默认编译选项。
Jonathan Leffler

1
真正。假设您使用的语言支持for循环声明,并且尚未在循环外使用var。
杰拉尔德

1
在某些语言(例如ANSI C)中,作用域规则不允许这种方法,@ KerrekSB
wirrbel 2013年

7
@wirrbel:您指的是ANSI C,1989年?就像他们有蒸汽机和打孔卡一样?可能是这样,但这是否相关?
Kerrek SB 2013年

3
该警告不是因为一段时间内分配有什么问题,而是因为在您真正打算进行比较时进行分配是一个常见的错误。如果要摆脱警告,可以执行与@Jonathan Leffler建议的等效的JS。就我个人而言,我可能还是会这样做,因为我对JavaScript真实规则不信任有多大:)也应该在某些语言(如C#和Java)中指出这是唯一的方法。
2015年

32

我发现它在经常​​涉及错误检测等操作的一系列动作中最有用。

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()


是的,我实际上只是这样做,以避免过多的嵌套,然后在网上搜索以查看是否是常见做法。最后我决定反对,因为我实际上只有另一个案例,但这似乎是将任务置于条件中的合理理由。
易卜拉欣

同事们讨厌它!对我而言,至少在大多数情况下,它比一棵大树或许多回报更为可维护。我更喜欢函数的单返回...
Nathan

一个合理的例子是,伴生块使模式变得明显,不言自明。但是,块内(“伪”)代码看起来有些丑陋,而反向样式(将您真正想做的事情放在最后)并不是那么好。

31

如果要调用一个函数,它会更有用:

if (n = foo())
{
    /* foo returned a non-zero value, do something with the return value */
} else {
    /* foo returned zero, do something else */
}

当然,您可以将n = foo();放进去。在一个单独的语句上,然后是(n),但我认为以上是一个易于理解的成语。


3
我认为您应该将其括在圆括号中,否则gcc会抱怨:警告:建议在用作真值的赋值周围加上括号[-Wparentheses]像这样:if((n = foo())){//对返回值进行处理} else {// foo返回零/ NULL,执行其他操作}
Fabio Pozzi 2015年

23

如果您要调用一个函数以返回要处理的数据或一个标志以指示错误(或已完成),则该功能很有用。

就像是:

while ((c = getchar()) != EOF) {
    // process the character
}

// end of file reached...

就个人而言,这是我不太喜欢的习惯用法,但有时替代方法比较丑陋。


13

如果您无意间尝试将分配用作真值,则GCC可以帮助您检测(使用-Wall)

if ((n = foo())) {
   ...
}

即,使用额外的括号表示这确实是您想要的。


1
很高兴知道。希望您的同事以同样的方式阅读。

1
我?天哪,我永远不会尝试将赋值用作真实值。我指的是海湾合作委员会的建议。
JesperE

GCC还可以编译Fortran。也许对编程语言很明确?
彼得·莫滕森

9

当您编写while循环而不是if语句时,该惯用法更为有用。对于if声明,您可以按照描述将其分解。但是如果没有这种构造,您要么不得不重复自己:

c = getchar();
while (c != EOF) {
    // ...
    c = getchar();
}

或使用半个循环的结构:

while (true) {
    c = getchar();
    if (c == EOF) break;
    // ...
}

我通常更喜欢半循环形式。


1
若要while循环播放,请使用:do { c = getchar(); ... } while (c != EOF);
Scott S

for循环可以用来代替:for (char c = getchar(); c != EOF; c= getchar()) { /* do something with c */ }-这是最简洁的,并且for总是“循环”对我说,曲风。小缺点是您必须指定返回c两次的函数。
Scott S

4

简短的答案是,面向表达式的编程语言允许使用更简洁的代码。它们不会强迫您将命令与查询分开


1
在if(),while()和其他类似语句中不进行赋值就强制执行编码是一个不断的斗争。C / C ++因其副作用而臭名昭著,这种副作用经常被发现是bug,尤其是当我们添加++和-运算符时。
亚历克西斯·威尔克

1
第一个链接没有用:C或C ++似乎不是这种语言。

3

例如,在PHP中,它对于遍历SQL数据库结果很有用:

while ($row = mysql_fetch_assoc($result)) {
    // Display row
}

这看起来比:

$row = mysql_fetch_assoc($result);
while ($row) {
    // Display row
    $row = mysql_fetch_assoc($result);
}

4
这也具有$ row的副作用,即$ row不能超出循环范围,因此可以更快地进行垃圾回收(无论如何使用GC的语言)。
达伦·格雷夫斯

2

另一个优点来自使用gdb的过程。在下面的代码中,如果我们要单步执行,则错误代码未知。

while (checkstatus() != -1) {
    // process
}

而是

while (true) {
    int error = checkstatus();
    if (error != -1)
        // process
    else
        //fail
}

现在,在单个步骤中,我们可以知道checkstatus()的返回错误代码是什么。


1
您的意思是调试代码时对临时更改会有所帮助吗?

0

我发现它对于返回可选值的函数(boost::optionalstd::optional在C ++ 17中)非常有用:

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与初始值设定项一起使用:)
cigien

-2

原因是:

  1. 性能提升(有时)

  2. 更少的代码(总是)

举一个例子:有一个方法someMethod(),在某种if情况下您要检查该方法的返回值是否为null。如果没有,您将再次使用返回值。

If(null != someMethod()){
    String s = someMethod();
    ......
    //Use s
}

由于您两次调用相同的方法,因此会降低性能。而是使用:

String s;
If(null != (s = someMethod())) {
    ......
    //Use s
}

这是什么语言?它不会在C#或C ++(“ If”的大写)中进行编译。
彼得·莫滕森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.