2017-03-02 9 views
3

/* 編集:このコードは例であり、私の質問は一般的に(C#)コーディングに関連していることに注意してください。私は何かをループし、単にif文をチェックする必要はありません。 */ループ内で一度条件を満たすと条件を一旦満たしていない場合

これはまったく可能かどうかは疑問ですが、確かに知りたいと思います。

私はファイルを読み込んで変数を使用する必要があります。 項目はカンマで区切られた1行のtxtファイルにあります。私が入手したファイルには、\ tで区切られた項目とスペースで区切られた項目がありました。 例:リアルに今

private string RemoveTab (string text) 
{ 
    string tempText = ""; 
    foreach (char c in text) 
    { 
     if (c == ' ' && tempText == "") 
     { 
      //nothing has to be done 
     } 
     else if (c != '\t') 
     { 
      tempText += c; 
     } 
    } 
    return tempText; 
} 

:「\ tのボーイング737-800は」

これは私がタブ(とスペース)を取り除くために作られた機能がある(二つのスペースに注意してください)質問: すべてのタブとスペースがスキップされたら、もう何もチェックする必要はありません。特に最初のif文ではありません。最初のif文を無効にする方法はありますか?

この例では、パフォーマンスに重大な影響を及ぼすのではないかと考えています。まあ、私はそれがないことを知っています。しかし、私のi5-4570はOverwatchやBattlefield 4のようなゲームに問題があるので、可能な限り効率的なものを作りたいと思っています。また、要件を満たす可能性がないときに多くのことをチェックすると、CPUを大量に消費するアプリケーションではCPU使用率に多少の影響を与える可能性があります。

+5

すべての作業を完了したことを知ったときに戻ってくるのはなぜですか?確かに、このような文字列を構築すべきではありません( 'StringBuilder'を使用してください)、' TrimStart'があなたの望むことをするかもしれません... –

+0

この状況の提案をありがとう!私はStringBuilderのことと、それをどのように使用できるかを見ていきます。しかし、それは私が持っている質問に答えることはできません:) – Animiles

+1

あなたはボトルネックがどこにあるかを推測したり、何十億もの "常にXを行う"ルールを学習しても、良いパフォーマンスを達成することはできません。パフォーマンス*目標*を設定します。あなたは明確で分かりやすいコードを書く。パフォーマンスを測定し、目標を達成できない場合は、ボトルネックを特定し、その場所で代替案を評価し始めます。 –

答えて

3

ホイールを改造しないでください。先頭と末尾の空白を削除するには、string.Trim(' ')(または該当する場合はstring.TrimStartstring.TrimEnd)を使用します。

残りは、単にLINQを使用して行うことができます。

var noTabsString = new string(text.Trim(' ').Where(c => c != '\t').ToArray()); 

arbitrarly長い文字列を構築するために文字列連結を使用して、あなたがどのような場合StringBuilderに使用する必要があり、通常は良いアイデアではありません。

しかし、私はあなたのアプローチをすべて回避したいと思っています。 stringIEnumerable<char>を実装し、すべての不要な文字を除外したら、stringという1つのインスタンスをインスタンス化するという事実を利用してください。

UPDATE具体的な質問に答えると、ループ内でifの状態を無効にする方法はありません。あなたが行うことができる唯一のことは、ループを2つに分割することですが、検査している状態が容認できないボトルネックであることが証明されていない限り、私は真剣にそれをアドバイスしません。

using (var enumerator = sequence.GetEnumerator()) 
{ 
    while (enumerator.MoveNext()) 
    { 
     if (veryExpensiveCheck) { ... } 
     else if (cheapCheck) 
     { 
      ... 
      break; //veryExpensiveCheck not needed anymore. 
     } 
    } 

    while (enumerator.MoveNext()) 
    { 
     if (cheapCheck) { ... } 
    } 
} 

可読性がドレインダウンしたので、再び、その本当に必要な場合を除き、これを行うと、あなたがいることを証明する実験データを持っていない。それを行うには

一つの方法は、次のパターンになります最初のオプションは受け入れられません。

+0

この特定の状況での提案をありがとう、私は確かにStringBuilderを見ていきます。しかし、悲しいことに私の質問には答えません。私は自分の投稿を編集して(うまくいけば)私の質問が何であるかを明確にする。 – Animiles

+0

@Animilesアップデートを読む。 – InBetween

+0

コンパイラは文字列の 'foreach'を文字列の文字配列を通して索引をつける' for(;;) 'として最適化します。これは正規の' using'/'MoveNext()'/'Current'コンボよりも優れています配列の場合も同じです)。概念的な 'MoveNext()'ベースのコードを最適化する際、これは最適化を元に戻すので、これは 'IEnumerable 'でそのような分割ループを最適化する方法ですが、 (;;) '。 –

0

常に条件が満たされていない場合は、まず条件付き小切手をまとめることができます。

if (!(c == ' ' && tempText == "") && (c != '\t')){ 
    tempText += c; 
} 

これが役に立ちます。

+0

それは実際にそれをより短くします。私もそれをしたかったのです。しかし、私は(c == ''&tempText == "")を完全にチェックすることを止めることができたのだろうかと思います。 if文を無効にできれば、私は巨大なプログラムを構築しているときに、安全なCPUリソースを確保できます:) – Animiles

1

すべてのタブとスペースがスキップされたら、もう何もチェックする必要はありません。

