在C / C#/ C ++中做反向循环的最好方法是什么?

我需要通过一个数组向后移动,所以我有这样的代码:

for (int i = myArray.Length - 1; i >= 0; i--) { // Do something myArray[i] = 42; } 

有没有更好的方法来做到这一点?

更新:我希望也许C#有这样的内置机制:

 foreachbackwards (int i in myArray) { // so easy } 

更新2:有更好的方法。 符文拿奖:

 for (int i = myArray.Length; i-- > 0; ) { //do something } //or for (int i = myArray.Length; i --> 0; ) { // do something } 

这在常规C中看起来更好(感谢Twotymz):

 for (int i = lengthOfArray; i--; ) { //do something } 

虽然有点晦涩难懂,但我可以说,这样做的最令人愉快的方式是

 for (int i = myArray.Length; i --> 0; ) { //do something } 

在C ++中,基本上可以在使用迭代器或索引进行迭代之间进行select。 取决于你是否有一个普通的数组,或者是一个std::vector ,你可以使用不同的技术。

使用std :: vector

使用迭代器

C ++允许你使用std::reverse_iterator:来做到这一点std::reverse_iterator:

 for(std::vector<T>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) { /* std::cout << *it; ... */ } 

使用索引

std::vector<T>::size返回的无符号整型不一定std::size_t 。 它可以更大或更小。 这对循环工作至关重要。

 for(std::vector<int>::size_type i = someVector.size() - 1; i != (std::vector<int>::size_type) -1; i--) { /* std::cout << someVector[i]; ... */ } 

它是有效的,因为无符号整型值是通过对它们的位数进行模数来定义的。 因此,如果你正在设置-N ,你最终会在(2 ^ BIT_SIZE) -N

使用数组

使用迭代器

我们正在使用std::reverse_iterator来进行迭代。

 for(std::reverse_iterator<element_type*> it(a + sizeof a / sizeof *a), itb(a); it != itb; ++it) { /* std::cout << *it; .... */ } 

使用索引

我们可以在这里安全地使用std::size_t ,与上面相反,因为sizeof总是按照定义返回std::size_t

 for(std::size_t i = (sizeof a / sizeof *a) - 1; i != (std::size_t) -1; i--) { /* std::cout << a[i]; ... */ } 

避免sizeof适用于指针的陷阱

其实上述确定数组大小的方式很糟糕。 如果a实际上是一个指针而不是一个数组(这经常发生,初学者会混淆它),它会默默地失败。 一个更好的方法是使用下面的代码,如果给出一个指针,它将在编译时失败:

 template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N]; 

它通过首先获取传递数组的大小,然后声明返回对相同大小的chartypes数组的引用。 char被定义为sizeof为1.所以返回的数组的sizeof为:N * 1,这是我们正在寻找的,只有编译时间评估和零运行时开销。

而不是做

 (sizeof a / sizeof *a) 

改变你的代码,现在就可以了

 (sizeof array_size(a)) 

在C#中 ,使用Visual Studio 2005或更高版本, input'forr'并点击[TAB] [TAB] 。 这将扩展到通过集合倒退的for循环。

弄错了(至less对我来说)是如此的容易,我认为把这个片段放在这里是一个好主意。

这就是说,我喜欢Array.Reverse() / Enumerable.Reverse() ,然后更好地迭代转发 – 他们更清楚地表明意图。

总是喜欢清晰的代码,反对“ typographically愉快的 ”代码。 因此,我会一直使用:

 for (int i = myArray.Length - 1; i >= 0; i--) { // Do something ... } 

您可以将其视为向后循环的标准方式。
只是我的两分钱…

C#中使用Linq

 foreach(var item in myArray.Reverse()) { // do something } 

对于长度为有符号整数types的数组来说,这绝对是最好的方法。 对于长度为无符号整数types的数组(例如C ++中的std::vector ),则需要稍微修改结束条件:

 for(size_t i = myArray.size() - 1; i != (size_t)-1; i--) // blah 

如果你只是说i >= 0 ,对于一个无符号的整数来说,总是如此,所以循环将是一个无限循环。

在我看来很好。 如果索引器是未签名的(uint等),则可能必须考虑到这一点。 叫我懒,但在(无符号)的情况下,我可能只是使用反variables:

 uint pos = arr.Length; for(uint i = 0; i < arr.Length ; i++) { arr[--pos] = 42; } 

(实际上,即使在这里,你也需要小心诸如arr.Length = uint.MaxValue这样的情况……也许某个地方……当然,那是不太可能的情况!)

在CI中喜欢这样做:

int i = myArray.Length; while (i--) { myArray[i] = 42; }
int i = myArray.Length; while (i--) { myArray[i] = 42; } 

MusiGenesis添加的C#示例:

 {int i = myArray.Length; while (i-- > 0) { myArray[i] = 42; }} 

在C ++中做这件事最好的方法是使用迭代器(或更好的范围)适配器,它会在序列被遍历时懒散地转换序列。

基本上,

 vector<value_type> range; foreach(value_type v, range | reversed) cout << v; 

以相反的顺序显示范围“范围”(这里是空的,但我确定你可以自己添加元素)。 当然,简单地迭代范围并没有多大用处,但是将新的范围传递给algorithm和其他东西是非常酷的。

这种机制也可以用于更强大的用途:

 range | transformed(f) | filtered(p) | reversed 

将懒惰地计算范围“范围”,其中函数“f”适用于所有元素,“p”不是真的元素将被删除,最后得到的范围是相反的。

pipe道语法是最可读的IMO,因为它是中缀。 Boost.Range库更新等待审查实现这一点,但它也很简单,自己做。 使用lambda DSEL来生成函数f和谓词p就更加酷了。

 // this is how I always do it for (i = n; --i >= 0;){ ... } 

我会在原来的问题中使用代码,但如果你真的想使用foreach,并在C#中有一个整数索引:

 foreach (int i in Enumerable.Range(0, myArray.Length).Reverse()) { myArray[i] = 42; } 

我不清楚为什么有更好的select,如果善良包括清晰度或可维护性。

我更喜欢一个while循环。 对于我来说,比在for循环中递减i更清楚

 int i = arrayLength; while(i) { i--; //do something with array[i] } 

我要在这里回答我自己的问题,但是我不太喜欢这个:

 for (int i = 0; i < myArray.Length; i++) { int iBackwards = myArray.Length - 1 - i; // ugh myArray[iBackwards] = 666; } 

注:这篇文章结束了更详细,因此脱离主题,我很抱歉。

这是说我的同行阅读,并相信这是有价值的“某处”。 这个线程不是这个地方。 我会感谢您的反馈意见,这应该去(我是新的网站)。


无论如何,这是在.NET 3.5中的C#版本,它是惊人的,因为它使用定义的语义在任何集合types。 在大多数常见的开发环境中,这是一个默认度量(重用),而不是性能或CPU周期最小化,尽pipe这似乎不是真实世界中发生的事情(过早优化)。

***扩展方法处理任何集合types,并采取行动委托期待一个单一的types值,所有的执行每个项目反向**

要求3.5:

 public static void PerformOverReversed<T>(this IEnumerable<T> sequenceToReverse, Action<T> doForEachReversed) { foreach (var contextItem in sequenceToReverse.Reverse()) doForEachReversed(contextItem); } 

较旧的.NET版本,或者你想更好地理解Linq内部? 阅读..或不..

假设:在.NETtypes的系统中,数组typesinheritance自IEnumerable接口(而不是genericsIEnumerable,仅IEnumerable)。

这就是您需要从头到尾迭代的所有操作,但是您想要朝相反的方向移动。 由于IEnumerable在“object”types的Array上工作,任何types都是有效的,

关键的测量:我们假设你是否可以按相反顺序处理任何“更好”的序列,那么只能用整数来完成。

.NET CLR 2.0-3.0的解决scheme:

说明:我们将接受任何IEnumerable实现实例,其任务包含相同的types。 所以,如果我们收到一个数组,整个数组包含Xtypes的实例。如果任何其他实例是一个types!= X,则抛出一个exception:

单身服务:

公共类ReverserService {私人ReverserService(){}

  /// <summary> /// Most importantly uses yield command for efficiency /// </summary> /// <param name="enumerableInstance"></param> /// <returns></returns> public static IEnumerable ToReveresed(IEnumerable enumerableInstance) { if (enumerableInstance == null) { throw new ArgumentNullException("enumerableInstance"); } // First we need to move forwarad and create a temp // copy of a type that allows us to move backwards // We can use ArrayList for this as the concrete // type IList reversedEnumerable = new ArrayList(); IEnumerator tempEnumerator = enumerableInstance.GetEnumerator(); while (tempEnumerator.MoveNext()) { reversedEnumerable.Add(tempEnumerator.Current); } // Now we do the standard reverse over this using yield to return // the result // NOTE: This is an immutable result by design. That is // a design goal for this simple question as well as most other set related // requirements, which is why Linq results are immutable for example // In fact this is foundational code to understand Linq for (var i = reversedEnumerable.Count - 1; i >= 0; i--) { yield return reversedEnumerable[i]; } } } public static class ExtensionMethods { public static IEnumerable ToReveresed(this IEnumerable enumerableInstance) { return ReverserService.ToReveresed(enumerableInstance); } } 

[TestFixture] public class Testing123 {

  /// <summary> /// .NET 1.1 CLR /// </summary> [Test] public void Tester_fornet_1_dot_1() { const int initialSize = 1000; // Create the baseline data int[] myArray = new int[initialSize]; for (var i = 0; i < initialSize; i++) { myArray[i] = i + 1; } IEnumerable _revered = ReverserService.ToReveresed(myArray); Assert.IsTrue(TestAndGetResult(_revered).Equals(1000)); } [Test] public void tester_why_this_is_good() { ArrayList names = new ArrayList(); names.Add("Jim"); names.Add("Bob"); names.Add("Eric"); names.Add("Sam"); IEnumerable _revered = ReverserService.ToReveresed(names); Assert.IsTrue(TestAndGetResult(_revered).Equals("Sam")); } [Test] public void tester_extension_method() { // Extension Methods No Linq (Linq does this for you as I will show) var enumerableOfInt = Enumerable.Range(1, 1000); // Use Extension Method - which simply wraps older clr code IEnumerable _revered = enumerableOfInt.ToReveresed(); Assert.IsTrue(TestAndGetResult(_revered).Equals(1000)); } [Test] public void tester_linq_3_dot_5_clr() { // Extension Methods No Linq (Linq does this for you as I will show) IEnumerable enumerableOfInt = Enumerable.Range(1, 1000); // Reverse is Linq (which is are extension methods off IEnumerable<T> // Note you must case IEnumerable (non generic) using OfType or Cast IEnumerable _revered = enumerableOfInt.Cast<int>().Reverse(); Assert.IsTrue(TestAndGetResult(_revered).Equals(1000)); } [Test] public void tester_final_and_recommended_colution() { var enumerableOfInt = Enumerable.Range(1, 1000); enumerableOfInt.PerformOverReversed(i => Debug.WriteLine(i)); } private static object TestAndGetResult(IEnumerable enumerableIn) { // IEnumerable x = ReverserService.ToReveresed(names); Assert.IsTrue(enumerableIn != null); IEnumerator _test = enumerableIn.GetEnumerator(); // Move to first Assert.IsTrue(_test.MoveNext()); return _test.Current; } }