循环变量的理想变量命名约定是什么?

时间:2020-03-06 14:25:29  来源:igfitidea点击:

如果我们正在编写一个简单的小循环,应该为计数器命名吗?

提供示例循环!

解决方案

我的经验是,大多数人使用单个字母,例如:

j
k
...
或者
x
y
或者
r
c(用于行/列)
或者
w,
h(用于宽度/高度)
, 等等。

但是很久以前,我学到了一个很棒的替代方法,并且从那时起就一直使用它:双字母变量。

// recommended style              ●    // "typical" single-letter style
                                  ●
for (ii=0; ii<10; ++ii) {         &#9679;    for (i=0; i<10; ++i) {
    for (jj=0; jj<10; ++jj) {     &#9679;        for (j=0; j<10; ++j) {
        mm[ii][jj] = ii * jj;     &#9679;             m[i][j] = i * j;
    }                             &#9679;        }
}                                 &#9679;    }

万一好处不那么明显:在代码中搜索任何单个字母都会发现很多我们不想要的东西。字母" i"在代码中经常出现,而不是我们要查找的变量。

始终尝试在上下文中为变量命名有意义的名称。

如果我们不能决定,则仅使用"索引",以便其他人(也许是我们!)可以更轻松地单击它以便以后进行重构。

保罗·史蒂芬森(Paul Stephenson)看到这个答案的例子。

仅当循环计数器为索引时,我才使用单个字母。我喜欢双字母背后的想法,但是它使代码非常难以阅读。

我已经开始在PHP中使用perlisms。

如果是单数迭代,则$ _对于那些知道其用法的人来说是个好名字。

我的习惯是在靠近" r"的地方使用" t",因此很容易在后面键入" for"

如果我有一个嵌套循环,则也为j。

这种约定是如此普遍,以至于如果我们设法在代码块中遇到变量" i",我们将看不到开头,但仍然可以立即识别出它的含义。

1)对于普通的老式小循环i,j,k如果需要三个以上的嵌套循环,这意味着该算法非常具体且复杂,或者我们应该考虑重构代码。

Java示例:

for(int i = 0; i < ElementsList.size(); i++) {
  Element element = ElementsList.get(i);
  someProcessing(element);
  ....
}

2)对于新的Java循环,例如for(Element element:ElementsList),最好使用普通的有意义的名称

Java示例:

for(Element element: ElementsList) {
  someProcessing(element);
  ....
}

3)如果使用的语言是可能的,请将循环转换为使用迭代器

Java迭代器示例:单击此处

像以前的海报一样,我也使用ii,jj,..主要是因为在许多字体中,单个i看起来与1非常相似。

如果将计数器用作容器的索引,则使用ijk

如果要用于迭代一个范围(或者执行一定数量的迭代),我经常使用n。不过,如果需要嵌套,我通常会恢复为ijk

在提供" foreach"风格构造的语言中,我通常这样写:

foreach widget in widgets do
  foo(widget)
end

我认为有些人会说出" widget"的命名方式与" widgets"类似,但我觉得它可读性强。

我使用"计数器"或者"循环"作为变量名。现代的IDE通常用完成词来表示,因此较长的变量名使用起来并不那么乏味。此外,将变量命名为其功能可以使程序员清楚自己的意图,即谁打算维护代码。

我总是使用一个有意义的名称,除非它是单级循环,并且该变量除了"我经历过该循环的次数"之外没有其他含义,在这种情况下,我使用i

使用有意义的名称时:

  • 该代码对于阅读代码的同事来说更容易理解,
  • 在循环逻辑中查找错误更容易,并且
  • 文本搜索变量名以返回对相同数据进行操作的相关代码段更为可靠。

示例-发现错误

使用单个字母在此嵌套循环中查找错误可能很棘手:

int values[MAX_ROWS][MAX_COLS];

int sum_of_all_values()
{
    int i, j, total;

    total = 0;
    for (i = 0; i < MAX_COLS; i++)
        for (j = 0; j < MAX_ROWS; j++)
             total += values[i][j];
    return total;
}

而使用有意义的名称则更容易:

int values[MAX_ROWS][MAX_COLS];

int sum_of_all_values()
{
    int row_num, col_num, total;

    total = 0;
    for (row_num = 0; row_num < MAX_COLS; row_num++)
        for (col_num = 0; col_num < MAX_ROWS; col_num++)
             total += values[row_num][col_num];
    return total;
}

为什么是" row_num"? -拒绝的替代品

