我正在尝试交换IEnumerable
的特定项目的排序。
给出IEnumerable<int> a;
个元素:
1,2,3,4,5
我想要做的是编写一个交换迭代器,其结果为a.Exchange(1, 2)
:
1,3,2,4,5
但是我不希望这个简单的目的可以多次迭代枚举。到目前为止我所拥有的是:
public static IEnumerable<T> Exchange<T>(
this IEnumerable<T> source, int index1, int index2) {
var i=0;
foreach(var y in source) {
if(index1==i) {
var j=0;
foreach(var x in source) {
if(index2==j) {
yield return x;
break;
}
++j;
}
}
else {
if(index2==i) {
var j=0;
foreach(var x in source) {
if(index1==j) {
yield return x;
break;
}
++j;
}
}
else {
yield return y;
}
}
++i;
}
}
以下假设index1
和index2
不会超过可枚举的元素。在大多数情况下,代码完成了交换工作(排序),但它确实迭代了不止一次。注意index1
和index2
可能不是source
的真实索引,当枚举发生时,它们将是Mth
和Nth
元素。
ToArray
或ToList
也可能会增加迭代次数。
答案 0 :(得分:8)
WOLOG认为index1
小于index2
。
建立自己的普查员;不要使用foreach
。
对于最多index1
的元素,正常迭代并产生每个元素。
然后当您点击index1
时,分配一个足够大的数组来保存index1
到index2
之间的元素 - 也就是说,包括index1
元素,但是不是index2
元素。
使用枚举器将元素读入该数组。
现在阅读index2
元素并将其生成。
您的枚举器现在设置为index2
以外的一个。
现在产生数组中的所有内容,除了 index1
元素。
然后产生index1
元素。
然后正常产生其余元素。
完成后不要忘记在枚举器上调用Dispose
。
答案 1 :(得分:2)
最简单的方法可能是这样的:
public static IEnumerable<T> Exchange<T>(
this IEnumerable<T> source, int index1, int index2)
{
return source.Select((x, i) => new { x, i })
.OrderBy(p => p.i == index1 ? index2 : p.i == index2 ? index1 : p.i)
.Select(p => p.x);
}
new[] { 1, 2, 3, 4, 5 }.Exchange(1, 2); // { 1, 3, 2, 4, 5 }
如果没有OrderBy
,我认为它看起来像这样:
public static IEnumerable<T> Exchange<T>(
this IEnumerable<T> source, int index1, int index2)
{
if (index1 > index2)
{
int x = index1;
index1 = index2;
index2 = x;
}
int index = 0;
List<T> itemsBetweenIndexes = new List<T>();
bool betweenIndexes = false;
T temp = default(T);
foreach(var item in source)
{
if (!betweenIndexes)
{
if (index == index1)
{
temp = item;
betweenIndexes = true;
}
else
{
yield return item;
}
}
else
{
if (index == index2)
{
betweenIndexes = false;
yield return item;
foreach(var x in itemsBetweenIndexes)
{
yield return x;
}
itemsBetweenIndexes.Clear();
yield return temp;
}
else
{
itemsBetweenIndexes.Add(item);
}
}
index++;
}
}
最初,这会循环查找index1
处的项目,直到找到它为止。找到后,它会开始将项目添加到内部队列,直到找到index2
。此时,它会生成index2
处的项目,然后按顺序生成队列中的每个项目,然后是index1
处的项目。然后它返回查找index1
(它将找不到),直到它到达列表的末尾。
答案 2 :(得分:2)
为了在不多次迭代原件的情况下执行此操作,您需要在要交换的索引之间存储子序列的内容。
以下是如何实施该算法(我将index1
和index2
重命名为smallerIndex
和greaterIndex
):
using (IEnumerator<T> e = source.GetEnumerator()) {
IList<T> saved = new List<T>(greaterIndex-smallerIndex+1);
int index = 0;
while (e.MoveNext()) {
// If we're outside the swapped indexes, yield return the current element
if (index < smallerIndex || index > greaterIndex) {
index++;
yield return e.Current;
} else if (index == smallerIndex) {
var atSmaller = e.Current;
// Save all elements starting with the current one into a list;
// Continue until you find the last index, or exhaust the sequence.
while (index != greaterIndex && e.MoveNext()) {
saved.Add(e.Current);
index++;
}
// Make sure we're here because we got to the greaterIndex,
// not because we've exhausted the sequence
if (index == greaterIndex) {
// If we are OK, return the element at greaterIndex
yield return e.Current;
}
// Enumerate the saved items
for (int i = 0 ; i < saved.Count-1 ; i++) {
yield return saved[i];
}
// Finally, return the item at the smallerIndex
yield return atSmaller;
index++;
}
}
}
答案 3 :(得分:0)
您可以通过创建List<T>
并将其作为IEnumerable<T>
类型返回来一次性完成:
public static IEnumerable<T> Exchange<T>(this IEnumerable<T> source, int index1, int index2)
{
// TODO: check index1 and index2 are in bounds of List/Enumerable
var result = source.ToList(); // single enumeration
// Swap vars
var temp = result[index1];
result[index1] = result[index2];
result[index2] = temp;
return result;
}