2012-04-05 8 views
5

En primer lugar, se revisan los iteradores C++ estilo quickly.for ejemplo:cómo hacer iteradores de python más expresivos? al igual que C++ iterador

//--- Iterating over vector with iterator. 
vector<int> v; 
. . . 
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) { 
    cout << *it << endl; 
} 

Es flexible. Es fácil cambiar los tipos de contenedores subyacentes. Por ejemplo, puede decidir más adelante que la cantidad de inserciones y eliminaciones es tan alta que una lista sería más eficiente que un vector. También tiene muchas funciones útiles para miembros. Muchas de las funciones miembro para vectores utilizan iteradores, por ejemplo, asignar, insertar o borrar. Además, podemos usar iterator (si se admite) bidireccionalmente, como ++, -. Esto es útil para analizar una secuencia como objetos.

Los problemas de python son: 1: Actualmente, la sintaxis de pitón para bucle es menos flexible que C++ para. (bueno, más seguro) 2: en lugar del estilo "it! = iter.end()", python lanzará una excepción cuando next() no tenga más. No es flexible.

Pregunta 1: ¿Es correcta mi idea anterior?

OK. Aquí viene mi pregunta, ¿cómo implementar un iterador python más potente tan poderoso como los iteradores de C++? Actualmente, la sintaxis de pitón para bucle es menos flexible que C++ para. También encuentro algunas soluciones posibles, como http://www.velocityreviews.com/forums/t684406-pushback-iterator.html. pero le pide al usuario que repliegue algo en lugar de preguntar al iterador -.

Pregunta 2: ¿Cuál es la mejor manera de implementar un iterador bidireccional en python? Al igual que http://www.cplusplus.com/reference/std/iterator/BidirectionalIterator/. El pseudo-código es la siguiente:

it = v.begin(); 
while(it!=v.end()) { 
    //do sth here 

    if (condition1) 
     ++it;//suppose this iterator supports ++ 
    if(condition2) 
     --it;//suppose this iterator supports -- 
} 

Las características principales son: 1) bidireccional, 2) "extremo" más simple de cheques. Los operadores "++" o "-" o las funciones comunes no importan (de todos modos, no tiene diferencia semántica).

Gracias,

Actualización: Tengo algunas soluciones posibles a partir de las respuestas:

i = 0 
while i < len(sequence): # or i < len and some_other_condition 
    star_it = sequence[i] 
    if condition_one(star_it): 
     i += 1 
    if condition_two(star_it): 
     i = max(i - 1, 0) 

Sin embargo, a diferencia de la matriz, de acceso aleatorio de la lista debe ser O (n). Supongo que el objeto "list" en python internamente se implementa usando cosas de la lista vinculada. Por lo tanto, esta solución while loop no es eficiente. Sin embargo, en C++, tenemos "iterador aleatorio", "iterador bidireccional". ¿Cómo debería obtener una mejor solución? Gracias.

+1

¿Puedes dar un ejemplo concreto de algo que no puedes hacer en 'Python' que puedes hacer fácilmente en' C++'? –

+0

El mecanismo de excepción de Python 'yield' + es sorprendentemente flexible (es lo que llamamos" continuaciones "en la programación funcional). Aprende a usarlos correctamente y serás recompensado. Es mucho más fácil definir iteradores compuestos en Python que en C++. –

+0

Esta pregunta es demasiado vaga, y no hay una respuesta real a la pregunta en el formulario actual. –

Respuesta

0

En realidad, el sistema iterador de C++ no es tan bueno. Los iteradores son similares a los punteros, y tienen sus problemas:

  • valores singulares: v.end() no puede dejar de hacer referencia de forma segura
  • cuestiones de inversión: std::for_each(end, begin, func);
  • problemas de desajuste: std::for_each(v0.begin(), v2.end(), func);

enfoque Python es mucho mejor en este sentido (aunque el uso de la excepción puede ser bastante sorprendente al principio, realmente ayuda a definir los iteradores anidados), porque, al contrario de su nombre, un iterador de Python es más parecido a un Range.

El concepto de Range es mucho mejor que C++ 11 introduce el rango-para construcción de lazo:

for (Object& o: range) { 
} 

Todo lo que es posible con un iterador es también posible con una amplia gama, aunque esto puede tomar algunas veces para darse cuenta y algunas traducciones parecen surrealistas al principio para aquellos de nosotros que fuimos educados con iteradores tipo puntero C++. Por ejemplo, los subintervalos perfectamente pueden expresarse:

for (Object& o: slice(range, 2, 9)) { 
} 

donde slice llevaría todos los elementos en posición [2, 9) dentro range.

Por lo tanto, en lugar de luchar contra tu lenguaje (Python) deberías profundizar más en él y adoptar su estilo. Luchar contra un idioma es generalmente una batalla perdida, aprender sus modismos, volverse eficiente.

+0

En pocas palabras, solo necesito un iterador bidireccional. Ya sea que tengamos tanta azúcar gramatical, parece fácil, pero no tan estúpida y transparente en C++. –

5

Para la mayoría de las situaciones, Python's for y los iteradores son lo más simple posible. Ese es su objetivo y no deberían comprometerlo por flexibilidad: su falta de flexibilidad no es un problema.

Para algunas situaciones en las que no se podía usar un bucle for, los iteradores de C++ podrían ser más simples. Pero siempre hay una manera de hacerlo en Python que no es mucho más complejo que usar un iterador de C++.


