更新循环列表索引的更优雅方法?

4

我有一个问题列表,用户将通过它们进行迭代。他们可以从任何问题开始,但这些问题是有顺序的。为了实现这个功能,我只需维护一个数组索引,并像下面这样递增它:

CurrentQuestion = (++CurrentQuestion < questions.Length) ? CurrentQuestion : 0;

这里发生的事情可能并不明显,有没有更优雅的方式来完成这个任务?


4
好的,请将那个行为抽象成一个方法,并给它一个简洁明了的名称。 - Kirk Woll
请注意,您选择的答案仍然像这个答案一样不易维护,同样优雅,但具有不同的语义。 - jcolebrand
@jcolebrand - 至少这个答案摆脱了 ++,而Lou关于它是惯用方式的说法是正确的。 - H H
@jcolebrand 我不确定我同意你关于可维护性的观点...所选答案只有一行代码,而且非常清晰明了。我不明白为什么将一行简洁的代码改成4行代码和2行注释会使代码更易于维护。在某些情况下是这样,但这行代码非常清晰,可以像原来一样轻松地维护。 - NominSim
哦,我并不反对这个观点,我只是想给未来读者指路。为了你的利益,我指出这个答案_和你现有的答案一样优雅_。请注意,我已经对这个解决方案进行了赞同和积极的评论。 - jcolebrand
2
在非平凡表达式中使用 ++ 是许多程序员警惕的信号。你需要准确理解何时应用 ++,这使得代码行更难读懂,而且可能是错误的。我的答案没有这样的问题,所以我认为它在这方面更好。 - Lou Franco
3个回答

13

我强烈反感在同一语句中对变量使用 ++,然后再次使用该变量。我认为这行代码在C#中可以工作正常,但是在C/C++中,这样的代码是未定义的,因此它引起了我的警觉。我更喜欢

CurrentQuestion = (CurrentQuestion+1) % questions.Length;

我认为这是在类C语言中进行时钟算术的惯用方式。


1
哦,那也是个不错的选择,我甚至没有考虑过取模,但你说得对。 - jcolebrand
2
你需要测试 'question.Length' 是否大于零,否则会出现异常。 - Hicham
另一个问题:如果CurrentQuestion的值大于int.MaxValue,那么CurrentQuestion将从int.MinValue继续。这意味着您将从Modulo获得负值。您可以使用Math.Abs或uint(然后它从uint.MaxValue开始)来解决此问题。 - zodo

3

这里正在发生的事情并不一定显而易见,有没有更优雅的方式来做这件事情呢?

虽然对于一些人来说可能不是很明显,但我知道那是在干什么。

然而,你需要考虑的是写易读的代码比聪明的代码更重要。代码需要被维护,而你并不比编译器更聪明。

像这样写代码,并且对其满意:

//ensure that the CurrentQuestion counter increments and loops back around after hitting "list max"
CurrentQuestion = CurrentQuestion + 1;
if (CurrentQuestion >= questions.Length) {
  CurrentQuestion = 0;
} // meta-comment: use braces incase you have to add more later

重要的是,这段代码现在可读性很高,而且仍然经过了优化。它执行正好与其他代码相同,我们可以在不大量重新阅读代码的情况下更改其中的某些部分。
还要注意我在这里使用的一些语义:
  • 即使您认为不需要,也始终使用大括号。
  • 使用CurrentQuestion = CurrentQuestion + 1;而不是CurrentQuestion += 1;CurrentQuestion++;++CurrentQuestion;,因为第一种更加明确表达了意图。始终编写有意义的代码。

请再检查一遍 - 我认为您当前的版本允许 CurrentQuestion 等于 questions.Length - default.kramer
1
我觉得使用++或+=已经非常明确意图了,你是说你从不在代码中使用它们吗?那似乎有点过分了。 - NominSim
1
尽管该回答的要点是清晰的,但样例代码的逻辑(截至目前为止...jcolebrand 似乎仍在编辑它)是不正确的。假设 CurrentQuestion 是基于零的索引,则增量操作 (CurrentQuestion = CurrentQuestion + 1) 应在循环检查之前执行。 - csd
我不使用它们仅仅是因为它们在这里具有相同的效果。我并不是说永远不要使用它们,因为当我递增循环计数器时,我会使用它们。但是当我像这样进行赋值时,我不使用它们,因为有人可能想在中途添加其他内容。在某些方面,这样呈现更好。 - jcolebrand
此外,写成 CurrentQuestion += 1; 并不是更加“清晰”,而是更加“可维护”。我发现自己混淆了重点。>.< - jcolebrand
显示剩余2条评论

1

没有必要使用条件运算符

 CurrentQuestion = ++CurrentQuestion % questions.Length;

但我猜你更喜欢哪一个,这只是风格问题,而不是其他什么。


这在大迭代中技术上会出现问题。 - NominSim
1
只有在@NominSim将要包装时才需要考虑这个问题。然而,由于这将需要在数组中添加比实际可容纳的更多元素,因此这并不是一个真正的问题。(更精确地说,这会超出最大元素数量的限制) - Rune FS
@KirkWoll 是的,抱歉我就是这个意思。 - NominSim
尽管编译器可能会将其优化掉,但这个答案可能比Lou Franco的答案慢,因为它涉及到对CurrentQuestion进行两次赋值:一次是来自增量(++CurrentQuestion),另一次是来自等号。 - csd
哎呦,把赋值目标用 ++ 了? - H H
如果我们追求速度,条件运算符比CurrentQuestion+1更快。我个人认为这种写法更易于阅读,而且由于很容易进行优化,因此速度可能不会变慢。 - Rune FS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接