最初の部分だけをスキップしたコードはどこにでもありますが、タブはどこにでもあります。あなたがそれを望んでいるのか、あなたがここに記述しているのかは明らかではありません。私はあなたが持っているコードがその結果に関して正しいと仮定しよう。

これは、ループの一部については何か他のものに対しては何かしたいという共通のパターンです。これは、簡単にそうforeachから糖衣構文を削除することによって行われています:

foreach (char c in text) 
{ 
    if (c == ' ' && tempText == "") 
    { 
     //nothing has to be done 
    } 
    else if (c != '\t') 
    { 
     tempText += c; 
    } 
} 

は次のようになります。

using(var en = text.GetEnumerator()) 
{ 
    while (en.MoveNext()) 
    { 
     char c = en.Current; 
     if (c == ' ' && tempText == "") 
     { 
      //nothing has to be done 
     } 
     else if (c != '\t') 
     { 
      tempText += c; 
     } 
    } 
} 

今、私たちはforeachを解体したことを、我々は簡単に十分な、さらにそれを変更することができます。

using(var en = text.GetEnumerator()) 
{ 
    while (en.MoveNext()) 
    { 
     char c = en.Current; 
     if (c != ' ') 
     { 
      do 
      { 
       if (c != '\t') 
       { 
        tempText += c; 
       } 
      } while (en.MoveNext()); 
      return tempText; 
     } 
    } 
    return ""; // only hit if text was all-spaces 
} 

は、今、私たちは唯一のcは、それが非スペースを見つけ、列挙の残りのループの異なる並べ替えを行う最初の時間までのスペースであるかどうかを確認するためにチェックをやっています。 (開始時にのみタブをスキップするつもりなら、内側のループから取り出して、最初のテストをc != ' ' && c != '\t'にしてください)。

(これは別の質問ですが、これと似たような変更が孤立していると考えられますが、入力文字列が非常に大きいか、コードが頻繁にヒットしない限り大事なことは、広範な応用の文脈の中でそれ自体は認められないものへのかなりの変化は、その広い文脈における大きな変化ではない)。

これは一般的なケースで、foreachに適用されます。ここではさらに2つのことができます。

文字列の列挙子がDispose()に何もしていないことがわかっているので、usingを削除することができます。もしあなたが本当にできることを確認してください。

もう1つは、foreachからiteratingに変更できます。

for(int i = 0; i < text.Length; ++i) 
{ 
    char c = text[i]; 
    if (c != ' ') 
    { 
     do 
     { 
      c = text[i]; 
      if (c != '\t') 
      { 
       tempText += c; 
      } 
     } while(++i < text.Length); 
     return tempText; 
    } 
    return ""; 
} 

または::

2の半分を行う両方のパターンです
int i = 0 
while(i < text.Length) 
{ 
    char c = text[i]; 
    if (c != ' ') 
    { 
     break; 
    } 
    ++i; 
} 
while(i < text.Length) 
{ 
    char c = text[i]; 
    if (c != '\t') 
    { 
     tempText += c; 
    } 
    ++i; 
} 

私たちが行うことができ、この出発点から

for(int i = 0; i < text.Length; ++i) 
{ 
    char c = text[i]; // We could also have done char[c] arr = text.Chars and used that. The compiler does the equivalent. 
    if (c == ' ' && tempText == "") 
    { 
     //nothing has to be done 
    } 
    else if (c != '\t') 
    { 
     tempText += c; 
    } 
} 

:我々はまた、として、あなたのオリジナルのロジックを記述することができることを考えてみましょう-loopsではなく、foreachではなくインデックスを使用します。多くの人がこれをより簡単に見つけることができます(特に配列や文字列の場合には、コンパイラはforeachを配列や文字列型の変数に標準MoveNext()/Currentのコンボではなくインデックスのfor(;;)にします)あなたはその最適化を失いたくない])一般的ではありませんが(インデックスに登録できないIEnumerable<char>では動作しません)

0

ループの本体を、必要な機能をカプセル化して実行時に変更できるAction<>オブジェクトに置き換えることができます。

private string RemoveTab(string text) 
{ 
    string tempText = String.Empty; 

    // An Action is an object that encapsulates a method that does 
    // not return a value. If you need to return something, use a Func<>. 

    // Create an Action<> that will be used in the loop until the initial 
    // condition has ceased. This Action<> will replace itself with the 
    // subsequent Action<>. 
    Action<char> Process = new Action<char>(c => 
    { 
     if ((c != ' ') && (c != '\t')) 
     { 
      tempText += c; 
      // Replace the Action with new functionality for subsequent 
      // iterations of the loop. 
      Process = new Action<char>(c1 => 
      { 
       if (c1 != '\t') 
       { 
        tempText += c1; 
       } 
      }); 
     } 
    }); 

    // Now the loop will use the Process Action<>, which will change 
    // itself to new behaviour once the initial condition no longer holds. 
    foreach (char c in text) 
    { 
     Process(c); 
    } 
    return tempText; 
} 

私はあなたがあなたの質問の例として使用するもののような単純な問題のために、このアプローチをお勧めしませんが、より複雑なシナリオのためにそれは考慮に値するかもしれません。しかし、将来誰かがコードを修正しなければならない可能性があり、あまりにも賢い人なら、あまりにも幸せにならないかもしれないと常に考えてください。

関連する問題