Si necesita separar el avance del repetidor de bucle, sólo tiene que utilizar un bucle while:

it = iter(obj) 

try: 
    while True: # or some secondary break condition other than StopIteration 
     star_it = next(it) 
     if condition_one(star_it): 
      star_it = next(it) 
except StopIteration: 
    pass # exhausted the iterator 

puedo pensar en sólo dos situaciones en las que --it tiene sentido en Python.

Lo primero es que está iterando sobre una secuencia. En ese caso, si tiene que ir hacia atrás, no use un iterador en absoluto - sólo tiene que utilizar un contador con un bucle de while:

i = 0 
while i < len(sequence): # or i < len and some_other_condition 
    star_it = sequence[i] 
    if condition_one(star_it): 
     i += 1 
    if condition_two(star_it): 
     i = max(i - 1, 0) 

La segunda es que si estás interactuando sobre una lista doblemente enlazada . En ese caso, una vez más, no use un iterador - acaba de atravesar los nodos normalmente:

current = node 
while current: # or any break condition 
    if condition_one(current): 
     current = current.next 
    if condition_two(star_it): 
     current = current.prev 

Una situación en la que podría creo que tiene sentido, pero no se puede utilizar cualquiera de los métodos anteriores , es con una colección desordenada como set o dict. Sin embargo, --itno tiene sentido en ese caso. Como la colección no está ordenada, semánticamente, cualquiera de los elementos previamente alcanzados sería apropiado, no solo el elemento anterior real.

Entonces, para conocer el objeto correcto al que volver, necesita memoria, ya sea iterando sobre una secuencia como mydict.values() o tuple(myset) y usando un contador, o ensamblando una secuencia de valores previos a medida que avanza y usando un bucle while y next como el anterior en lugar de un bucle for.

+1

@XinlinCao Pero las soluciones adaptadas a esas situaciones son más fáciles de usar que las genéricas, especialmente cuando la genérica debería ser más compleja para otras situaciones. Python tiene herramientas para analizar. En cuanto a los archivos, no veo cómo un iterador es mejor que simplemente llamar a 'seek'? – agf

+2

@XinlinCao: sus casos de uso son demasiado vagos. Proporcione un caso de uso concreto del mundo real, y podemos mostrarle una buena solución en Python. –

+0

@XinlinCao Estamos tratando de decirte __ esa es la pregunta incorrecta__. Hay mejores formas en Python de hacer lo que harías con un iterador bidireccional en C++. – agf

0

podría implementar una forma similar de C++ utilizando objetos de Python:

class Iterable(object): 
    class Iterator(object): 
    def __init__(self, father, pos=0): 
     self.father = father 
     self.pos = pos 

    def __getitem__(self, pos=0): 
     return self.father[self.pos + pos] 

    def __setitem__(self, pos, value): 
     self.father[self.pos + pos] = value 

    def __iadd__(self, increment): 
     self.pos += increment 
     return self 

    def __isub__(self, decrement): 
     self.pos -= decrement 
     return self 

    def __ne__(self, other): 
     return self.father != other.father or self.pos != other.pos 

    def __eq__(self, other): 
     return not (self != other) 

    def begin(self): 
    return self.Iterator(self) 

    def end(self): 
    return self.Iterator(self, len(self)) 

class Vector(list, Iterable): 
    pass 

v = Vector([54, 43, 32, 21]) 

counter = 0 
it = v.begin() 
print it, it[0] 
while it != v.end(): 
    counter += 1 
    print it[0] 
    if counter == 2: 
    it += 1; # suppose this iterator supports ++ 
    if counter == 1: 
    it -= 1; # suppose this iterator supports -- 
    it += 1 

Esto reemplaza *it por it[0] (también analógico a C++) y it++ por it += 1, pero en efecto se mantiene más o menos la misma.

dejas las formas Pythonic si lo hace, aunque ;-)

1

Soluciones para algunas situaciones se mencionan:

  1. desea reemplazar los objetos en el contenedor subyacente. Para los diccionarios, iterar sobre las claves o elementos, no sólo los valores:

    for key, value in my_dict.iteritems(): 
        if conditiion(value): 
         my_dict[key] = new_value 
    

    para usar listas enumerate():

    for index, item in enumerate(my_list): 
        if condition(item): 
         my_list[index] = new_item 
    
  2. ¿Quieres un iterador con un valor de "look-ahead". Probablemente usaría algo adaptado a una situación específica, pero aquí es una receta para situaciones generales:

    def iter_with look_ahead(iterable, sentinel=None): 
        iterable, it_ahead = itertools.tee(iterable) 
        next(it_ahead, None) 
        return izip_longest(iterable, it_ahead, fillvalue=sentinel) 
    
    for current, look_ahead in iter_with look_ahead(tokens): 
        # whatever 
    
  3. usted quiere recorrer en sentido inverso. Use reversed() para los contenedores que lo soportan.

  4. Quiere acceso aleatorio. Basta con activar su iterable en una lista y uso de índices:

    my_list = list(my_iterable) 
    
0

Tenga en cuenta que el objeto de lista en Python es una matriz, por lo que la preocupación por la eficiencia mencionado en la pregunta es en realidad un no-tema.

+1

Esto debería estar en la sección de comentarios en lugar de la solución. – DaveyLaser

Cuestiones relacionadas