【问题标题】:Is for keyword obsolete like goto in modern object-oriented languages? [closed]对于现代面向对象语言中的 goto 关键字是否已过时? [关闭]
【发布时间】:2010-07-24 03:45:21
【问题描述】:

for 关键字在 C# 或 Java 等语言中是否已过时或可能会像 goto 一样过时?再过几年,看到一个使用for的面向对象代码会不会很奇怪,像今天一样,看到gotos就很可疑?

换句话说,有什么理由知道并在程序中使用for


我注意到for 有很多在使用foreach 时不存在的弱点:

'For' 很少使用,我们也很少需要它:

不了解科学发展,但是做一般的东西,软件产品和网站,特别是不涉及微积分,for 很少使用。我见过并做过很多项目,在数千行代码中没有 for 循环。可能即使在处理微积分时,操作 arrayscollectionsmatricesranges 也更频繁、更优雅并且比循环有用。

有几个地方看似需要for,其实不然,可以改用面向集合的方案。

阅读初学者开发者的源代码,我还发现for 曾经做过.NET Framework 已经做过的事情。例如,用相同的值填充一个数组,重复N次,有些人会使用循环,而他们必须使用Enumerable.Repeat()

有时,我看到循环用于遍历数组、集合或枚举。 foreach 将是一个更优雅的解决方案,并且写起来更短。

'For' 不是Jimmy-proof:

可能我是一个非常糟糕的开发人员,但很多时候,当我使用for 时,我会一次又一次地犯同样的错误:在循环范围内放置预定义变量以外的内容,如下所示:

int SomeValue
{
    get
    {
        // Resources-expensive operation goes here.
    }
}

for (int i = 0; i < this.SomeValue; i++)
{
    // Code here.
}

// Instead, a less stupid developer would write:
int someValue = this.SomeValue;
for (int i = 0; i < someValue; i++)
{
    // ...
}

当然,这样做的问题是每次迭代都会调用this.SomeValue,浪费资源。例如,今天晚上,我发现自己犯了一个可怕的错误:循环使用了 0..N 范围,其中 N 是对数据库进行查询的属性.在 SQL Profiler 中看到 相同的查询 被重复了 10 000 次真是令人惊讶。

'For' 很丑:

也许这太主观了,但是当一直在操作集合时,第二个解决方案不是更自然地写/读吗?

// Solution 1. C style.
for (int i = 0; i < 10; i++)
{
    // Do something.
}

// Solution 2. Enumerable-oriented.
foreach (var i in Enumerable.Range(0, 10))
{
    // Do something.
}

顺便说一句,我发现第二个更容易理解。区别是:

for (i = 0; i < 10; i++)
for (i = 0; i <= 10; i++)
for (i = 1; i < 10; i++)
for (i = 1; i <= 10; i++)

容易看到吗?而Enumerable.Range() 只接受两个参数,非常明确:第一个 - 从哪里开始,以及元素的数量。

更复杂的代码呢?

foreach (string containingTwo in Enumerable.Range(0, 10).
    Where(c => c.ToString().Contains('2')).
    Select(c => c.ToString().PadLeft(8)))
{
    Console.Write(containingTwo);
}

'For' 太死板了:

很难扩展for。假设我们想在不使用 ASP.NET 分页控件的情况下显示网站上的页面列表。最明显的解决方案是使用for (int i = 1; i &lt;= countPages; i++) 显示数字。

现在,如果需求发生变化,我们不想显示每个页面,而只显示前两个、最后两个、当前页面和与当前最近的页面,该怎么办?

如果我们使用一个集合,它会很容易改变。实际上,使用循环,它不是。

'For' 对 Linq 不友好:

Linq 可以做很多事情,避免手写。但是当我们使用for 时,Linq 就没有地方了。如果有过滤要做,我们必须自己做。如果必须转换结果,则必须在单独的行上完成。是的,当然,lambda 表达式也可以在一个简单的循环中使用,但是没有一种易于阅读的方法可以在一行短代码中编写所有内容。 SumAverage 不可用,就像 JoinExceptGroupBy 一样。

