在使用整数(例如在For循环中)时是否应避免<=和> =?[关闭]


15

我已经向我的学生解释说,等于测试对于浮点变量并不可靠,但对于整数则可以。我使用的教科书说>和<比> =和<=更容易阅读。我在某种程度上同意,但是在For循环中?让循环指定开始和结束值是否更清晰?

我是否缺少教科书作者正确的东西?

另一个示例在范围测试中,例如:

如果分数> 89等级='A'
否则,如果分数> 79等级='B'​​...

为什么不直接说:如果分数> = 90?

loops 

11
不幸的是,由于这些选项之间在行为上没有客观差异,所以这构成了对人们认为更直观的民意调查,并且民意调查不适用于此类StackExchange网站。
Ixrec

1
没关系 真。
Auberon

4
实际上,这在客观上是可以回答的。待命...
罗伯特·哈维

9
@Ixrec我总是觉得有趣的是,“最佳实践”不适合作为主题。谁不想改进或使其代码更具可读性?如果人们不同意,我们都会从这个问题中学到更多,甚至可能……改变我们的思想!gh,很难说。罗伯特·哈维(Robert Harvey)说,他可以客观地回答。这将很有趣。

1
@nocomprende通常是因为“最佳实践”是一个非常模糊和过度使用的术语,可以根据有关该语言的客观事实引用有用的建议,但也经常引用“最受欢迎的观点”(或使用该术语的人的观点)实际上,所有选项都同样有效和无效。在这种情况下,您只能像罗伯特所做的那样,通过将答案限制在某些类型的循环中来进行客观论证,就像您在注释中指出自己无法完全回答问题一样。
Ixrec

Answers:


36

在具有从零开始的数组的大括号编程语言中,习惯上这样编写for循环:

for (int i = 0; i < array.Length, i++) { }

这遍历数组中的所有元素,是迄今为止最常见的情况。避免使用<=>=

只有当您需要跳过第一个或最后一个元素,或以相反的方向遍历或从另一个起点或另一个终点遍历时,才需要更改此参数。

对于集合,使用支持迭代器的语言,更常见的情况是:

foreach (var item in list) { }

这完全避免了比较。

如果您在寻找何时使用<=vs 的硬性规定<,那没有一个。使用最能表达您意图的内容。如果您的代码需要表达“每小时小于或等于55英里”的概念,则需要说<=,而不是<

回答有关成绩范围的问题>= 90更有意义,因为90是实际边界值,而不是89。


9
它并不能完全避免进行比较,它只是通过将其移入枚举器实现而将其扫地出门。:P
梅森惠勒

2
当然,但这不再遍历数组,不是吗?
罗伯特·哈维

4
因为数组是这种for循环的最常见用例。for我在这里提供的循环形式将立即被具有少量经验的任何开发人员识别。如果您想根据更具体的情况提供更具体的答案,则需要将其包括在问题中。
罗伯特·哈维

3
“使用最能表达您意图的东西” <-这!
贾斯珀(Jasper N. Brouwer)

3
@ JasperN.Brouwer就像编程的第零定律。当所有样式规则和编码约定的要求与代码的清晰度相冲突时,它们将陷入最深的耻辱。
Iwillnotexist Idonotexist

16

没关系

但是为了论证,让我们分析两个选项:a > bvs a >= b

不挂断!这些不相等!
OK,那么a >= b -1VS a > ba > bVS a >= b +1

嗯,a >ba >= b两个看起来比a >= b - 1a >= b +1。这些到底是什么1?因此,我认为必须通过添加或减去随机数s 来消除从具有>而不是相反产生的任何好处。>=1

但是,如果是数字怎么办?是说a > 7还是更好a >= 6?等一等。我们是否在认真争论使用>vs >=和忽略硬编码变量是否更好?所以,它真的就变成了是否一个问题a > DAYS_OF_WEEK是比a >= DAYS_OF_WEEK_MINUS_ONE...还是a > NUMBER_OF_LEGS_IN_INSECT_PLUS_ONEVS a >= NUMBER_OF_LEGS_IN_INSECT?我们回到加/减法1,只是这次是变量名。或者,也许是在辩论是否最好使用阈值,限制,最大值。

似乎没有一般规则:这取决于所比较的内容

但是,实际上,代码中还有许多重要的事情需要改进,还有客观和合理的指导原则(例如,每行X字符限制),但仍有例外。


1
好的,没有一般规则。(想知道为什么这本教科书似乎规定了一般规则,但我可以放任不管。)关于这是我错过的备忘录,没有新的共识。

