为啥要在条件中使用赋值?

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...

就我个人而言,这是一个我不太喜欢的成语,但有时替代方案更难看。

【讨论】:

以上是关于为啥要在条件中使用赋值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥要在hibernate中使用关联?

为啥不应该在函数式编程中使用变量赋值

有人可以解释为啥条件运算符和赋值运算符一起使用时表现奇怪吗?

为啥 Java 没有条件与和条件或运算符的复合赋值版本? (&&=, ||=)

为啥 Java 没有条件与和条件或运算符的复合赋值版本? (&&=, ||=)

为啥要在 Webflux 应用程序中构建自己的错误/异常处理?