【问题标题】:Why would you use an assignment in a condition?为什么要在条件中使用赋值?
【发布时间】:2010-09-14 04:34:03
【问题描述】:

在许多语言中,赋值在条件下是合法的。我一直不明白这背后的原因。为什么要写:

if (var1 = var2) {
  ...
}

代替:

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

【问题讨论】:

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

标签: conditional-statements language-agnostic variable-assignment


【解决方案1】:

我今天在用 Arduino(C 语言)编程时使用它。

案例

我有一个发射器和一个接收器。 发送器想要发送数据,直到它被接收到。我想在进程完成后设置一个标志。

while (!(newtork_joined = transmitter.send(data))) {
Serial.println("Not Joined");
}

这里:

  • "transmitter.send" - 传输成功时返回真
  • “newtork_joined” - 是我成功时设置的标志

结果

  1. 传输不成功时,不设置标志,while循环为真,一直执行

  2. 成功时,设置标志,while循环为假,我们退出

不漂亮吗?

【讨论】:

    【解决方案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”的大写)。
    【解决方案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...
    

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

    【讨论】:

      猜你喜欢
      • 2013-01-30
      • 2011-02-17
      • 2017-03-11
      • 1970-01-01
      • 2019-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-15
      相关资源
      最近更新 更多