2
@nocomprende,因为编码和格式设置标准纯属主观且极具争议。在大多数情况下,它们都不重要,但是程序员仍然对它们进行神圣的战争。(仅在草率的代码可能导致错误的情况下才重要)

1
我已经看到了很多关于编码标准的疯狂讨论,但是这可能只是小菜一碟。真傻。
JimmyJames

@JimmyJames这是关于讨论>VS >=或有关的讨论是否讨论>VS >=是有意义的?尽管最好避免讨论此问题:p
Thanos Tintinidis

2
“程序只能被人类读取,并且只能由计算机执行”-唐纳德·努斯(Donald Knuth)。使用最容易阅读,理解和维护的样式。
simpleuser

7

在计算上有使用时的成本没有区别<>比较<=>=。计算速度一样快。

但是,大多数for循环将从0开始计数(因为许多语言对其数组使用0索引)。因此,这些语言中的规范for循环是

for(int i = 0; i < length; i++){
   array[i] = //...
   //...
}

这样做<=需要您在某处添加-1以避免偏离一个错误

for(int i = 1; i <= length; i++){
   array[i-1] = //...
   //...
}

要么

for(int i = 0; i <= length-1; i++){
   array[i] = //...
   //...
}

当然,如果语言使用基于1的索引,则可以使用<=作为限制条件。

关键是条件中表示的值是问题描述中的值。阅读起来更干净

if(x >= 10 && x < 20){

} else if(x >= 20 && x < 30){

}

半开间隔

if(x >= 10 && x <= 19){

} else if(x >= 20 && x <= 29){

}

并且必须做数学才能知道19到20之间没有可能的值


我看到了“长度”的概念,但是如果您只是使用来自某处的值,并且该值应从起始值迭代到结束值,该怎么办。一个典型的例子是Markup百分比从5到10。说起来不是很清楚:for(markup = 5; markup <= 10; markup ++)...吗?我为什么要更改为测试:标记<11

6
@nocomprende不会,如果问题描述中的边界值是5和10,那么最好在代码中明确地指定它们,而不是稍微改变一下值。
棘轮怪胎

@nocomprende当上限为参数时,正确的格式更加明显 for(markup = 5; markup <= MAX_MARKUP; ++markup)。其他任何事情都会使它复杂化。
纳文

1

我要说的不是要使用>还是> =。关键是要使用允许您编写表达性代码的任何内容。

如果发现需要加/减一个,请考虑使用另一个运算符。我发现当您以自己的域名的良好模型开始时,好事就会发生。然后,逻辑将自己写入。

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour > speedLimit;
}

这比

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour >= (speedLimit + 1);
}

在其他情况下,最好采用其他方式:

bool CanAfford(decimal price, decimal balance)
{
    return balance >= price;
}

比好多了

bool CanAfford(decimal price, decimal balance)
{
    const decimal epsilon = 0e-10m;
    return balance > (price - epsilon);
}

请原谅“原始痴迷”。显然,您想分别在此处使用Velocity和Money类型,但是为了简洁起见,我省略了它们。关键是:使用更简洁的版本,使您可以专注于要解决的业务问题。


2
限速示例是一个糟糕的示例。在90kph的区域中行驶90kph不被认为是超速行驶。限速包括在内。
eidsonator

你是绝对正确的,我这是放屁的。随时对其进行编辑以使用更好的示例,希望这一点不会完全消失。
2016年

0

正如您在问题中指出的那样,测试浮点变量的相等性是不可靠的。

这同样适用于真正的<=>=

但是,整数类型不存在这种可靠性问题。在我看来,作者对哪种方法更具可读性表示了自己的观点。

您是否同意他当然是您的意见。


这是其他作者和该领域的白发(实际上我是白发)持有的普遍看法吗?如果仅仅是“一个记者的意见”,我可以告诉学生。我有,实际上。以前从未听说过这个想法。

作者的性别无关紧要,但无论如何我都更新了答案。对于要解决的特定问题,我更喜欢选择<<=基于最自然的选择。正如其他人指出的那样,在FOR循环<中使用C类型语言更有意义。还有其他用例<=。随时随地使用所有可用的工具。
Dan Pichelman

不同意。有可能是可靠性问题与整数类型:在C,考虑:for (unsigned int i = n; i >= 0; i--)或者for (unsigned int i = x; i <= y; i++),如果y恰好是UINT_MAX。糟糕,这些循环永远存在。
jamesdlin

-1

每个关系<<=>=> ==!=有自己的使用情况来比较两个浮点值。每种都有特定的含义,应选择适当的含义。

