如何仅通过一次定向来交换枚举项目?[英] How to exchange the items of enumeration by interating only once?

本文是小编为大家收集整理的关于如何仅通过一次定向来交换枚举项目?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我正在尝试交换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也可能增加迭代时间.

推荐答案

wolog假设index1小于index2.

制作自己的枚举者;不要使用foreach.

对于最高index1的元素,正常迭代并屈服.

然后,当您击中index1时,分配一个足够大的数组以将元素保存在index1到index2之间 - 也就是说,包括index1 element,但不能index2元素.

使用枚举器将元素读取到该数组中.

现在读取index2 the元素并产生.

现在,您的枚举器已设置为index2以上的一个.

现在在数组中产生所有内容 index1 th元素.

然后产生index1 th元素.

然后正常产生其余元素.

完成后,请不要忘记在枚举器上打电话Dispose

其他推荐答案

最简单的方法可能是这样:

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(不会找到),直到它到达列表的尽头.

其他推荐答案

为了执行此操作而无需多次迭代原始操作,您需要存储您要交换的索引之间的子序列的内容.

这是您可以实现算法的方法(我将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++;
        }
    }
}

inideOne on Ideone .

本文地址:https://www.itbaoku.cn/post/1557196.html

问题描述

I'm trying to exchange the ordering of particular items of an IEnumerable.

Given an IEnumerable<int> a; of the elements:

1, 2, 3, 4, 5

and what I want to do is write a exchange iterator which resulting a.Exchange(1, 2) in:

1, 3, 2, 4, 5

But I don't want the enumerable be iterated more than once with this simple purpose. What I have so far is:

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;
    }
}

Here's a assumption that index1 and index2 wouldn't exceed the elements of the enumerable. The code done the work of exchange(the ordering) in most cases, but it does iterate more than once. Note index1 and index2 might not be the real indices of source, they would be the Mth and Nth element when the enumeration occurs.

ToArray or ToList may also increase the times of iteration.

推荐答案

WOLOG assume that index1 is less than index2.

Make your own enumerator; don't use foreach.

For elements up to index1, iterate normally and yield each.

Then when you hit index1, allocate an array big enough to hold the elements between index1 through index2 -- that is, including the index1th element, but not the index2th element.

Read the elements into that array using your enumerator.

Now read the index2th element and yield it.

Your enumerator is now set to one beyond index2.

Now yield everything in the array except the index1th element.

Then yield the index1th element.

Then yield the rest of the elements normally.

Don't forget to call Dispose on the enumerator when you're done.

其他推荐答案

The easiest way is probably something like this:

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 }

To do it without an OrderBy, I think it would look something like this:

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++;
    }
}

Initially, this loops through looking for the item at index1 yielding every item until it finds it. Once found it begins adding items to an internal queue until it finds index2. At that point it yields the item at index2, followed by every item in the queue, in order, followed by the item at index1. It then goes back to looking for index1 (which it won't find) until it reaches the end of the list.

其他推荐答案

In order to perform this operation without iterating the original more than once, you would need to store the content of the subsequence between the indexes that you are swapping.

Here is how you can implement the algorithm (I renamed index1 and index2 to smallerIndex and 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++;
        }
    }
}

Demo on ideone.