Modismo para hacer algo dos veces en C++

¿Existe un modismo común para hacer algo dos veces, como en la siguiente situación?

    for ( int i = 0; i < num_pairs; i++ ) {
        cards.push_back( Card(i) );
        cards.push_back( Card(i) );
    }

Tengo la sensación de que hay una manera más clara que la introducción de una nueva variable de ciclo que cuenta de 0 a 1, especialmente porque no se usa excepto para contar.

    for ( int i = 0; i < num_pairs; i++ )
        for ( int j = 0; j < 2; j++ )
            cards.push_back( Card(i) );

(Card es solo una clase que inventé y no es relevante para la pregunta).

preguntado el 22 de mayo de 12 a las 20:05

¿A qué te refieres exactamente con idioma? ¿Qué tipo de respuesta estás buscando? -

legibilidad y mantenibilidad del valor -

No veo nada malo con el primer ejemplo. Los modismos son útiles para cosas más complejas y están destinados a hacer que los programas sean más comprensibles y correctos. Lo que estás preguntando es algo bastante sencillo, no es necesario que haya un modismo para ello. -

Si tuviera un vector o algo así, le recomendaría que lo use std::for_each(start, end, []() {cards.push_back(Card(i));}); -

Tu puedes decir for (int dummy : { 0, 1 }) { /* do stuff */ }. -

5 Respuestas

Probablemente quieras usar el fill_n Funcionar en <algorithm>

for ( int i = 0; i < num_pairs; i++ )
    std::fill_n(std::back_inserter(cards), 2, Card(i));

contestado el 22 de mayo de 12 a las 20:05

¿Llamaría esto al constructor de la Tarjeta una o dos veces? - Andreas

Entonces, desafortunadamente, no sería equivalente al código que publiqué. - Andreas

@Andreas Llamaría a Card(int) (o lo que sea que sea esa firma) una vez, y probablemente llamaría al constructor de copias dos veces. - bames53

Personalmente, lo dejaría como está. Se ve limpio, es fácil de entender y es bastante legible. Solo deja un comentario mencionando por qué lo haces dos veces (pero lo harías sin importar qué).

contestado el 22 de mayo de 12 a las 20:05

Tengo algunas sugerencias. Ver el pasado por mi recomendación:

  1. en mi opinión insert(it, N, value) latidos std::fill_n:

    for ( int i = 0; i < num_pairs; i++ ) {
        cards.insert(cards.end(), 2, Card(i) );
    }
    
  2. Si el orden no es importante, puede generar las tarjetas una vez y duplicarlas. después de el hecho

    std::copy(cards.begin(), cards.end(), std::back_inserter(cards));
    
  3. Usando un truco sucio de lambda y división de enteros. advertencia esto tiene el sello de una optimización prematura: reduce la legibilidad sin una buena razón.

    std::vector<Card> cards(num_pairs * 2);
    int i = 0;
    std::generate_n(cards.begin(), num_pairs, [&i] () { return Card(i++/2); });
    

    (asume Card is construible por defecto. Si no, usa cards.back_inserter())

  4. Mi recomendación

    Lo siguiente gana tanto en desempeño como en expresión de intención:

    std::vector<Card> cards;
    cards.reserve(num_pairs*2);
    for (int i = 0; i < num_pairs; ++i)
    {
        cards.emplace_back(i);
        cards.emplace_back(i);
    }
    

contestado el 22 de mayo de 12 a las 22:05

Tiene razón al usar la expansión del bucle interno en declaraciones, ya que usar otro bucle solo 2 iteraciones será malo para el rendimiento. Debido a los frecuentes jumps, el segundo (bucles anidados) se ejecutará lentamente. Si habrá alguna diferencia notable depende completamente de la num_pairs.

Entonces, el primero es mejor para el rendimiento (por marginal que sea la ganancia). Este tipo de bucles en expansión se llama loop unwinding/unrolling en la terminología del compilador. Sin embargo, este término no se usa a nivel de programación, ya que generalmente solo el compilador lo hace para hacer que el código sea más rápido. Idioms están seguros design notions que ayudan a los programadores a escribir un código mejor y más eficiente y a entender mejor el lenguaje.

contestado el 22 de mayo de 12 a las 20:05

Si desea hacer esto con mucha frecuencia, escriba una pequeña función de utilidad:

template<class F>
void repeat(unsigned times, F callback) {
  while (times--) callback();
}

// ...

for (int i = 0; i < num_pairs; i++) {
  repeat(2, [&] { cards.push_back(Card(i)); });
}

Escribí un ejemplo en ideone.


Su primer enfoque puede resultar confuso para los futuros lectores de su código. Podrían pensar que el código estuvo allí dos veces por accidente. El uso de una función como esta evita esta confusión.

El impacto en el rendimiento sería muy mínimo, incluso > 0, ya que es probable que el compilador alinee la función y optimice completamente el ciclo para pequeños times'. Si le preocupa el rendimiento, primero haga una evaluación comparativa.

contestado el 22 de mayo de 12 a las 21:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.