Каков наилучший способ сделать обратный цикл в C / C# / C++?


мне нужно двигаться назад через массив, поэтому у меня есть такой код:

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

есть ли лучший способ сделать это?

Update: я надеялся, что, возможно, у C# был какой-то встроенный механизм для этого, например:

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

Обновление 2: Есть are лучшие способы. Руна берет приз с:

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
}
15 80

15 ответов:

хотя, по общему признанию, немного неясно, я бы сказал, что наиболее типографически приятный способ сделать это

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

в C++ у вас есть выбор между итерацией с использованием итераторов или индексов. В зависимости от того, есть ли у вас простой массив, или std::vector, вы используете разные методы.

С помощью 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]; ... */
}

избегая ловушек с sizeof применяется к указателям

на самом деле выше способ определения размера массива хреново. Если a на самом деле является указателем вместо массива (что происходит довольно часто, и новички будут путать его), он будет молча терпеть неудачу. Лучший способ-использовать следующее, которое не будет выполнено во время компиляции, если задан указатель:

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

он работает, получая размер пройденного сначала массив, а затем объявление, чтобы вернуть ссылку на массив типа char того же размера. char определяется какsizeof из: 1. Так что возвращаемый массив будет иметь sizeof of: N * 1, что мы и ищем, только с оценкой времени компиляции и нулевыми накладными расходами во время выполнения.

вместо

(sizeof a / sizeof *a)

измените свой код так, чтобы он теперь делал

(sizeof array_size(a))

В C#, используя Visual Studio 2005 или более позднюю версию,введите 'forr' и нажмите [TAB] [TAB]. Это будет расширяться до for цикл, который идет назад по коллекции.

это так легко ошибиться (по крайней мере для меня), что я думал, что положить этот фрагмент будет хорошей идеей.

что сказал, мне нравится Array.Reverse()/Enumerable.Reverse() а затем повторить вперед лучше - они более четко заявить о намерениях.

Я бы всегда предпочитаю четкий код против 'типографским способом приятно'. Таким образом, я всегда буду использовать :

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

вы можете рассматривать его как стандартный способ петли назад.
Только мои два цента...

In C# используя Linq:

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

Это определенно лучший способ для любого массива, длина которого является целочисленным типом со знаком. Для массивов, длина которых является беззнаковым целочисленным типом (например,std::vector в C++), то вам нужно немного изменить граничное условие:

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

Если ты только что сказал i >= 0, это всегда верно для целого числа без знака, поэтому цикл будет бесконечным циклом.

выглядит хорошо для меня. Если индексатор был без знака (uint и т. д.), Вам, возможно, придется принять это во внимание. Назовите меня ленивым, но в этом (без знака) случае я мог бы просто использовать переменную счетчика:

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

(на самом деле, даже здесь вам нужно быть осторожным в таких случаях, как arr.Длина = uint имеет.Максвелл... может быть != где-то... конечно, это очень маловероятно!)

В C мне нравится это делать:


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

C# пример добавлен MusiGenesis:

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

лучший способ сделать это в C++, вероятно, использовать итератор (или лучше, диапазон) адаптеры, которые будут лениво преобразовывать последовательность по мере ее прохождения.

по сути,

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

отображает диапазон "диапазон" (здесь он пуст, но я уверен, что вы можете добавить элементы самостоятельно) в обратном порядке. Конечно, просто повторять диапазон не очень полезно, но передача этого нового диапазона алгоритмам и прочее довольно круто.

этот механизм также может используйте для гораздо более мощных применений:

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

будет лениво вычислять диапазон "диапазон", где функция" f "применяется ко всем элементам, элементы, для которых" p " не является истинным, удаляются, и, наконец, результирующий диапазон отменяется.

синтаксис трубы является наиболее читаемым IMO, учитывая его инфикс. повышение.Обновление библиотеки диапазона в ожидании обзора реализует это, но довольно просто сделать это самостоятельно. Это еще более круто с лямбда DSEL для генерации функции f и предикат p в строке.

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

Я предпочитаю цикл while. Это более ясно для меня, чем уменьшение i в состоянии цикла for

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

Я бы использовал код в исходном вопросе, но если вы действительно хотите использовать foreach и иметь целочисленный индекс в C#:

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

Я не уверен, что понимаю, почему любая из альтернатив лучше, если доброта включает ясность или ремонтопригодность.

Я попробую ответить на свой собственный вопрос здесь, но мне это тоже не нравится:

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

Примечание: этот пост оказался гораздо более подробным и, следовательно, не по теме, я прошу прощения.

это, как говорится, мои сверстники читают его и считают, что это ценно "где-то". Этой теме не место. Я был бы признателен за ваши отзывы о том, где это должно идти (я новичок на сайте).


В любом случае это версия C# в .NET 3.5, которая удивительна тем, что она работает на любом типе коллекции, используя определенную семантику. Это мера по умолчанию (повторное использование!) не минимизация производительности или цикла процессора в большинстве распространенных сценариев разработки, хотя это никогда не кажется тем, что происходит в реальном мире (преждевременная оптимизация).

*** метод расширения, работающий над любым типом коллекции и принимающий делегат действия, ожидающий одно значение типа, все выполняется над каждым элементом в обратном порядке**

Requres 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 тип массива наследуется от интерфейса IEnumerable (не общий IEnumerable только IEnumerable).

Это все, что вам нужно повторить от начала до конца, однако вы хотите двигаться в противоположном направлении. Поскольку IEnumerable работает с массивом типа 'object', допустим любой тип,

критическая мера: мы предполагаем, что если вы можете обрабатывать любую последовательность в обратном порядке, что "лучше" , то только будучи в состоянии сделать это на целые.

решение a для .NET CLR 2.0-3.0:

описание: мы примем любой экземпляр реализации IEnumerable с мандатом, что каждый экземпляр, который он содержит, имеет тот же тип. Поэтому, если мы получаем массив, весь массив содержит экземпляры типа X. Если какие-либо другие экземпляры имеют тип !=X выбрасывается исключение:

одноэлементный услуги:

public class 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] публичный класс 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;
    }
}