嵌套FOR循环:可读性&性能

时间:2016-07-11 10:11:29

标签: c# performance loops for-loop

我理解嵌套的FOR循环。我理解他们做了什么,以及他们是如何做到的。但我的问题是他们看起来非常难以理解。

举个例子:

for (int i = 0, y = 0; y <= ySize; y++) {
    for (int x = 0; x <= xSize; x++, i++) {
        vertices[i] = new Vector3(x, y);
    }
}

现在,这个循环非常简单。它只是一个x / y“二维”循环。但是当我在这个嵌套循环中添加越来越多的“维度”时,有一种方法可以使代码不是嵌套内巢的可怕混乱和愚蠢的回溯计数器变量(i,x,y,z,等)吗

此外,额外的嵌套是否会以线性方式影响性能,或者当你嵌套更多的东西时,额外的FOR会使事情变得越来越低效吗?

6 个答案:

答案 0 :(得分:23)

我认为你在这里遇到的问题不是嵌套的for循环,而是在循环中更多地使用变量。

开口大括号之前的换行符也有助于提高可读性(尽管这是subjective)。

相反如何:

int i = 0;

for (int y = 0; y <= ySize; y++)
{
    for (int x = 0; x <= xSize; x++)
    {
        vertices[i++] = new Vector3(x, y);
    }
}

此方法对于其他维度也应保持相对可读(在此示例中,我已将i的递增移至其自己的行,如所示USR )。

int i = 0;

for (int y = 0; y <= ySize; y++)
{
    for (int x = 0; x <= xSize; x++)
    {
        for (int a = 0; a <= aSize; a++)
        {
            for (int b = 0; b <= bSize; b++)
            {
                vertices[i] = new Vector3(x, y, a, b);

                i++;
            }
        }
    }
}

关于性能,我建议重点关注确保代码首先是人类可读和可理解的,然后测量运行时性能,可能使用RedGate ANTS等工具

答案 1 :(得分:12)

通常的解决方案是重构为包含一个或两个for循环的方法,并保持重构直到每个方法都清晰且不太大。

停止缩进并将循环结果数据与应用逻辑分开的另一种解决方案是使用Linq。

int i = 0;
var coordinates = from y in Enumerable.Range(0, ySize + 1)
                  from x in Enumerable.Range(0, xSize + 1)
                  select new { x, y, i = i++ };

foreach (var coordinate in coordinates) {
    vertices[coordinate.i] = new Vector3(coordinate.x, coordinate.y);
}

仅当已声明vertices数组时才会这样。如果您可以创建一个新数组,那么您可以这样做:

var vertices = (from y in Enumerable.Range(0, ySize + 1)
                from x in Enumerable.Range(0, xSize + 1)
                select new Vector3(coordinate.x, coordinate.y)
               ).ToArray();

答案 2 :(得分:9)

# This is a helper function for the logic
# a and b will be tested; retA, retB, NA or '..' (see below) will be returned
logicalTest <- function(a, b, retA, retB){ 

  # coerce factors into character
  if(is.factor(retA)) retA <- as.character(retA)
  if(is.factor(retB)) retB <- as.character(retB)

  tmp <- a == b                        # compare a and b (surrogates for SNP.x and SNP.y) and put in tmp variable

  if(is.na(tmp)){                      # if the comparison was NA one of a or b must have been NA ...
    if(is.na(a) & is.na(b)) return(NA)  # if both were NA just return NA,
    else if(is.na(a)) return(retB)      # if a was NA return b,
    else return(retA)                   # otherwise return a
  } else if(tmp){                      # if tmp is TRUE (a == b)
    return(retA)                        # return a
  } else return("..")                  # else (a != b) return ".."
}

# load dplyr for the bit below
library(dplyr)

result <- df %>% 
  group_by(Name) %>% 
  transmute(SNP = logicalTest(SNP.x, SNP.y, SNP.x, SNP.y),
            ILMN.Strand = logicalTest(SNP.x, SNP.y, ILMN.Strand.x, ILMN.Strand.y),
            Customer.Strand = logicalTest(SNP.x, SNP.y, Customer.Strand.x, Customer.Strand.y))

# get cleaned results
result[!rowSums(is.na(result)),] # drop rows with NAs
result[!(rowSums(is.na(result)) | result$SNP == ".."),] # drop rows with NAs and ".."

循环过度使用。大多数循环可以表示为查询。这使得它们更易于编写和维护,并且使它们成为表达式而不是易于移动的语句。

表现更差,比如3-10x。这对您的具体情况是否重要取决于此处花费的时间以及您的绩效目标。

答案 3 :(得分:5)

a)通常你会发现你不需要非常深的嵌套,所以这不会是一个问题。

b)您可以将嵌套循环变为单独的方法。 (即如果您在d c ba嵌套a,您可以创建一个接受b和{{1}的方法}}作为参数并执行cd。您甚至可以让VS为您执行此操作,方法是选择c循环并单击编辑 - >重构 - >提取方法。)

至于性能 - 显然更多的嵌套意味着更多的迭代,但如果你有它们 - 你需要它们。只是更改一个嵌套循环以包含在原始循环中(并计算你在“实际”代码中的位置),恕我直言,通常没有任何明显的帮助。

答案 4 :(得分:1)

N-deep循环嵌套可能比C#中可表达的任何替代方案更强可读。相反,考虑使用具有矢量算术作为基元的更高级语言:例如,在NumPy中,代码的直接等价物是

xSize = 10
ySize = 20
vertices = np.meshgrid(
    np.linspace(0, xSize, xSize),
    np.linspace(0, ySize, ySize))

和N维泛化是

sizes = [10, 20, 30, 40, 10] # as many array entries as you like
vertices = np.meshgrid(*(
    np.linspace(0, kSize, kSize)
    for kSize in sizes))

答案 5 :(得分:0)

Just messing around here, but how about padding out spaces so that the loop conditions line up? Here is @Richard Everett's code reformatted a bit:

int i = 0;
for             (int y = 0; y <= ySize; y++) {
    for         (int x = 0; x <= xSize; x++) {
        for     (int a = 0; a <= aSize; a++) {
            for (int b = 0; b <= bSize; b++) {
                vertices[i++] = new Vector3(x, y, a, b);
            }
        }
    }
}