你可以在C#中乱序列举一个集合吗?
有没有办法使用foreach
循环向后或以完全随机的顺序迭代集合?
正如其他答案所提到的, Reverse()
扩展方法将允许您以相反的顺序枚举序列。
这是一个随机枚举扩展方法:
public static IEnumerable OrderRandomly (this IEnumerable sequence) { Random random = new Random(); List 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);
使用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) { }
我认为没有办法直接这样做,但使用通过yield return
关键字返回新集合的扩展方法几乎as good
。 这些可能来自一个已有的图书馆; 其他人已经指出LINQ有一个Reverse
方法,像OrderBy
这样的东西也可以。
示例:如果在IEnumerable
上使用LINQ扩展方法Reverse()
,它使用yield return
以相反的顺序给出集合,那么执行foreach(var myThing in myCollection.Reverse())
将枚举通过集合相反的顺序。
重要 : yield return
是关键。 这意味着“当我枚举这个集合时,然后去取东西。” 与仅构建新的反向集合的替代方案相反,该集合效率极低并且可能具有副作用。
我实际上喜欢使用LINQ的cfeduke方法,这让我觉得它让我感到不安。 添加到我之前的示例中。 如果你想在LINQ的帮助下进行Odd和Even迭代,你可以使用
// 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); }
使用C5通用集合库中的IList
,反向迭代是一个function,而不是扩展:
foreach (var i in list.Reverse()) { }
同样,您可以使用Shuffle()
方法获得随机排序:
var listClone = (IList) list.Clone(); listClone.Shuffle(); foreach (var i in listClone) { }
从C#2.0开始,您可以使用yield关键字来实现自定义迭代器。 您可以在MSDN上阅读有关yield关键字的更多信息,请访问http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
您可以将yield视为从循环内部返回值的能力,但是您应该参考上面的链接以获得它们是什么以及它们可以做什么的完整解释。
我写了一个关于如何实现几个自定义迭代器的简短示例。 我已经将它们实现为扩展方法( http://msdn.microsoft.com/en-us/library/bb383977.aspx ),以使代码更加流式化,我也使用数组初始化器( http:// msdn .microsoft.com / zh-cn / library / aa664573.aspx )设置整数列表的初始值。
扩展方法和数组初始化器都不是实现自定义迭代器所必需的,但它们是c#3.0的很好的function,它有助于编写更清晰的代码
这是我的例子。 它显示了如何通过仅返回奇数,偶数,反转或完全随机的数字来迭代整数列表。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { List ints = new List { 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 { /// /// Iterates over the list only returns even numbers /// /// public static IEnumerable Even(this List list) { foreach (var i in list) { if (i % 2 == 0) { yield return i; } } } /// /// Iterates over the list only returns odd numbers /// public static IEnumerable Odd(this List list) { foreach (var i in list) { if (i % 2 != 0) { yield return i; } } } /// /// Iterates over the list in reversed order /// public static IEnumerable Reversed(this List list) { for (int i = list.Count; i >= 0; i--) { yield return i; } } /// /// Iterates over the list in random order /// public static IEnumerable Random(this List 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 visited = new List (); // 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]; } } } } }
您可以通过提供自己的Comparator对列表进行排序并迭代该列表。
你可以倒退:
for (int i=col.count-1; i>0; i--){ DoSomething ( col.item[i]) ; }
不确定确切的语法,但这是范例。
至于完全随机的顺序,您可以通过它的索引访问集合元素。 为确保您点击每个项目,您需要跟踪已处理的元素(可能通过复制集合然后在访问后删除元素)。
编辑:随机访问的更多细节随机访问的代码可能如下所示:
collection c = originalCollection; while (c.count > 0) { int i = randomNumber(seed) mod c.count element d = c[i]; c.remove(d); DoSomething(d); }
你想收集一个collections品并与之相互影响吗?
如果是,试试这个:
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;
从我对C#语言规范的阅读中,foreach迭代语句依赖于正在迭代的struct / class /接口,并在其上定义了GetEnumerator()函数。 GetEnumerator()返回的对象必须将MoveNext()定义为成员函数。 MoveNext()定义为在第一次调用时访问列表中的“first”对象,然后在后续调用中访问“next”,返回true,直到列表中不再存在其他元素,并返回false。
Domenic所指的特征,yield yield,首先出现在规范的2.0版本中,并且看起来似乎对此有用。 对于1.1版,您唯一的选择是从您的基础派生一个新的struct / class /接口并覆盖GetEnumerator()以返回一个新的IEnumerator,其中MoveNext()函数将遵循选择第一个集合元素和任何的不同规则后续收集元素。
我自己的建议是使用索引集合,然后使用带有适当索引计算的for循环(这里可以使用随机数生成器,如果需要,使用整数数组或其他一些技术来validation相同的索引值不是使用两次)如果你必须在实际操作中这样做。
使用随机排序
http://www.dailycoding.com/..using_linq.aspx
List list = new List (); 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();