5

次のような状況のように、何かを 2 回行うための一般的なイディオムはありますか?

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

特にカウント以外では使用されないため、0 から 1 までカウントする新しいループ変数を導入するよりも明確な方法があると感じています。

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

Card私が作成したクラスであり、質問には関係ありません。)

4

5 に答える 5

10

fill_nおそらく、関数を使用したいでしょう<algorithm>

for ( int i = 0; i < num_pairs; i++ )
    std::fill_n(std::back_inserter(cards), 2, Card(i));
于 2012-05-22T19:29:03.867 に答える
6

個人的にはそのままでいいと思います。きれいに見え、理解しやすく、非常に読みやすいです。なぜあなたがそれを2回行うのかについてコメントを残してください(しかし、あなたは何があってもそれをするでしょう).

于 2012-05-22T19:29:49.483 に答える
5

いくつか提案があります。私の推奨事項については、最後を参照してください。

  1. 私の意見では、ビート:insert(it, N, value)std::fill_n

    for ( int i = 0; i < num_pairs; i++ ) {
        cards.insert(cards.end(), 2, Card(i) );
    }
    
  2. 順序が重要でない場合は、カードを一度だけ生成し、事後に複製することができます

    std::copy(cards.begin(), cards.end(), std::back_inserter(cards));
    
  3. 汚いラムダと整数除算のトリックを使用します。警告には、時期尚早の最適化の特徴があります。正当な理由もなく可読性が低下します。

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

    Card(はdefault-constructibleであると仮定します。そうでない場合は、 を使用しますcards.back_inserter())

  4. 私のおすすめ

    以下は、パフォーマンスと意図の表現の両方で勝ちます。

    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);
    }
    
于 2012-05-22T21:10:13.560 に答える
1

別のループを使用すると2回の反復のみがパフォーマンスに悪いため、内側のループをステートメントに展開することについては正しいです。が頻繁jumpsに発生するため、2 つ目のループ (ネストされたループ) の実行は遅くなります。顕著な違いがあるかどうかは、完全に に依存しますnum_pairs

したがって、最初のものはパフォーマンスが優れています(ただし、ゲインはわずかです)。この種の拡張ループはloop unwinding/unrolling、コンパイラの用語で呼ばれます。ただし、この用語はプログラミングレベルでは使用されません。通常、コンパイラーのみがコードを高速化するために使用するためです。プログラマーがより優れた効率的なコードを記述し、言語をよりよく理解するのに役立つIdiomsと確信しています。design notions

于 2012-05-22T19:45:37.717 に答える
1

これを非常に頻繁に行いたい場合は、ちょっとしたユーティリティ関数を書きます:

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

Ideoneで例を書きました。


あなたの最初のアプローチは、コードの将来の読者を混乱させる可能性があります。彼らは、コードが偶然に 2 回あったと考えるかもしれません。このような関数を使用すると、この混乱を避けることができます。

コンパイラーは関数をインライン化し、小さなループを完全に最適化する可能性が高いため、パフォーマンスへの影響は非常に小さくなりますtimes。パフォーマンスが気になる場合は、まずベンチマークを行ってください。

于 2012-05-22T19:46:26.807 に答える