Можете ли вы перечислить коллекцию в C # не по порядку? - PullRequest
13 голосов
/ 31 октября 2008

Есть ли способ использовать цикл foreach для перебора коллекции в обратном порядке или в совершенно случайном порядке?

Ответы [ 11 ]

15 голосов
/ 31 октября 2008

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

Вот метод расширения случайного перечисления:

public static IEnumerable<T> OrderRandomly<T>(this IEnumerable<T> sequence)
{
    Random random = new Random();
    List<T> copy = sequence.ToList();

    while (copy.Count > 0)
    {
        int index = random.Next(copy.Count);
        yield return copy[index];
        copy.RemoveAt(index);
    }
}

Ваше использование будет:

foreach (int n in Enumerable.Range(1, 10).OrderRandomly())
    Console.WriteLine(n);
15 голосов
/ 31 октября 2008

Используя System.Linq вы можете сделать ...

// List<...> list;
foreach (var i in list.Reverse())
{
}

Для случайного порядка вам придется отсортировать его случайным образом, используя list.OrderBy (еще одно расширение Linq), а затем выполнить итерацию этого упорядоченного списка.

var rnd = new Random();
var randomlyOrdered = list.OrderBy(i => rnd.Next());
foreach (var i in randomlyOrdered)
{
}
1 голос
/ 01 ноября 2008

Используя IList<T> из C5 Generic Collection Library , обратная итерация является функцией, а не расширением:

foreach (var i in list.Reverse())
{
}

Также вы можете использовать метод Shuffle() для получения случайного порядка:

var listClone = (IList<T>) list.Clone();
listClone.Shuffle();
foreach (var i in listClone)
{
}
1 голос
/ 31 октября 2008

Мне действительно понравился подход cfeduke с LINQ, и меня беспокоит, что это ускользнуло от меня. Добавить в мой предыдущий пример. Если вы хотите выполнять нечетные и четные итерации с помощью LINQ, вы можете использовать

// Even
foreach (var i in ints.FindAll(number => number % 2 == 0))
{
      Console.WriteLine(i);
}

// Odd
foreach (var i in ints.FindAll(number => number % 2 != 0))
{
      Console.WriteLine(i);
}
1 голос
/ 31 октября 2008

Начиная с C # 2.0, у вас есть возможность использовать ключевое слово yield для простой реализации пользовательских итераторов. Вы можете прочитать больше о ключевом слове yield на MSDN http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

Вы можете думать о доходности как о способности возвращать значение из цикла, но вам следует обратиться к ссылке выше для полного объяснения того, что они из себя представляют и что они могут сделать.

Я написал короткий пример того, как реализовать несколько пользовательских итераторов. Я реализовал их как методы расширения (http://msdn.microsoft.com/en-us/library/bb383977.aspx), чтобы сделать код немного более ориентированным на поток, и я также использую инициализаторы массива (http://msdn.microsoft.com/en-us/library/aa664573.aspx), чтобы установить начальные значения для списка целых чисел.

Ни методы расширения, ни инициализаторы массива не нужны для реализации пользовательских итераторов, но они являются хорошими функциями c # 3.0, которые помогают писать более чистый код

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> ints = 
                new List<int> { 1,2,3,4,5,6,7,8,9,10};

            Console.WriteLine("Iterating over Odd numbers only.");
            foreach (int i in ints.Odd())
            {
                Console.WriteLine(i);
            }

            Console.WriteLine("Iterating over Even numbers only.");
            foreach (int i in ints.Even())
            {
                Console.WriteLine(i);
            }

            Console.WriteLine("Iterating over the list in reversed order.");
            foreach (int i in ints.Reversed())
            {
                Console.WriteLine(i);
            }

            Console.WriteLine("Iterating over the list in random order.");
            foreach (int i in ints.Random())
            {
                Console.WriteLine(i);
            }

            Console.ReadLine();
        }
    }

    public static class ListExtensions
    {
        /// <summary>
        /// Iterates over the list only returns even numbers
        /// </summary>
        /// <param name="list"></param>
        public static IEnumerable<int> Even(this List<int> list)
        {
            foreach (var i in list)
            {
                if (i % 2 == 0)
                 {
                    yield return i;
                }
           }
        }

        /// <summary>
        /// Iterates over the list only returns odd numbers
        /// </summary>
        public static IEnumerable<int> Odd(this List<int> list)
        {
            foreach (var i in list)
            {
                if (i % 2 != 0)
                {
                    yield return i;
                }
            }
        }

        /// <summary>
        /// Iterates over the list in reversed order
        /// </summary>
        public static IEnumerable<int> Reversed(this List<int> list)
        {
            for (int i = list.Count; i >= 0; i--)
            {
                yield return i;
            }
        }

        /// <summary>
        /// Iterates over the list in random order
        /// </summary>
        public static IEnumerable<int> Random(this List<int> list)
        {
            // Initialize a random number generator with a seed.
            System.Random rnd =
                new Random((int)DateTime.Now.Ticks);

            // Create a list to keep track of which indexes we've
            // already returned
            List<int> visited =
                new List<int>();

            // loop until we've returned the value of all indexes
            // in the list
            while (visited.Count < list.Count)
            {
                int index =
                    rnd.Next(0, list.Count);

                // Make sure we've not returned it already
                if (!visited.Contains(index))
                {
                    visited.Add(index);
                    yield return list[index];
                }
            }
        }
    }
}
1 голос
/ 31 октября 2008