【问题讨论】:

  • 你把它命名为所有通用 OO 语言然后只谈论 .NET,当然你不明白为什么还要使用for
  • 我会说goto 并没有过时,这会让人们感到不安。这是一个很少有用的工具,没错,但有时它是迄今为止最好的解决方案。这是关于可读性和可维护性 - 而不是性能。当您的逻辑流程最好使用状态图来描述时,每个转换的最简单和最清晰的表示就是 goto。问题是这个参数只有在你的代码不需要保存它的状态然后再恢复时才有效,在这种情况下循环切换习惯用法更有意义——但即使这样,一个单独的线程也可以轻松地挂起自己。
  • 另外,在您的i = someValue 上 - (1) 我假设您的意思是 i == someValue,并且 (2) 优化器通常可以为您将其从循环中拉出来,尽管不可否认它经常会赢t(例如,如果 operator== 不是内联的,则无法知道是否存在预期的副作用)。
  • @Steve314:这是一个错误:i = someValue 必须是 i &lt; someValue。在我的情况下(当数据库被查询数千次时),内联是不可能的:编译器无法预测数据库在循环内没有改变。
  • 这是强烈的观点,我建议将其设为社区 wiki。

标签: c# for-loop readability


【解决方案1】:

在我当前的 C# 项目中,有一个地方仍然使用传统的 for 循环,它处理左右移动数组以找到两条曲线之间的最佳匹配。在这种情况下,foreach 在语义上根本不足以让陌生人弄清楚发生了什么。这看起来不错:

for(int shift = -5; shift <= 5; shift++)
    // shift data and compare

是的,从技术上讲,我可以在 Range(-5,5) 上使用foreach,但在我看来,这就像完成同样事情的肮脏黑客。我不是在这些数字进行操作,而是它们是我操作的一个组成部分。我觉得 foreach 应该覆盖您希望检索/更新/生成的域对象。

我有时也会处理非常大的(数百万/数十亿个元素)double[]s。在forforeach 上运行秒表,我发现foreach 在处理此类数组时比for 慢约1.5 倍。我怀疑当 .NET 5.0 出现时,性能将非常相似,因为所有内容都将被视为某种形式的 IEnumerable。它基本上已经存在,至少在精神上。

但在大多数情况下,我同意。我认为最终,我们会看到传统的for 循环的目的是淡出foreach 风格的循环。但是,我希望随后将其缩短回for,因为它的输入速度更快。我一直不明白他们为什么不走 Java 路线,只是在 for 中允许两个不同的表达式。

【讨论】:

  • 仍然是一个问题,即它是否足够普遍,可以被证明是核心语言语法。毕竟,for 循环总是可以重写为while 循环。将步进操作移到最后可能不太理想,但这没什么大不了的,甚至可能更合适。
  • @Steve314 这绝对是核心语言语法。它被广泛使用,并且有许多鲜为人知的构造几乎没有被广泛使用,它们仍然是“核心语言语法”,例如 switch 语句或 do-while 循环。
  • @Steve314 问题不在于另一种循环样式是否可行(所有循环都可以用所有其他关键字重写),而是它表达是什么。 for 循环只是表达了与 while 不同的东西。看看我上面的班次示例;这很有意义。把它翻译成一个while循环,它对读者来说就失去了意义。可能吗?是的。它在语义上合理吗?不。创建一门语言不仅仅是为您提供基本的工作;它是关于为您提供语义上明显的关键字来表达您在特定情况下的意图。
  • 绝对是的,除了没有编程语言为每个可能的语义结构提供特定的句法结构。如果“for”在某些语言中很少使用,那么它可以相当合理地过时。否则 - 好吧,你不妨接受我对“goto”的评论,呃,呃,呃,我想是认真的。
  • @Rafe - 我的主要语言是 C++,所以我绝对不是 foreach 上瘾者。如果“switch”是一个较少使用的构造,我会感到惊讶,但对于“do ... while”,我也很乐意将其称为过时的。这不仅罕见,而且可能令人困惑。我说我们需要 Ada exit when - 只是一个 if () break 因为 (1) here-is-an-exit-point 关键字是前面的而不是隐藏的 - 简单,例如缩进与您的开始/结束或大括号相同的级别,以及(2)您可以命名您打算退出的循环,尽管我个人认为这不应该是可选的。
