1

これは少し奇妙な質問であり、私が必要とするものは何でも実験していますが、私はまだ答えに興味があります.繰り返し部分の場合、文字列を単一の文字列オブジェクトとして保持し、必要に応じて呼び出され、それを処理する方がよいでしょうか。または、文字列を繰り返し部分を表す小さな文字列に分割し、必要に応じて連結する必要があります。 ?

例を挙げてみましょう: IP アドレスを検証するための正規表現を作成したい素朴なプログラマーがいるとしましょう (つまり、この正規表現が意図したとおりに機能しないことはわかっていますが、繰り返しの意味を示すのに役立ちます)。例の 2 番目の部分の入力を少し節約できます)。したがって、彼はこの関数を次のように記述します。

 private bool isValidIP(string ip)
 {
   Regex checkIP = new Regex("\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?");
   return checkIP.IsMatch(ip);
 }

ここで、若いプログラマーは、"\d"、"\d?"、および "\" があることに気付きました。数回繰り返すだけです。これにより、ストレージ スペースを節約し、これが後で何を意味するかを思い出すことができるという考えが彼に与えられます。そこで彼は関数を作り直します:

 private bool isValidIP(string ip)
 {
   string escape = "\\";
   string digi = "d";
   string digit = escape + digi;
   string possibleDigit = digit + '?';
   string IpByte = digit + possibleDigit + possibleDigit;
   string period = escape + '.';
   Regex checkIP = new Regex(IpByte + period + IpByte + period + IpByte + period + IpByte);
   return checkIP.IsMatch(ip);
 }

最初の方法は簡単です。プログラムの命令に38文字を格納するだけで、関数が呼び出されるたびにメモリに読み込まれます。2 番目のメソッドは、2 つの長さ 1 の文字列と 2 つの文字をプログラムの命令に格納する (私が推測する) だけでなく、これら 4 つを異なる順序に連結するためのすべての呼び出しも格納します。これにより、プログラムが呼び出されたときにメモリ内に少なくとも 8 つの文字列が作成されます (6 つの名前付き文字列、正規表現の最初の 4 つの部分の一時的な文字列、および前の文字列 + 正規表現の 3 つの文字列から作成された最終的な文字列)。この 2 番目の方法は、たまたま正規表現が探しているものを説明するのにも役立ちますが、最終的な正規表現がどのようになるかはわかりません。また、リファクタリングにも役立つ可能性があります。

繰り返しますが、どの方法が良いでしょうか?プログラムサイズとメモリ使用量のトレードオフと同じくらい簡単でしょうか? もちろん、このような単純なものでは、トレードオフはせいぜい無視できる程度ですが、もっと大きくて複雑な文字列の場合はどうでしょうか?

ああ、そうです。IP アドレスのより優れた正規表現は次のようになります。

 ^(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)(\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)){3}$

例としてうまく機能しないでしょうか?

4

4 に答える 4

3

最初のオプションは、はるかに優れたオプションです。これが理由です

  1. より明確です。

  2. それは安価です。新しいオブジェクトを宣言するときはいつでも、それは「高価な」プロセスです。ヒープ上にそのためのスペースを確保する必要があります (少なくとも文字列用)。はい、理論的には 1 バイトほど節約できますが、各文字列、追加のメモリ命令などにスペースを割り当てたり割り当てたりするのにもっと多くの時間を費やします (おそらく、私はそれをテストしていません)。 GC の使用も考慮する必要があることを忘れないでください。文字列を割り当て続けると、最終的にはプロセスティックも占有することに対処する必要があります。あなたは本当に最適化をしたいのですが、このコードがそれほど効率的ではないことは簡単にわかります。1 つには定数がありません。つまり、コンパイラに最適化されていない文字列を最適化させるのではなく、必要以上のオブジェクトを作成している可能性があります。変更する必要はありません。これは、このコードをレビューする人として、何が起こっているのかを見て、何かが間違っているかどうかを理解するために、何が起こっているのかをもっと詳しく調べる必要があると思います.

  3. より明確です(はい、これはもう一度言いました)。アカデミックな追求を行い、どれだけ効率的にできるかを確認したいと考えています。カッコいい。わかりました。私は自分でやります。楽しいです。私はそれを製品コードに落とし込むことは決してありません。ティックを失うことは気にしません。本番環境にバグがあることは気にします。他のプログラマーが私のコードの動作を理解できるかどうかも気にします。他の誰かのコードを読むのは十分に難しいです。私が入れたマイクロ最適化と、間違ったコードを「微調整」した場合に何が起こるかを理解しようとする必要があるという余分なタスクを追加したくありません。

  4. あなたは別のポイントに当たります。元の正規表現が間違っている場合はどうなりますか。Google は、この問題が解決されたことを通知します。正しく、テスト済みの別の正規表現を Google で検索できます。「コードの何が問題なのか」を Google で検索することはできません。あなたは確かにそれを投稿することができますが、それは他の誰かが関与してそれを見なければならないことを意味します.

最初の例で簡単に競馬に勝つ方法を次に示します。

 Regex checkIP = new Regex(
   "\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?");

 private bool isValidIP(string ip)
 {
   return checkIP.IsMatch(ip);
 }

一度宣言すれば、何度でも再利用できます。正規表現を動的に再作成していくつかを保存するのに時間をかけている場合は、そうしないでください。技術的には、オブジェクトを 1 回だけ作成することもできますが、それはクラス レベルの変数に移動するよりもはるかに多くの作業です。

于 2012-04-04T22:10:30.660 に答える
2

ここで効果的にコンパイラを操作し、独自の文字列圧縮を実装しようとしています。あなたが説明している種類の文字列リテラルについては、コンパイルされたバイナリから数十バイトが節約されるだけのようですが、これはメモリの配置のために実現されない場合もあります。この数バイトのスペースの節約と引き換えに、このアプローチではコードの複雑さと実行時のオーバーヘッドが増加し、デバッグが困難になることは言うまでもありません。

保管料が安い。あなたの人生 (そして同僚の人生) をより困難にするのはなぜですか? コードはシンプルで、明確で、わかりやすいものにしてください。後で感謝します。

于 2012-04-04T21:58:31.867 に答える
0

2 番目は、2 つの文字列を連結するたびにメモリに 3 つあるため、メモリ消費が悪化します。

コンパイラは for を作成することで文字列定数のいくつかのインスタンスの処理を開始しましたが、システムがfor を作成した場合、そのオーバーヘッドが発生するStringBuilderため、メモリ負荷の少ない最初のものに投票します。StringBuilder最初の段落が表示されない場合...

RegEx のコンパイルがメモリ使用量にどのように影響するか興味があります。

于 2012-04-04T21:47:45.630 に答える
0

ここでの節約は幻想であり、この文字列を分割することは大きなやり過ぎです。わずかな量のメモリを節約し、単純なコードを複雑にすることは無意味です。節約は見られませんが、そのコードを維持する次の人は、それを理解するのに 10 倍の時間を費やすことになります。

文字列は不変であるため、文字列がまったく/めったに変更されない場合は、1 つにまとめてください。文字列の連結が激しいと、ガベージ コレクターに負担がかかります。

文字列とサブ文字列が大きく、少なくともキロバイトを節約できる場合を除き、そのような最適化に時間と労力を費やさないでください。

于 2012-04-04T21:47:56.940 に答える