在 C/C#/C++ 中执行向后循环的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/275994/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's the best way to do a backwards loop in C/C#/C++?
提问by MusiGenesis
I need to move backwards through an array, so I have code like this:
我需要在数组中向后移动,所以我有这样的代码:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something
myArray[i] = 42;
}
Is there a better way of doing this?
有没有更好的方法来做到这一点?
Update: I was hoping that maybe C# had some built-in mechanism for this like:
更新:我希望 C# 可能有一些内置机制,例如:
foreachbackwards (int i in myArray)
{
// so easy
}
Update 2: There arebetter ways. Rune takes the prize with:
更新2:有是更好的方法。符文获得奖品:
for (int i = myArray.Length; i-- > 0; )
{
//do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
// do something
}
which looks even better in regular C (thanks to Twotymz):
在常规 C 中看起来更好(感谢 Twotymz):
for (int i = lengthOfArray; i--; )
{
//do something
}
采纳答案by Johannes Schaub - litb
While admittedly a bit obscure, I would say that the most typographically pleasing way of doing this is
虽然不可否认有点晦涩,但我会说这样做的最令人愉悦的排版方式是
for (int i = myArray.Length; i --> 0; )
{
//do something
}
回答by Adam Rosenfield
That's definitely the best way for any array whose length is a signed integral type. For arrays whose lengths are an unsigned integral type (e.g. an std::vector
in C++), then you need to modify the end condition slightly:
对于任何长度为有符号整数类型的数组来说,这绝对是最好的方法。对于长度为无符号整数类型的数组(例如std::vector
C++ 中的an ),则需要稍微修改结束条件:
for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
// blah
If you just said i >= 0
, this is always true for an unsigned integer, so the loop will be an infinite loop.
如果你只是说i >= 0
,这对于无符号整数总是正确的,所以循环将是一个无限循环。
回答by Marc Gravell
Looks good to me. If the indexer was unsigned (uint etc), you might have to take that into account. Call me lazy, but in that (unsigned) case, I might just use a counter-variable:
在我看来很好。如果索引器未签名(uint 等),您可能必须考虑到这一点。叫我懒惰,但在那种(无符号)情况下,我可能只使用一个反变量:
uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
arr[--pos] = 42;
}
(actually, even here you'd need to be careful of cases like arr.Length = uint.MaxValue... maybe a != somewhere... of course, that is a very unlikely case!)
(实际上,即使在这里,您也需要小心 arr.Length = uint.MaxValue... 可能是 != 某处...当然,这是一种非常不可能的情况!)
回答by MusiGenesis
I'm going to try answering my own question here, but I don't really like this, either:
我将尝试在这里回答我自己的问题,但我也不喜欢这样:
for (int i = 0; i < myArray.Length; i++)
{
int iBackwards = myArray.Length - 1 - i; // ugh
myArray[iBackwards] = 666;
}
回答by Keltex
In C#using Linq:
在C# 中使用Linq:
foreach(var item in myArray.Reverse())
{
// do something
}
回答by Johannes Schaub - litb
In C++ you basicially have the choice between iterating using iterators, or indices.
Depending on whether you have a plain array, or a std::vector
, you use different techniques.
在 C++ 中,您基本上可以选择使用迭代器或索引进行迭代。根据您使用的是普通数组还是 a std::vector
,您可以使用不同的技术。
Using std::vector
使用 std::vector
Using iterators
使用迭代器
C++ allows you to do this using std::reverse_iterator:
C++ 允许您使用 std::reverse_iterator:
for(std::vector<T>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) {
/* std::cout << *it; ... */
}
Using indices
使用索引
The unsigned integral type returned by std::vector<T>::size
is notalways std::size_t
. It can be greater or less. This is crucial for the loop to work.
通过返回的无符号整型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]; ... */
}
It works, since unsigned integral types values are defined by means of modulo their count of bits. Thus, if you are setting -N
, you end up at (2 ^ BIT_SIZE) -N
它有效,因为无符号整数类型值是通过对它们的位数取模来定义的。因此,如果您正在设置-N
,您最终会在(2 ^ BIT_SIZE) -N
Using Arrays
使用数组
Using iterators
使用迭代器
We are using std::reverse_iterator
to do the iterating.
我们std::reverse_iterator
用来做迭代。
for(std::reverse_iterator<element_type*> it(a + sizeof a / sizeof *a), itb(a);
it != itb;
++it) {
/* std::cout << *it; .... */
}
Using indices
使用索引
We can safely use std::size_t
here, as opposed to above, since sizeof
always returns std::size_t
by definition.
我们可以安全地使用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]; ... */
}
Avoiding pitfalls with sizeof applied to pointers
避免将 sizeof 应用于指针的陷阱
Actually the above way of determining the size of an array sucks. If a is actually a pointer instead of an array (which happens quite often, and beginners will confuse it), it will silently fail. A better way is to use the following, which will fail at compile time, if given a pointer:
实际上,上述确定数组大小的方法很糟糕。如果 a 实际上是一个指针而不是一个数组(这种情况经常发生,初学者会混淆它),它会默默地失败。更好的方法是使用以下内容,如果给定一个指针,它将在编译时失败:
template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N];
It works by getting the size of the passed array first, and then declaring to return a reference to an array of type char of the same size. char
is defined to have sizeof
of: 1. So the returned array will have a sizeof
of: N * 1, which is what we are looking for, with only compile time evaluation and zero runtime overhead.
它的工作原理是首先获取传递的数组的大小,然后声明返回对相同大小的 char 类型数组的引用。char
被定义为具有sizeof
:1。所以返回的数组将具有sizeof
:N * 1,这是我们正在寻找的,只有编译时评估和零运行时开销。
Instead of doing
而不是做
(sizeof a / sizeof *a)
Change your code so that it now does
更改您的代码,使其现在可以
(sizeof array_size(a))
回答by domain.dot.net team
NOTE: This post ended up being far more detailed and therefore off topic, I apologize.
注意:这篇文章最终变得更加详细,因此偏离主题,我深表歉意。
That being said my peers read it and believe it is valuable 'somewhere'. This thread is not the place. I would appreciate your feedback on where this should go (I am new to the site).
话虽如此,我的同行阅读了它并相信它在“某处”有价值。这个线程不是地方。我很感激您对应该去哪里的反馈(我是该网站的新手)。
Anyway this is the C# version in .NET 3.5 which is amazing in that it works on any collection type using the defined semantics. This is a default measure (reuse!) not performance or CPU cycle minimization in most common dev scenario although that never seems to be what happens in the real world (premature optimization).
无论如何,这是 .NET 3.5 中的 C# 版本,令人惊奇的是它可以使用定义的语义在任何集合类型上工作。在最常见的开发场景中,这是一个默认的度量(重用!)而不是性能或 CPU 周期最小化,尽管这在现实世界中似乎永远不会发生(过早优化)。
*** Extension method working over any collection type and taking an action delegate expecting a single value of the type, all executed over each item in reverse **
*** 扩展方法适用于任何集合类型并采用期望该类型的单个值的动作委托,所有这些都在相反的每个项目上执行 **
Requres 3.5:
要求 3.5:
public static void PerformOverReversed<T>(this IEnumerable<T> sequenceToReverse, Action<T> doForEachReversed)
{
foreach (var contextItem in sequenceToReverse.Reverse())
doForEachReversed(contextItem);
}
Older .NET versions or do you want to understand Linq internals better? Read on.. Or not..
较旧的 .NET 版本还是您想更好地了解 Linq 内部结构?继续阅读.. 或不..
ASSUMPTION: In the .NET type system the Array type inherits from the IEnumerable interface (not the generic IEnumerable only IEnumerable).
假设:在 .NET 类型系统中,Array 类型继承自 IEnumerable 接口(不是泛型 IEnumerable 仅 IEnumerable)。
This is all you need to iterate from beginning to end, however you want to move in the opposite direction. As IEnumerable works on Array of type 'object' any type is valid,
这就是您从头到尾迭代所需的全部内容,但是您想要朝相反的方向移动。由于 IEnumerable 适用于“对象”类型的数组,因此任何类型都是有效的,
CRITICAL MEASURE: We assume if you can process any sequence in reverse order that is 'better' then only being able to do it on integers.
关键措施:我们假设如果您可以以“更好”的相反顺序处理任何序列,那么只能对整数进行处理。
Solution a for .NET CLR 2.0-3.0:
.NET CLR 2.0-3.0 的解决方案 a:
Description: We will accept any IEnumerable implementing instance with the mandate that each instance it contains is of the same type. So if we recieve an array the entire array contains instances of type X. If any other instances are of a type !=X an exception is thrown:
说明:我们将接受任何 IEnumerable 实现实例,并要求它包含的每个实例都属于同一类型。因此,如果我们收到一个数组,则整个数组都包含 X 类型的实例。如果任何其他实例的类型为 !=X,则会引发异常:
A singleton service:
单例服务:
public class ReverserService { private ReverserService() { }
公共类 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 {
[TestFixture] 公共类 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;
}
}
回答by xyz
I'd use the code in the original question, but if you really wanted to use foreach and have an integer index in C#:
我会使用原始问题中的代码,但如果您真的想使用 foreach 并在 C# 中有一个整数索引:
foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
myArray[i] = 42;
}
回答by Twotymz
In C I like to do this:
在 CI 中喜欢这样做:
int i = myArray.Length;
while (i--) {
myArray[i] = 42;
}
C# example added by MusiGenesis:
MusiGenesis 添加的 C# 示例:
{int i = myArray.Length; while (i-- > 0)
{
myArray[i] = 42;
}}
回答by Jay Bazuzi
In C#, using Visual Studio 2005 or later, type 'forr' and hit [TAB] [TAB]. This will expand to a for
loop that goes backwards through a collection.
在 C# 中,使用 Visual Studio 2005 或更高版本,键入 'forr' 并点击 [TAB] [TAB]。这将扩展为for
向后遍历集合的循环。
It's so easy to get wrong (at least for me), that I thought putting this snippet in would be a good idea.
很容易出错(至少对我来说),我认为把这个片段放在里面是个好主意。
That said, I like Array.Reverse()
/ Enumerable.Reverse()
and then iterate forwardsbetter - they more clearly state intent.
也就是说,我喜欢Array.Reverse()
/Enumerable.Reverse()
然后更好地向前迭代- 它们更清楚地说明意图。