Можно ли выполнить итерацию назад по foreach?

StackOverflow https://stackoverflow.com/questions/1211608

  •  06-07-2019
  •  | 
  •  

Вопрос

Я знаю, что мог бы использовать for и добиться того же эффекта, но могу ли я выполнить цикл назад через foreach цикл в С#?

Это было полезно?

Решение

При работе со списком (прямая индексация) вы не можете сделать это так же эффективно, как с помощью цикла for.

Изменить. Обычно это означает, что когда вы можете использовать цикл foreach, это, вероятно, правильный метод для этой задачи. Кроме того, поскольку <=> реализовано по порядку, сама конструкция создана для выражения циклов, которые не зависят от индексов элементов и порядка итераций, что особенно важно в параллельное программирование . Мое мнение , что итерация, основанная на порядке, не должна использовать <=> для цикла.

Другие советы

Если вы используете .NET 3.5, вы можете сделать это:

IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())

Он не очень эффективен, поскольку в основном он должен проходить через перечислитель вперед, помещая все в стек, а затем выводить все обратно в обратном порядке.

Если у вас есть непосредственно индексируемая коллекция (например, IList), вам определенно следует использовать цикл for.

Если вы работаете в .NET 2.0 и не можете использовать цикл for (т. е. у вас просто есть IEnumerable), вам просто нужно написать собственную функцию Reverse. Это должно работать:

static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
    return new Stack<T>(input);
}

Это зависит от некоторого поведения, которое, возможно, не так очевидно. Когда вы передаете IEnumerable конструктору стека, он будет перебирать его и помещать элементы в стек. Когда вы затем выполняете итерацию в стеке, все всплывает в обратном порядке.

Это и метод расширения .NET 3.5 Reverse(), очевидно, взорвутся, если вы передадите ему IEnumerable, который никогда не прекращает возвращать элементы.

Как говорит 280Z28, для IList<T> вы можете просто использовать индекс. Вы можете скрыть это в методе расширения:

public static IEnumerable<T> FastReverse<T>(this IList<T> items)
{
    for (int i = items.Count-1; i >= 0; i--)
    {
        yield return items[i];
    }
}

Это будет быстрее, чем Enumerable.Reverse(), который сначала буферизует все данные. (Я не верю, что Reverse применяет какие-либо оптимизации так же, как Count().) Обратите внимание, что эта буферизация означает, что данные полностью читаются при первом запуске итерации, тогда как FastReverse будет & Quot; см Quot &; любые изменения, внесенные в список во время итерации. (Он также сломается, если вы удалите несколько элементов между итерациями.)

Для общих последовательностей нет способа повторения в обратном порядке - последовательность может быть бесконечной, например:

public static IEnumerable<T> GetStringsOfIncreasingSize()
{
    string ret = "";
    while (true)
    {
        yield return ret;
        ret = ret + "x";
    }
}

Что бы вы ожидали, если бы попытались повторить это в обратном порядке?

Прежде чем использовать 'foreach' для итерации, переверните список методом 'reverse':

    myList.Reverse();
    foreach( List listItem in myList)
    {
       Console.WriteLine(listItem);
    }

Если вы используете List < T >, вы также можете использовать этот код:

List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();

Это метод, который записывает список обратно сам по себе.

Теперь foreach:

foreach(string s in list)
{
    Console.WriteLine(s);
}

Вывод:

3
2
1

Иногда у вас нет такой роскоши, как индексирование, или, возможно, вы хотите отменить результаты запроса Linq, или, возможно, вы не хотите изменять исходную коллекцию. Если что-то из этого верно, Linq может вам помочь.

Метод расширения Linq, использующий анонимные типы с Linq Select для предоставления ключа сортировки для Linq OrderByDescending;

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }

Использование:

    var eable = new[]{ "a", "b", "c" };

    foreach(var o in eable.Invert())
    {
        Console.WriteLine(o);
    }

    // "c", "b", "a"

Он называется «Инвертировать», потому что он является синонимом «Обратного» и позволяет устранить неоднозначность с помощью реализации List Reverse.

Также возможно перевернуть определенные диапазоны коллекции, поскольку Int32.MinValue и Int32.MaxValue находятся вне диапазона любого индекса коллекции, мы можем использовать их для процесса упорядочивания;если индекс элемента находится ниже заданного диапазона, ему присваивается Int32.MaxValue, чтобы его порядок не менялся при использовании OrderByDescending. Аналогично, элементам с индексом, превышающим заданный диапазон, будет присвоено Int32.MinValue, чтобы они появятся в конце процесса заказа.Всем элементам в данном диапазоне присваиваются их обычные индексы, и они соответственно меняются местами.

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source, int index, int count)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i < index ? Int32.MaxValue : i >= index + count ? Int32.MinValue : i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }

Использование:

    var eable = new[]{ "a", "b", "c", "d" };

    foreach(var o in eable.Invert(1, 2))
    {
        Console.WriteLine(o);
    }

    // "a", "c", "b", "d"

Я не уверен в производительности этих реализаций Linq по сравнению с использованием временного списка для упаковки коллекции для реверсирования.


На момент написания я не знал о собственной реализации Reverse в Linq, тем не менее, было интересно над этим работать.https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx

Нет.ForEach просто перебирает коллекцию для каждого элемента, и порядок зависит от того, использует ли он IEnumerable или GetEnumerator().

Это возможно, если вы можете изменить код коллекции , который реализует IEnumerable или IEnumerable (например, ваша собственная реализация IList).

Создайте итератор выполняя эту работу за вас, например, как следующая реализация через Интерфейс IEnumerable (в этом примере предполагается, что 'items' - это поле List):

public IEnumerator<TObject> GetEnumerator()
{
    for (var i = items.Count - 1; i >= 0; i--)
    { 
        yield return items[i];
    }
}

IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

Из-за этого ваш список будет перебирать ваш список в обратном порядке.

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

Обдумав хороший ответ от Джона Скита , это может быть универсально:

public static IEnumerable<T> Directional<T>(this IList<T> items, bool Forwards) {
    if (Forwards) foreach (T item in items) yield return item;
    else for (int i = items.Count-1; 0<=i; i--) yield return items[i];
}

А потом использовать как

foreach (var item in myList.Directional(forwardsCondition)) {
    .
    .
}

Я использовал этот код, который работал

                if (element.HasAttributes) {

                    foreach(var attr in element.Attributes().Reverse())
                    {

                        if (depth > 1)
                        {
                            elements_upper_hierarchy_text = "";
                            foreach (var ancest  in element.Ancestors().Reverse())
                            {
                                elements_upper_hierarchy_text += ancest.Name + "_";
                            }// foreach(var ancest  in element.Ancestors())

                        }//if (depth > 1)
                        xml_taglist_report += " " + depth  + " " + elements_upper_hierarchy_text+ element.Name + "_" + attr.Name +"(" + attr.Name +")" + "   =   " + attr.Value + "\r\n";
                    }// foreach(var attr in element.Attributes().Reverse())

                }// if (element.HasAttributes) {

Это работает довольно хорошо

List<string> list = new List<string>();

list.Add("Hello");
list.Add("Who");
list.Add("Are");
list.Add("You");

foreach (String s in list)
{
    Console.WriteLine(list[list.Count - list.IndexOf(s) - 1]);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top