是否从for-header中提取调用以确定数组/字符串的长度?

时间:2011-07-06 09:08:48

标签: language-agnostic for-loop complexity-theory

我最近注意到我的一位同事正在做

int len = foo.length();
for (int i = 0; i < len; ++i)
    doStuff(foo[i]);

我知道这在C中被认为是一种很好的做法,其中strlen()在O(length_of_string)中运行。但是我希望更新的语言(比如Java或Python)能够将字符串的长度与字符一起存储,从而允许length()在O(1)中运行。我经常写:

for (int i = 0; i < foo.length(); ++i)
    doStuff(foo[i]);

保存一行代码。但我的同事让我感到疑惑......这是非常好的做法,还是期望O(1)行为是不合理的?

(作为相关问题:现代编译器现在不能自动从for-header中提取strlen()调用吗?)

3 个答案:

答案 0 :(得分:1)

这些陈述实际上是两种不同的陈述。

int len = foo.length(); // Will run once
for (int i = 0; i<len;++i)

这里将检查i<len每个循环,len只是一个可以读取的变量。

for (int i = 0; i < foo.length(); ++i)

此处i < foo.length()包含函数调用,因为foo的长度可以在循环内部发生变化(例如,您可以删除foo之外的字符而不是递增i })每次迭代都会调用函数foo.length()

有些语言中foo可能是常量,foo.length()可以由编译器优化,但最好是保存而不是遗憾。

此外,某些语言可能允许这样的内容:

for (int i=0, len=foo.length();i<len;++i)

仍然可以为你节省一条线。

答案 1 :(得分:0)

首先,调用函数foo.length()在任何情况下,循环的每次迭代都需要比使用临时变量来存储调用foo.length()的结果更多的资源。

此外,在重构代码时,使用代码可能会导致错误。例如,这个循环永远不会结束:

for (int i = 0; i < foo.length(); ++i)
{
    doStuff(foo[i]);

    // few line of code, written another man
    doWork(foo); // Passing by reference
}

void doWork(Foo fooObj)
{
    // some work

    fooObj.Add(new SomeObject());
}

答案 2 :(得分:0)

这不是语言不可知的。这取决于编译器的智能程度。如果您可以明确声明长度是常量,则可以内联整个循环,因此根本不会发生任何测试。谈到Java,我敢打赌编译器可以变得非常聪明,所以你不必那么明确。你正确的java.lang.String预先计算它的长度。在复杂性方面,定义您正在计算的重要操作是切实可行的。严格来说,在图灵机上你必须是O(n)才能找到输入的结尾(“$”)。