作为对其他答案和评论的回应,以下是一些替代使用row_num和col_num的建议,以及为什么我选择不使用它们:

  • r和c:这比i和j稍好。如果我所在组织的标准是将单字母变量设置为整数,并且始终将其用作等效描述性名称的首字母,那么我只会考虑使用它们。如果函数中有两个以" r"开头的变量,系统就会崩溃,即使其他以" r"开头的对象出现在代码中,可读性也会受到影响。
  • rr和cc:对我来说这很奇怪,但是我不习惯使用双字母循环变量样式。如果这是我组织中的标准,那么我想它会比rc稍微好一点。
  • " row"和" col":乍一看,它比" row_num"和" col_num"更简洁,并且具有描述性。但是,我希望像"行"和"列"这样的裸名词来指代结构,对象或者指向它们的指针。如果row可能意味着行结构本身或者行号,那么将会导致混乱。
  • " iRow <MAX_COLS"充其量为"该行的整数循环计数器小于最大(列数)列"。
  • 这可能是个人的事情,但我更喜欢一读。

我可以接受的" row_num"的替代方法是" row_idx":"索引"一词唯一地指数组位置,除非应用程序的领域在数据库引擎设计,金融市场或者类似领域。

上面的示例尽可能地小,因此某些人可能看不到描述性地命名变量的意义,因为他们可以一口气将整个功能掌握在脑海中。但是,在实际代码中,功能会更大,逻辑也会更复杂,因此,体面的名称对于提高可读性和避免错误更为重要。

总而言之,我对所有变量命名(不仅仅是循环)的目标是完全明确的。如果有人读取了我的代码的任何部分,并且无法立即计算出变量的含义,那么我就失败了。

如果它是一个简单的计数器,则我坚持使用" i",否则,请使用表示上下文的名称。我倾向于将可变长度保持为4. 这主要是从代码读取的角度来看,由于我们具有自动完成功能,因此写入不算在内。

我长期使用i / j / k命名方案。但是最近我已经开始采用一种更常用的命名方法。

我已经按其含义命名了所有变量,所以为什么不以相同的确定性方式命名循环变量。

根据要求提供了一些示例:

如果我们需要循环通过一个项目集合。

for (int currentItemIndex = 0; currentItemIndex < list.Length; currentItemIndex++)
{
    ...
}

但是我尽量避免使用普通的for循环,因为我倾向于想要列表中的真实项目并使用它,而不是列表中的实际位置。因此,与其以for块开头不是:

Item currentItem = list[currentItemIndex];

我尝试使用该语言的foreach构造。改变了。

for (int currentItemIndex = 0; currentItemIndex < list.Length; currentItemIndex++)
{
    Item currentItem = list[currentItemIndex];
    ...
}

进入

foreach (Item currentItem in list)
{
   ...
}

这使它更易于阅读,因为仅表达了代码的真实含义(处理列表中的项目),而不是我们想要处理项目的方式(保持当前项目的索引增加它的长度,直到达到长度)列表,因此表示项目集合的末尾)。

我唯一仍使用一个字母变量的时间是在循环槽尺寸时。但是然后我将使用x,y,有时使用z。

非迭代循环:

非嵌套循环:。 。 。索引是一个值。

. . . using i, as you would in Algebra, is the most common practise . . .
for (int i = 0; i < LOOP_LENGTH; i++) {

    // LOOP_BODY
}

嵌套循环:。 。 。区分指数有助于理解。

. . . using a descriptive suffix . . .
for (int iRow = 0; iRow < ROWS; iRow++) {

    for (int iColumn = 0; iColumn < COLUMNS; iColumn++) {

        // LOOP_BODY
    }
}

foreach循环:。 。 。一个对象需要一个名字。

. . . using a descriptive name . . .
for (Object something : somethings) {

    // LOOP_BODY
}

迭代循环:

for循环:。 。 。迭代器引用对象。既不是迭代器,也不是迭代器。索引,也不是索引。

. . . iter abreviates an Iterators purpose . . .
for (Iterator iter = collection.iterator(); iter.hasNext(); /* N/A */) {

    Object object = iter.next();

    // LOOP_BODY
}

while循环:。 。 。限制迭代器的范围。

. . . commenting on the loops purpose . . .
/* LOOP_DESCRIPTION */ {

    Iterator iter = collection.iterator();

    while (iter.hasNext()) {

        // LOOP_BODY
    }
}
This last example reads badly without comments, thereby encouraging them. 
  It's verbose perhaps, but useful in scope limiting loops in C.

在Perl中,内部循环的标准变量名称为$ _。 for,foreach和while语句默认为该变量,因此无需声明它。通常,$ _可能像中性通用代词" it"一样被读取。因此,一个相当标准的循环可能看起来像:

foreach (@item){
    $item_count{$_}++;
}

用英语将其翻译为:

For each item, increment it's item_count.

但是,更常见的是根本不使用变量。许多Perl函数和运算符默认为$ _:

for (@item){
    print;
}

用英语讲:

