C # указатели, итераторы и дженерики - PullRequest
5 голосов
/ 05 февраля 2012

Я сильно озадачен

Как я могу использовать итератор в C # как итератор C ++? Я не могу найти аксессор Begin () или End (), я даже не могу узнать, как объявить итератор. Я читал об Иенумераторе. Моя цель - реализовать функцию слияния. Вот часть моей функции Merge, написанной на C ++. В основном я ищу C # эквивалент того, что показано, за исключением того, что я буду использовать ссылочный тип, а не целые числа.

void merge(vector<int>::iterator left, vector<int>::iterator right, vector<int>::iterator      leftEnd, vector<int>::iterator rightEnd, vector<int>::iterator full)
{

    while(left != leftEnd && right!= rightEnd) //compare left and right until the end of the vector is reached
    {
        if(*right < *left)      //right < left so insert right to the output vector and advance the iterators
        {
            *full++ = *right++;
        }
        else                   //left < right so insert left to the output vector and advance the iterators
        {
            *full++ = *left++;
        }
    }

    while(left != leftEnd)    //copy any remaining elements into the output from left 
    {
        *full++ = *left++;
    }
}

Кроме того, какие коллекции я должен использовать? (в настоящее время я пытаюсь List<T> и LinkedList<T>).

Ответы [ 4 ]

7 голосов
/ 05 февраля 2012

Звучит так, будто вы хотите что-то вроде:

bool leftValid = left.MoveNext();
bool rightValid = right.MoveNext();

while (leftValid && rightValid)
{
    if (right.Current < left.Current)
    {
        full.Add(right.Current);
        rightValid = right.MoveNext();
    }
    else
    {
        full.Add(left.Current);
        leftValid = left.MoveNext();
    }
}

while (leftValid)
{
    full.Add(left.Current);
    leftValid = left.MoveNext();    
}

while (rightValid)
{
    full.Add(right.Current);
    rightValid = right.MoveNext();    
}

Здесь full должно быть чем-то вроде IList<T> - .NET-итераторы не позволяют вносить изменения в базовую коллекцию.

Вы не должны пытаться написать "связующий" код, чтобы позволить вам использовать .NET-итераторы, такие как C ++; гораздо лучше попытаться начать думать с точки зрения итераторов .NET, когда вы используете .NET.

Обратите внимание, что в .NET очень редко передаются итераторы. Было бы более естественно настроить ваш метод на IEnumerable<T> параметры и сделать что-то вроде:

using (IEnumerable<T> leftIterator = leftSequence.GetEnumerator())
{
    using (IEnumerable<T> rightIterator = rightSequence.GetEnumerator())
    {
        // Code as above, just using leftIterator and rightIterator
        // instead of left and right
    }
}
3 голосов
/ 05 февраля 2012

.net контейнеры не поддерживают итераторы в стиле C ++. Единственное, что у них есть, это

  • простой прямой итератор с именем IEnumerator<T>
  • , который не может изменить коллекцию
  • не случайный доступ
  • не может быть скопировано (некоторые коллекции имеют итераторы типов значений, которые можно копировать, но это непростая задача и редко используется)
  • и в большинстве коллекций также становится недействительным при каждом изменении коллекции

Практически единственное, что они могут сделать, - это повторяться в операторе foreach.


Возможно, вы захотите взглянуть на интерфейс IList<T>, который допускает произвольный доступ, но поддерживается только в коллекциях, которые поддерживают быструю индексацию. В такой коллекции вы можете реализовать сортировку слиянием на месте, используя индексы.

void Merge<T>(IList<T> container,int left, int right, int leftEnd, int rightEnd, int full)

и затем используйте container[left] вместо *left.


Печальным следствием этого является то, что вы не можете реализовать эффективную функцию независимой сортировки контейнеров на месте, как в C ++.

2 голосов
/ 05 февраля 2012

Я думаю, что вы хотите GetEnumerator (), MoveNext () и Current.

Обычно вы можете просто использовать foreach для итерации, но ваш случай особенный.

Если факт, скореечем использовать "full", организуйте это как блок итератора и лениво объединяйте два перечислимых.

IEnumerable<T> Merge<T>(IEnumerable<T> left, IEnumerable<T> right)
{
    ... yield return Min<T>(left.Current, right.Current); ..,
}
0 голосов
/ 05 февраля 2012

Вы можете использовать массивы фиксированного размера или List<T>, которые на других языках также называются ArrayLists.К их элементам можно получить доступ через индексатор (list[i]), а к элементам можно добавить list.Add(item);.Они растут автоматически.LinkedLists не может быть доступен через индексатор и должен быть пройден.

Вы можете объявить метод следующим образом

void merge(IEnumerator<int> left, IEnumerator<int> right, 
           List<int> full)
{
    // Jon Skeet's code goes here
}

Вы можете получить перечислитель как этот

IEnumerable<int> intEnumerable = ...;
IEnumerator<int> intEnumerator = intEnumerable.GetEnumerator();

IEnumerable<T> реализуется большинством типов коллекций.Не универсальные коллекции обычно реализуют IEnumerable.

(отредактировано в ответ на комментарий @ CodeInChaos).

...