我将为您提供一些示例,在这些示例中,您需要为每个示例精确地使用此运算符。(但是要注意NaN。)

  • 您有一个昂贵的纯函数f,该函数将浮点值作为输入。为了加快计算速度,您决定添加一个最新计算值的缓存,即,一个映射x到的查找表f(x)。您将真正想要==比较参数。
  • 您想知道是否可以有意义地除以某个数字x?您可能要使用x != 0.0
  • 您想知道一个值x是否在单位间隔内?(x >= 0.0) && (x < 1.0)是正确的条件。
  • 您已经计算出d矩阵的行列式,并想知道它是否为正定的?除了没理由使用其他任何东西d > 0.0
  • 您想知道是否Σ ñ = 1,...,∞ ñ 发散?我会测试alpha <= 1.0

浮点数学运算(通常)并不精确。但这并不意味着您应该担心它,将其视为魔术,并且肯定不会总是将两个浮点数相等(如果它们在内)1.0E-10。这样做确实会破坏您的数学,并导致所有奇怪的事情发生。

  • 例如,如果您对上述缓存使用模糊比较,则会引入搞笑错误。但更糟糕的是,该函数将不再是纯函数,并且错误取决于先前计算的结果!
  • 是的,即使x != 0.0y是一个有限的浮点值,也y / x不必是有限的。但是,知道是否y / x由于溢出或操作在数学上没有明确定义而引起的不确定性可能是有意义的。
  • 如果您有一个函数,该函数的前提是输入参数x必须在单位间隔[0,1)中,那么当使用x == 0.0或调用它引发断言失败时,我真的会很不高兴x == 1.0 - 1.0E-14
  • 您计算出的行列式可能不准确。但是,如果您要假装当计算的行列式为时矩阵不是正定的1.0E-30,那么什么也得不到。您所做的只是增加了给出错误答案的可能性。
  • 是的,您的参数alpha可能会受到舍入误差的影响,因此alpha <= 1.0即使表达式alpha所计算的真实数学值可能确实大于1,它也可能是正确的。但是,此时您无能为力。

与软件工程中一样,在适当的级别处理错误,并且仅处理一次。如果在1.0E-10每次比较浮点数时按四舍五入的顺序添加舍入误差(这似乎是大多数人所使用的魔术值,我不知道为什么),则很快就会出现以下误差:1.0E+10


1
尽管是公认的,但最出色的答案仅适用于与提出的问题不同的问题。
Dewi Morgan's

-2

循环中使用的条件类型可能会限制编译器执行的优化种类,无论是好是坏。例如,给定:

uint16_t n = ...;
for (uint16_t i=1; i<=n; i++)
  ...  [loop doesn't modify i]

编译器可以假设上述条件应导致循环在第n个传递循环之后退出,除非 n可能达到65535,并且循环可能以某种方式退出,而不是i超过n。如果满足这些条件,则编译器必须生成代码,该代码将导致循环运行,直到上述条件以外的其他原因导致循环退出。

如果循环改为:

uint16_t n = ...;
for (uint16_t ctr=0; ctr<n; ctr++)
{
  uint16_t i = ctr+1;
  ... [loop doesn't modify ctr]
}

那么编译器可以放心地认为循环永远不需要执行n次以上,因此可以生成更有效的代码。

请注意,任何带符号类型的溢出都可能带来严重后果。鉴于:

int total=0;
int start,lim,mult; // Initialize values somehow...
for (int i=start; i<=lim; i++)
  total+=i*mult;

编译器可能会将其重写为:

int total=0;
int start,lim,mult; // Initialize values somehow...
int loop_top = lim*mult;
for (int i=start; i<=loop_top; i+=mult)
  total+=i;

如果在计算中不发生溢出,则这种循环的行为将与原始循环相同,但即使在整数溢出通常具有一致的包装语义的硬件平台上,也可以永久运行。


是的,我认为这在教科书中还有一段距离。还没有考虑。理解编译器优化很有用,并且必须避免溢出,但这并不是我提出这个问题的动机,它更多地是关于人们如何理解代码。读取x> 5或x> = 6更容易吗?好吧,这取决于...

除非您是为Arduino写的,否则int的最大值将比65535高一点。–
Corey

1
@Corey:在存在该类型的任何平台上,uint16_t的最大值将为65535;我在第一个示例中使用了uint16_t,因为我希望比uint32_t知道更多的人知道uint16_t的确切最大值。在两种情况下都适用相同的观点。第二个示例与“ int”的类型无关,它代表了许多人对于现代编译器无法识别的关键行为点。
超级猫
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.