在for循环注释中使用此额外变量有什么好处?


11

我在一个正在处理的大型项目(伪代码)中发现以下循环注释:

var someOtherArray = [];
for (var i = 0, n = array.length; i < n; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

引起我注意的是这个额外的“ n”变量。我以前从未见过这样写过的书。

显然,在这种情况下,没有理由不能以以下方式编写此代码(我已经很习惯了):

var someOtherArray = [];
for (var i = 0; i < array.length; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

但这让我思考。

编写这样的for循环是否有意义?这个想法浮现在脑海,“数组”的长度在for循环执行期间可能会改变,但是我们不想循环到比原始大小还远的地方,但是我无法想象这种情况。

在循环内缩小数组也没有太大意义,因为我们很可能会得到OutOfBoundsException。

是否有已知的设计模式在其中使用此注释?

编辑 如@ Jerry101所述,原因是性能。这是我创建的性能测试的链接:http : //jsperf.com/ninforloop。在我看来,除非您要遍历非常大的数组,否则差异并不足够大。我从中复制的代码最多只能包含20个元素,因此我认为这种情况下的可读性超过了性能方面的考虑。


对您的反应是:这是面向对象的编程。标准array.length快速且便宜。但是无论出于何种原因,.length的实现都可能会变得昂贵。如果值保持不变,则不要多次获取。
Pieter B

我同意。很好地了解此选项,但是除非我得到与您的sql查询类似的示例,否则我可能不会经常使用它。谢谢you☺
弗拉德Spreys

问题是,如果数组是一百万个项目,那么您将调用array.length一百万次,而不是1次,而值保持不变。问一百万次并且知道每个后续答案都将与第一个答案相同……对我来说听起来有点荒谬。
Pieter B

1
我发现这不会使代码更难阅读。(严重的是,如果有人在这样一个简单的循环中遇到麻烦,他们应该担心。)但是:1)如果经过4年的C语言和C语言降级之后,每个认真的编译器都没有做到这一点,我会感到非常惊讶为了你; 2)这可能不是热点。除非有人将其解析到库中(例如,数据结构的实现),否则似乎不值得花额外的5秒钟时间来解析它
Doval,2015年

1
在C#/。NET中,使用的变体n可能会比使用的变体慢,array.Length因为JITter可能不会注意到它可以消除数组边界检查。
CodesInChaos

Answers:


27

该变量n可确保生成的代码不会在每次迭代时都获取数组长度。

根据所使用的语言,该数组实际上是集合对象还是JavaScript“数组”以及其他优化细节,此优化可能会在运行时间上有所不同。


3
不,不是。每次迭代是否获取数组的长度不仅取决于语言,还取决于编译器和编译器选项以及循环中要执行的操作(我在谈论C和C ++)
BЈовић15年

..即使如此,如果我真的想要的话,我会亲自将n赋值放在循环的上方,因为-正如OP所指出的-这是一种罕见的(YMMV)构造,很容易被其他开发人员忽略/不理解代码。
cwap

20
如所写,它的作用域n是循环,这通常是一件好事。只有在以后要再次使用它时,才在循环之前定义它。
Jerry101

4
@ Jerry101:显然,示例代码段是JavaScript,其中没有诸如循环作用域之类的东西……
Bergi 2015年

1
@BЈовић:在实践中,编译器很少设法证明数组大小没有变化(通常,仅当您要遍历的向量是该函数的局部函数并且您仅在循环中调用内联函数时,这才是微不足道的证明)。
Matteo Italia 2015年

2

数组长度的获取可能很容易“昂贵”,而不是您迭代的实际操作。

因此,如果集合不变,则仅查询一次长度。

不是使用数组,而是使用来自sql服务器的记录集,通过不查询每次迭代的记录数,我已经看到了显着的改进。(当然,只有在可以保证数组或记录集在此过程中不会被更改的情况下,才这样做)。


如果记录计数发生变化,那又如何?假设有100个项目,并且在处理项目50时,插入了#25之后的项目。因此,当您处理项目#51时,它与您已经处理过的#50一样,并且出错了。因此,问题不在于长度变化,而更普遍。
gnasher729

@ gnasher729一旦您陷入非对称行为,很多事情都会出错。但是您可以针对启动和sql事务进行处理。
Pieter B

2

这个想法浮现在脑海,“数组”的长度在for循环执行期间可能会改变,但是我们不想循环到比原始大小还远的地方,但是我无法想象这种情况。

谈论性能的人可能是正确的,这就是为什么要这样写(以这种方式写的人可能不正确,因为它会显着影响性能,但这是另一回事)。

但是,为了回答问题的这一部分,我认为最有可能发生在循环的主要目的是修改其循环的数组时。用伪代码考虑这样的事情:

guests = [];
for (i = 0; i < invitations.length; ++i) {
    if (! invitations[i].is_declined()) {
        guests.append(invitations[i].recipient())
    }
}
// maybe some other stuff to modify the guestlist
for (i = 0, n = guests.length; i < n; ++i) {
    if (guests[i].has_plus_one()) {
        guests.append(guests[i].get_plus_one());
    }
}

当然,还有其他编写方法。在这种情况下,我可以在检查收件人是否接受邀请的同时检查是否有加号,并在完整的来宾列表中一次生成一个邀请。我不一定要结合这两个操作,但是我不能立即想到一个绝对错误的原因,因此需要这种设计。偶尔考虑是一种选择。

实际上,我认为这段代码最不对的地方是guests数组的成员资格意味着代码不同点的不同事物。因此,我当然不会称其为“模式”,但我不知道排除这种情况是不够的:-)


数组也可以由另一个线程修改。这样可以防止编译器优化长度检索。因此,有问题的是,程序员明确表示数组即使在其他线程中也不会改变。
Basilevs

0

如果可能的话,应该使用遍历数组所有元素的迭代器。您将数组仅用作序列而不是随机访问存储,因此很明显,仅执行顺序访问的迭代器会更快。想象一下,您认为数组实际上是一棵二叉树,但是有人编写了通过对元素进行计数来确定“长度”的代码,并通过遍历元素来访问第i个元素的代码,每个都以O(n )。迭代器可以非常快,您的循环会很流畅。

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.