For [each] item, print [it].

这也是计数器的标准。 (但是,在Perl中使用计数器的频率远低于在其他语言(例如C)中使用)。因此,要打印从1到100的整数的平方:

for (1..100){
    print "$_*$_\n";
}

由于只有一个循环可以使用$ _变量,因此通常在最里面的循环中使用它。这种用法与英语通常的工作方式相符:

For each car, look at each tire and check it's pressure.

在Perl中:

foreach $car (@cars){
    for (@{$car->{tires}}){
        check_pressure($_);
    }
}

如上所述,最好在外部循环中使用更长的描述性名称,因为很难在一长段代码中记住通用循环变量名称的真正含义。

有时,使用较短的,非描述性的通用名称(例如$ i,$ j和$ k)是有意义的,而不是$ _或者描述性名称。例如,匹配已发布算法(例如叉积)中使用的变量很有用。

@JustMike。 。 。几个例子: 。 。伴随着Java。

非嵌套循环:。 。 。尽可能限制范围

/*LOOP_DESCRIPTION*/ {

    int i;

    for (i = 0; i < LOOP_LENGTH; i++) {

        // loop body
    }  
}

嵌套循环:。 。 。同上

/*LOOP_DESCRIPTION*/ {

    int row, column;

    for (row = 0; row < ROWS; row++) {

        for (column = 0; column < COLUMNS; column++) {

            // loop body
        }
    }  
}

这种布局的一个优点是,它读起来很烂,却没有注释,从而鼓励了他们。
也许这很冗长,但是就我个人而言,这就是我在C语言中执行循环的方式。
另外:启动时确实使用了" index"和" idx",但是我的同伴通常将其更改为" i"。

我使用的是i,ii,iii,iv,v ...不过,从来没有高于iii。

第一条规则是变量名称的长度应与变量的范围相匹配。第二条规则是有意义的名称会使错误更浅。第三个规则是,如果我们想在变量名中添加注释,则选择了错误的变量名。最终规则是与队友一样,只要不违反先前的规则即可。

我使用i,j,k(或者r&c进行行列循环)。如果一个方法中需要三个以上的循环变量,则该方法可能太长且太复杂,并且我们可能会受益于将方法拆分为更多方法并正确命名它们的代码。

我已经开始使用与上下文相关的循环变量名称和匈牙利语混合。

当遍历行时,我将使用iRow。当遍历列时,我将使用iCol。遍历汽车时,我将使用" iCar"。你明白了。

对于数值计算,matlab等,请勿使用i,j

这些是保留的常量,但是matlab不会抱怨。

我个人的最爱是

指数
第一秒
柜台
数数

我最喜欢的在类似矩阵的集合上循环的约定是使用x + y,因为它们用在笛卡尔坐标中:

for x in width:
    for y in height:
        do_something_interesting(x,y)

无论我们选择什么,只要代码含义相同,都在代码中一致地使用相同的索引。例如,要遍历一个数组,可以使用ijjkappa,但是在任何地方都必须以相同的方式进行操作:

for (i = 0; i < count; i++) ...

最佳做法是使循环的这一部分在代码中看起来相同(包括始终使用" count"作为限制),这样它就成为一种习惯用法,我们可以在思维上跳过以集中精力于代码,循环的主体。

同样,例如,如果我们要遍历2D像素阵列,则可以编写

for (y = 0; y < height; y++)
  for (x = 0; x < width; x++)
    ...

只需在编写这种类型的循环的每个地方都以相同的方式进行即可。

我们希望读者能够忽略无聊的设置,并在实际循环中看到自己在做什么的光彩。

与往常一样,史蒂夫·麦康奈尔(Steve McConnell)的《代码完成》在这方面也提供了一些极好的建议。相关页面(无论如何在第一版中)分别是340和341. 绝对建议有兴趣改进循环编码的任何人对此进行介绍。麦康奈尔建议使用有意义的循环计数器名称,但人们应该阅读他自己要说的内容,而不要依靠我的拙劣总结。

我通常使用:

for(lcObject = 0; lcObject < Collection.length(); lcObject++)
{
   //do stuff
}

我也使用双字母约定。 ii,jj,kk。我们可以grep那些,而不会提出一堆不需要的匹配项。

我认为即使将这些字母加倍,使用它们也是最好的方法。这是一个熟悉的约定,即使加倍。

遵守约定有很多话要说。它使事情更具可读性。

对于整数,我使用int索引,除非它是嵌套的,然后对要迭代的内容使用int groupIndex和int userIndex的Index后缀。

在Python中,如果我只是在计算时间,我会使用i,j和k。如果将迭代计数用作索引,则使用x,y和z。但是,如果实际上要生成一系列参数,则将使用一个有意义的名称。