【解决方案2】:

在 Java(和其他一些语言)中,您仍将 for 关键字用于“for each”循环:

for (String s : myListOfString) { 
    // code code code
}

所以你可能想改写这个问题,但如果你的意思是“你认为我们会停止使用 for (int i=0; i&lt;something; i++) 循环,那么你肯定是对的。在 Python 中,相当于 for (int i=0;... 循环(for i in range(len(myList))) 不鼓励迭代集合(请参阅 delnan 的评论——说得好),或多或少是出于您在原始问题中提到的原因。

【讨论】:

  • 在 Python 中,for 总是迭代并且从不计数。但是您有内置的 range(),它可以为您提供手动计算的数字。但是,只有在您使用它们来迭代集合(可以立即迭代)时,才“非常不鼓励”它的使用。有时,计数正是您想要的 - 但不足以作为单独的语法包含在内。
  • 已编辑以考虑 delnan 的评论。
  • 我不明白为什么这是公认的答案。它根本没有触及提出的问题,除了挑剔措辞(而且“那么你可能肯定是对的”不算数,没有理由,它只是被扔在那里)。
  • @Blindy - 这个问题有很多原因,除非你不同意,否则除了“你可能绝对是对的”之外,也许没有太多可说的了。可能这个问题有点过头了,基本上是在回答(而不是仅仅解释)本身。
  • @Blindy:我接受了这个答案有几个原因。其中之一是展示如何在 Python 中完成事情(也感谢 delnan 评论)。另一个可能是表明我的问题表述不当,没有太多意义。 ;)
【解决方案3】:

虽然有些人可能不喜欢 for,但我仍然经常看到它,而且它是一种非常紧凑且(在我看来)可读的方式来生成循环。我知道在Java中,每个人都使用for循环,实际上我很少看到foreach构造。也就是说,总会有人会继续使用for 循环,也会有人讨厌这些人。

关于您的另一个问题,您绝对有必要知道如何执行for 循环(相信我,这并不难),就像您需要知道如何执行while 循环一样.有些事情foreach 能够有效地完成,所以你偶尔会使用for。至于你用不用,那是你的选择。

【讨论】:

    【解决方案4】:

    我对 for 循环的最大问题是当它们嵌套时,我不小心写了一个 i 而不是 j 或反之亦然......尤其是在 for 标头内,例如在增量或条件期间部分。幸运的是,我已经训练自己在这种情况下要格外小心:D

    虽然不是面向对象,但我认为 for 循环在函数式编程中(大部分)已经过时了。

    【讨论】:

      【解决方案5】:

      我错了还是应该

      for (int i = 0; i = someValue; i++)

      for (int i = 0; i

      也不是 for 循环通常比 foreach 循环更优化 或者 c# jit 可以优化吗

      foreach (var i in Enumerable.Range(0, 10))
      {
          // Do something.
      }
      

      转化为等效的字节码

      for (int i = 0; i < 10; i++)
      {
          // Do something.
      }
      

      在此处插入关于优化与清晰度的标准论据

      如果由我决定,我们都会使用 smalltalk 风格的循环,即在每个集合上调用匿名函数,但这可能会严重降低速度。

      【讨论】:

      • 我不懂 C#,但是如果你遍历一个 collection,“for each”循环肯定会更快,而不仅仅是从 0 计数到 n .如果最快的迭代方法是简单地索引,那么集合就会这样做。如果有更快的方法(例如链表),那么它将改为这样做。
      • 是的,你是对的,这是问题中的一个错字。使用“for each”循环可能不会发生这种情况!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多