ChatGPT解决这个技术问题 Extra ChatGPT

为什么要在条件中使用赋值?

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

if (var1 = var2) {
  ...
}

代替:

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

?

这个问题真的与语言无关吗?哪些语言会受此影响?我知道 C 和 C++ 是,还有什么?
除了 C 和 C++ 之外,还有哪些其他语言允许这样做(没有编译时或运行时错误)? php?珀尔?
注意:与此(无意分配)相关的常见编程错误的规范问题是{ 1}(即使是从 2013 年开始)。
正如预期的那样,还有 a question from 2008(但搜索引擎不喜欢指向它)。

P
Peter Mortensen

循环比 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,这是唯一的方法。
J
Jonathan Leffler

我发现它在通常涉及错误检测等的操作链中最有用。

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 case,但这似乎是将作业置于条件中的正当理由。
同事讨厌!对我来说,至少在大多数情况下,它比一棵巨树或许多回报更易于维护。我更喜欢从函数中单次返回...
一个合理的例子,伴随块使模式明显,不言自明。但是,块内(“伪”)代码看起来有点难看,反向风格(把你真正想做的放在最后)也不是很好。
C
Chris Young

如果您正在调用一个函数,它会更有用:

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,做其他事情 }
M
Michael Burr

如果您调用的函数返回要处理的数据或指示错误(或您已完成)的标志,这将很有用。

就像是:

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

// end of file reached...

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


J
JesperE

如果您无意中尝试使用赋值作为真值,GCC 可以帮助您检测(使用 -Wall),以防它建议您编写

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

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


很高兴知道。我希望你的同事以同样的方式阅读它。
我?天哪,我永远不会尝试将赋值用作真值。我指的是 GCC 的建议。
GCC 也可以编译 Fortran。也许对编程语言要明确?
我更喜欢显式 if ((n = foo()) != 0) 来避免很多连续的右括号,但我承认你可以通过诸如 if ((n = foo2(a, somefunc(b, sizeof(SomeType)))) != 0) 的符号来增加连续 ) 的数量
G
Greg Hewgill

当您编写 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 的函数两次。
P
Peter Mortensen

简短的回答是expression-oriented 编程语言允许更简洁的代码。他们不会强迫您separate commands from queries


在 if()、while() 和其他类似语句中强制执行没有赋值的编码是一场持久战。 C/C++ 因其副作用而臭名昭著,这些副作用通常会变成错误,尤其是当我们添加 ++ 和 -- 运算符时。
第一个链接没用:C 或 C++ 似乎不是这样的语言。
P
Paige Ruten

例如,在 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 的语言)。
P
Peter Mortensen

另一个优点是在使用 GDB 期间。

在下面的代码中,如果我们单步执行,则不知道错误代码。

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

相当

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

现在在单步执行期间,我们可以知道 checkstatus() 的返回错误代码是什么。


你的意思是它可以在调试代码时对临时更改有所帮助?
J
Julien-L

我发现它对于返回可选项(C++17 中的 boost::optionalstd::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 初始化器:)
P
Priteem

我今天在用 Arduino(C++ 语言的子集)编程时使用了它。

案子

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

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

这里:

"transmitter.send" - 传输成功时返回 true

“newtork_joined” - 是我成功时设置的标志

结果

传输不成功时,不设置标志,while循环为真,继续执行成功时,设置标志,while循环为假,我们退出

不漂亮吗?


关于“在 Arduino(C 语言”中):Arduino 的编程语言在很大程度上是 C++(不是 C)的子集——尽管它是什么并不完全清楚。例如,it has class 'string' (例如,为其定义的运算符“+=”)。这绝对不是 C。
@PeterMortensen,你是对的。让我编辑。
P
Peter Mortensen

原因是:

性能提升(有时) 更少的代码(总是)

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

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

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

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

这是什么语言?它不会在 C# 或 C++ 中编译(“If”的大写)。