Я не думаю, что есть способ сделать это напрямую, но в значительной степени as good использовать метод расширения, который возвращает новую коллекцию через ключевое слово yield return. Они могут прийти из уже существующей библиотеки; другие отметили, что в LINQ есть метод Reverse, и такие вещи, как OrderBy, также будут работать.

Пример: если вы используете метод расширения LINQ Reverse() для IEnumerable<T>, который использует yield return для предоставления коллекции в обратном порядке, то выполнение foreach(var myThing in myCollection.Reverse()) будет перечислять всю коллекцию в обратном порядке.

Важно : yield return является ключевым. Это значит «когда я перечисляю эту коллекцию, то иди за вещами». В отличие от альтернативы простого построения нового , обращенного сбора, который крайне неэффективен и, возможно, имеет побочные эффекты.

0 голосов
/ 21 ноября 2008

Использовать случайный порядок
http://www.dailycoding.com/..using_linq.aspx

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

list.Add(new Employee { Id = 1, Name = "Davolio Nancy" });
list.Add(new Employee { Id = 2, Name = "Fuller Andrew" });
list.Add(new Employee { Id = 3, Name = "Leverling Janet" });
list.Add(new Employee { Id = 4, Name = "Peacock Margaret" });
list.Add(new Employee { Id = 5, Name = "Buchanan Steven" });
list.Add(new Employee { Id = 6, Name = "Suyama Michael" });
list.Add(new Employee { Id = 7, Name = "King Robert" });
list.Add(new Employee { Id = 8, Name = "Callahan Laura" });
list.Add(new Employee { Id = 9, Name = "Dodsworth Anne" });

list = list.OrderBy(emp => Guid.NewGuid()).ToList();
0 голосов
/ 31 октября 2008

Из моего прочтения спецификации языка C # оператор итерации foreach зависит от структуры / класса / интерфейса, который итерируется, для которого определена функция GetEnumerator (). Объект, возвращаемый GetEnumerator (), должен иметь MoveNext (), определенный как функция-член. MoveNext () определяется как доступ к «первому» объекту в списке при его первом вызове, затем к «следующему» при последующих вызовах, возвращая истину до тех пор, пока в списке больше не будет элементов, после чего он возвращает ложь.

Функция, к которой относится Domenic, возвращает доход, впервые появляется в версии спецификации 2.0 и действительно полезна для этой цели. Для версии 1.1 единственным вариантом будет получение новой структуры / класса / интерфейса из вашей базы и переопределение GetEnumerator () для возврата нового IEnumerator, где функция MoveNext () будет следовать различным правилам при выборе первого элемента коллекции и любых других. элемент последующего сбора.

Моя собственная рекомендация заключается в том, чтобы использовать индексированную коллекцию, а затем использовать цикл for с соответствующим вычислением индекса (здесь можно использовать генератор случайных чисел, если необходимо, с целочисленным массивом или другим методом для проверки того же индекса). значение не используется дважды), если вы должны сделать это на практике.

0 голосов
/ 31 октября 2008

Хотите ли вы собрать коллекцию и познакомиться с ней?

Если да, попробуйте это:

Random rand = new Random(Environment.TickCount);

test.Sort((string v1, string v2) => {
                if (v1.Equals(v2))
                {
                    return 0;
                }

                int x = rand.Next();
                int y = rand.Next();

                if (x == y)
                {
                    return 0;
                }
                else if (x > y)
                {
                    return 1;
                }

                return -1; 
            });

for (string item in test)
{
  Console.WriteLn(item);
}
// Note that test is List<string>;
0 голосов
/ 31 октября 2008

Вы можете отсортировать список, указав свой собственный компаратор и выполнить итерацию по нему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...