2012-02-05 19 views
5

Stumped en gran medidaC# punteros, iteradores y genéricos

¿Cómo puedo utilizar un iterador en C# como un iterador C++? No puedo encontrar un acceso Begin() o End(), ni siquiera puedo averiguar cómo declarar un iterador. He leído sobre el Ienumerator. Mi objetivo es implementar la función de fusión. Aquí hay una parte de mi función Merge escrita en C++. Sobre todo, estoy buscando el equivalente de C# de lo que se muestra, excepto que usaré un tipo de referencia en lugar de números enteros.

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

Además, ¿qué colección (es) debo usar? (Actualmente he estado intentando List<T> y LinkedList<T>).

+0

¿No está implementando nuevamente el método [Enumerable.Concat (Of TSource)] (http://msdn.microsoft.com/en-us/library/bb302894.aspx)? –

+0

@Serge no, esta es la parte de combinación del tipo de fusión. Tenga en cuenta 'if (* right <* left)' – CodesInChaos

Respuesta

7

Parece que usted quiere algo como:

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

Aquí full tendría que haber algún tipo de IList<T> - iteradores .NET no permiten que se realizan cambios en la colección subyacente.

no debería intentar escribir el código "puente" para permitirle usar iteradores .NET como C++; es mucho mejor tratar de comenzar a pensar en términos de los iteradores .NET cuando usa .NET.

Tenga en cuenta que es bastante raro pasar iteradores en .NET. Sería más natural para que su método para IEnumerable<T> parámetros, y hacer algo como:

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

Y tenga en cuenta que 'full' no puede ser un iterador ya que los enumeradores de .NET son de solo lectura. En su lugar, tendrá que pasar algo como 'IList'. –

+0

@MattiVirkkunen: De hecho, notará eso. –

+0

@CodeInChaos: Lo siento, sí, aclararé un poco sobre la inmutabilidad. No estoy seguro de lo que quiere decir con que .NET no admite la creación de copias de los iteradores; puede crear un método que tome dos 'IEnumerator ' sy funcionaría bien. Tendría más sentido tomar 'IEnumerable ', fíjate. Voy a editar por eso. –

2

Creo que quieres GetEnumerator(), MoveNext(), y el actual.

Normalmente, puede usar foreach para repetir, pero su caso es especial.

Si es así, en lugar de usar "lleno", organícelo como un bloque de iteración y combine dos enumerables de forma perezosa.

IEnumerable<T> Merge<T>(IEnumerable<T> left, IEnumerable<T> right) 
{ 
    ... yield return Min<T>(left.Current, right.Current); .., 
} 
3

.net contenedores no son compatibles con los iteradores de estilo C++. Lo único que tienen es una

  • sencilla iterador hacia adelante denominado IEnumerator<T>
  • que no puede modificar la colección
  • no es de acceso aleatorio
  • no se pueden copiar (algunas colecciones tienen tipo de valor iteradores los cuales se pueden copiar, pero eso es un asunto complicado y rara vez se utiliza)
  • y en la mayoría de las colecciones también obtiene invalidada siempre que modifique la colección

Casi todo lo que pueden hacer es repetir en una declaración foreach.


Es posible que desee ver en la interfaz IList<T> que permite acceso aleatorio, pero sólo se admite en colecciones que apoyan una rápida indexación. En una colección de este tipo, puede implementar el tipo de fusión en contexto mediante el uso de índices.

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

y luego usar container[left] en lugar de *left.


consecuencia desafortunada de esto es, que no se puede implementar una eficiente función de clasificación de contenedores agnóstico en el lugar como C++ tiene.

+0

Si definimos el iterador como un identificador agnóstico de contenedor conveniente para una colección que mantiene la posición actual y habilita la navegación, el único tipo de iterador que admite .NET es el iterador de solo lectura directa. Lo cual es una pena Hay muchos usos para iteradores bidireccionales o de acceso aleatorio en muchos algoritmos no triviales. –

0

Puede usar matrices, que tienen un tamaño fijo, o List<T>, que también se denominan ArrayLists en otros idiomas. Se puede acceder a sus elementos a través de un indexador (list[i]) y los artículos se pueden agregar al list.Add(item);. Crecen automáticamente LinkedLists no se puede acceder a través de un indexador y debe atravesarse.

Usted sería declarar el método como este

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

Usted puede obtener un enumerador como esto

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

IEnumerable<T> es implementado por la mayoría de los tipos de colección genéricos. Las colecciones no genéricas generalmente implementan IEnumerable.

(Editado en respuesta al comentario de @ CodeInChaos).

+0

No hay ningún enumerable que pueda pasar a 'left' /' right'. Esas son copias de iteradores parcialmente iterados. – CodesInChaos

+0

No creo que su edición resuelva el problema. Esos iteradores probablemente apuntan a diferentes elementos en el mismo contenedor. Piense en ellos como índices en una lista, pero trabajando en colecciones que no admiten indexación rápida. – CodesInChaos

+0

Un 'IEnumerator ' (no 'IEnumerable '!) ** es ** un tipo de índice en una lista. 'left' y' right' pueden señalar diferentes ubicaciones en la misma colección ahora. (Cambié su tipo de 'IEnumerable ' a 'IEnumerator ') –