我需要向后移动一个数组,所以我有这样的代码:
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
}
答案 0 :(得分:130)
虽然有点模糊,但我会说这种印刷方式最令人愉悦的方式是
for (int i = myArray.Length; i --> 0; )
{
//do something
}
答案 1 :(得分:110)
在C ++中,您可以在使用迭代器或索引进行迭代之间进行选择。
根据您是使用普通数组还是std::vector
,您可以使用不同的技术。
C ++允许您使用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]; ... */
}
实际上上面确定阵列大小的方法很糟糕。如果a实际上是一个指针而不是一个数组(这经常发生,并且初学者会混淆它),它将无声地失败。更好的方法是使用以下命令,如果给出指针,它将在编译时失败:
template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N];
它首先获取传递的数组的大小,然后声明返回对相同大小的char类型数组的引用。 char
被定义为具有sizeof
:1。因此返回的数组将具有sizeof
:N * 1,这是我们正在寻找的,只有编译时评估和零运行时开销。
而不是做
(sizeof a / sizeof *a)
更改您的代码,使其现在
(sizeof array_size(a))
答案 2 :(得分:51)
在C#中,使用Visual Studio 2005或更高版本,键入'forr'并点击[TAB] [TAB] 。这将扩展为for
循环,后退通过集合。
很容易出错(至少对我而言),我认为把这个片段放在一个好主意。
也就是说,我喜欢Array.Reverse()
/ Enumerable.Reverse()
,然后更好地迭代转发 - 它们更明确地说明意图。
答案 3 :(得分:32)
我会始终更喜欢清晰的代码,而不是'印刷品令人愉悦的'代码。 因此,我总是使用:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something ...
}
您可以将其视为向后循环的标准方式 只是我的两分钱......
答案 4 :(得分:17)
在 C#中使用 Linq :
foreach(var item in myArray.Reverse())
{
// do something
}
答案 5 :(得分:10)
对于任何长度为有符号整数类型的数组来说,这绝对是最好的方法。对于长度为无符号整数类型的数组(例如C ++中的std::vector
),则需要稍微修改结束条件:
for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
// blah
如果你刚才说i >= 0
,对无符号整数总是如此,所以循环将是一个无限循环。
答案 6 :(得分:4)
对我来说很好看。如果索引器是未签名的(uint等),您可能必须考虑到这一点。叫我懒惰,但在那个(无符号)情况下,我可能只使用一个反变量:
uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
arr[--pos] = 42;
}
(实际上,即使在这里,你也需要注意像arr.Length = uint.MaxValue这样的情况......也许是!=某处...当然,这是一个非常不可能的情况!)
答案 7 :(得分:4)
在C中我喜欢这样做:
int i = myArray.Length;
while (i--) {
myArray[i] = 42;
}
MusiGenesis添加的C#示例:
{int i = myArray.Length; while (i-- > 0)
{
myArray[i] = 42;
}}
答案 8 :(得分:3)
在C ++中执行此操作的最佳方法可能是使用迭代器(或更好的,范围)适配器,这些适配器将在遍历时延迟转换序列。
基本上,
vector<value_type> range;
foreach(value_type v, range | reversed)
cout << v;
以相反的顺序显示范围“范围”(这里,它是空的,但我很确定你可以自己添加元素)。 当然,简单地迭代范围并没有多大用处,但将新范围传递给算法和东西非常酷。
此机制也可用于更强大的用途:
range | transformed(f) | filtered(p) | reversed
懒惰计算范围“范围”,其中函数“f”应用于所有元素,“p”不为真的元素将被删除,最后结果范围反转。
管道语法是最易读的IMO,因为它是中缀。 Boost.Range库更新等待审核实现了这一点,但是自己也可以这么做。使用lambda DSEL来生成函数f和谓词p in-line会更加酷。
答案 9 :(得分:1)
// this is how I always do it
for (i = n; --i >= 0;){
...
}
答案 10 :(得分:1)
我更喜欢while循环。对于我来说,比在for循环
的条件下递减i
更清楚
int i = arrayLength;
while(i)
{
i--;
//do something with array[i]
}
答案 11 :(得分:0)
我会在原始问题中使用代码,但如果您真的想使用foreach并在C#中使用整数索引:
foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
myArray[i] = 42;
}
答案 12 :(得分:0)
如果善意包括清晰度或可维护性,我不确定为什么任何替代品会更好。
答案 13 :(得分:0)
对于 C++:
正如其他人所提到的,在可能的情况下(即,当您一次只需要一个元素时),强烈建议使用迭代器既明确又避免常见的陷阱。现代 C++ 有一个更简洁的语法,auto
:
std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
std::cout<<*it<<" ";
}
打印 4 3 2 1
。
你也可以在循环中修改值:
std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
*it = *it + 10;
std::cout<<*it<<" ";
}
导致 14 13 12 11
被打印,然后 {11, 12, 13, 14}
出现在 std::vector
中。
如果您不打算在循环期间修改该值,则应确保在您尝试这样做时会出现错误,这与编写 for(const auto& element : vec)
的方式类似。这可能是这样的:
std::vector<int> vec = {1,2,3,4};
for (auto it = vec.crbegin(); it != vec.crend(); ++it) { // used crbegin()/crend() here...
*it = *it + 10; // ... so that this is a compile-time error
std::cout<<*it<<" ";
}
在这种情况下,我的编译器错误是:
/tmp/main.cpp:20:9: error: assignment of read-only location ‘it.std::reverse_iterator<__gnu_cxx::__normal_iterator<const int*, std::vector<int> > >::operator*()’
20 | *it = *it + 10;
| ~~~~^~~~~~~~~~
另请注意,您应该确保不要同时使用不同的迭代器类型:
std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.end(); ++it) { // mixed rbegin() and end()
std::cout<<*it<<" ";
}
导致详细错误:
/tmp/main.cpp: In function ‘int main()’:
/tmp/main.cpp:19:33: error: no match for ‘operator!=’ (operand types are ‘std::reverse_iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >’ and ‘std::vector<int>::iterator’ {aka ‘__gnu_cxx::__normal_iterator<int*, std::vector<int> >’})
19 | for (auto it = vec.rbegin(); it != vec.end(); ++it) {
| ~~ ^~ ~~~~~~~~~
| | |
| | std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}
| std::reverse_iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >
如果堆栈上有 C 风格的数组,你可以这样做:
int vec[] = {1,2,3,4};
for (auto it = std::crbegin(vec); it != std::crend(vec); ++it) {
std::cout<<*it<<" ";
}
如果您确实需要索引,请考虑以下选项:
void loop_reverse(std::vector<int>& vec) {
if (vec.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
throw std::invalid_argument("Input too large");
}
const int sz = static_cast<int>(vec.size());
for(int i=sz-1; i >= 0; --i) {
// do something with i
}
}
void loop_reverse2(std::vector<int>& vec) {
for(size_t i=vec.size(); i-- > 0;) { // reverse indices from N-1 to 0
// do something with i
}
}
void loop_reverse3(std::vector<int>& vec) {
for(size_t offset=0; offset < vec.size(); ++offset) {
const size_t i = vec.size()-1-offset; // reverse indices from N-1 to 0
// do something with i
}
}
答案 14 :(得分:-1)
我打算在这里回答我自己的问题,但我也不喜欢这样:
for (int i = 0; i < myArray.Length; i++)
{
int iBackwards = myArray.Length - 1 - i; // ugh
myArray[iBackwards] = 666;
}
答案 15 :(得分:-4)
注意:这篇文章最终更加详细,因此不在话题,我道歉。
据说我的同龄人读了它并且相信它在某个地方很有价值。这个帖子不是那个地方。我很感激你对这应该去哪里的反馈(我是网站的新手)。
无论如何,这是.NET 3.5中的C#版本,它的惊人之处在于它可以使用定义的语义处理任何集合类型。这是一个默认的度量(重用!),而不是大多数常见开发方案中的性能或CPU周期最小化,尽管这似乎永远不会出现在现实世界中(过早优化)。
***扩展方法在任何集合类型上工作,并使一个操作委托期望一个单独的类型值,所有这些都反过来执行每个项目**
要求3.5:
public static void PerformOverReversed<T>(this IEnumerable<T> sequenceToReverse, Action<T> doForEachReversed)
{
foreach (var contextItem in sequenceToReverse.Reverse())
doForEachReversed(contextItem);
}
较旧的.NET版本或者您想更好地了解Linq内部版本吗?请继续阅读......或者不......
假设:在.NET类型系统中,Array类型继承自IEnumerable接口(不是通用的IEnumerable IEnumerable)。
这是您从头到尾迭代所需的全部内容,但是您希望向相反的方向移动。由于IEnumerable适用于“对象”类型的数组,因此任何类型都有效,
关键度量:我们假设您能够以相反的顺序处理任何“更好”的序列,然后才能在整数上执行。
.NET CLR 2.0-3.0的解决方案a:
描述:我们将接受任何IEnumerable实现实例,其任务是它包含的每个实例都是相同的类型。因此,如果我们接收一个数组,整个数组包含类型X的实例。如果任何其他实例属于类型!= X抛出异常:
单身人士服务:
公共类ReverserService { private 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] 公共课测试123 {
/// <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;
}
}