2016-01-07 10 views
6

1 2 3のような文字列があり、doubleを含む部分文字列の位置を特定すると、一時文字列を作成せずに部分文字列から直接解析する方法はありますか?直接重複する部分文字列を解析する

たとえば、私はSystem.Double.Parse(str.Substring(0, 1))を実行できますが、それはゆっくりと不要な一時的な文字列を作成します。元の文字列の一部から直接doubleを解析することは可能ですか?

EDIT

エリックリペットは「小さな文字列が安い」と述べて、ここに私の動機に疑問を呈しました。この動機は、intの構文解析で同じことをして、小さな文字列がそれほど安くないため、大幅なパフォーマンスの向上が見られるからです。ここで

は、一時的な文字列を使用してintのシーケンスをlexes機能である:

let lex f (s: string) = 
    let rec inside i0 (s: string, i) = 
    if i = s.Length then 
     f (s.Substring(i0, i-i0) |> System.Int32.Parse) 
    else 
     let c = s.[i] 
     if '0'<=c && c<='9' then 
     inside i0 (s, i+1) 
     else 
     f (s.Substring(i0, i-i0) |> System.Int32.Parse) 
     outside (s, i) 
    and outside (s: string, i) = 
    if i < s.Length then 
     let c = s.[i] 
     if '0'<=c && c<='9' then 
     inside i (s, i) 
     else 
     outside (s, i+1) 
    outside (s, 0) 

これは、文字列から15625000個のint型をlexのために2.4sかかります。

let lex f (s: string) = 
    let rec inside n (s: string, i) = 
    if i = s.Length then f n else 
     let c = s.[i] 
     if '0'<=c && c<='9' then 
     inside (10*n + int c - int '0') (s, i+1) 
     else 
     f n 
     outside (s, i) 
    and outside (s: string, i) = 
    if i < s.Length then 
     let c = s.[i] 
     if '0'<=c && c<='9' then 
     inside 0 (s, i) 
     else 
     outside (s, i+1) 
    outside (s, 0) 

は、この一時的な文字列を使用するソリューションよりも速く9xの上、0.255sをとります。

はここで一時的な文字列を回避したバージョンです!

なぜ字句浮動小数点数が異なるべきなのかわかりません。したがって、部分文字列から浮動小数点数を解析する機能を提供しないことによって、.NET上ではテーブルのパフォーマンスが一桁も離れた状態になります。私は多くの科学的コンピューティングを行い、多くの場合、特にスタートアップ時に大量のデータをレックスする必要があるため、このような風にパフォーマンスを投げたくはありません。

+4

極端なマイクロ最適化のようです。ライブラリが必要になるか、本格的なダブルパーサーを書くことになりますが、これはまさに単純な作業ではありません。 – Rob

+4

実際にここで特定されたパフォーマンスの問題がありますか?小さな弦は安いです。つまり、個々の文字だけをコピーするレクサーを書くことができます。 –

+2

@EricLippert:一時ファイルを作成せずにintを解析するためのベンチマークコードで質問を更新しました.9倍以上高速です。私は、浮動小数点の解析でも同様に巨大なパフォーマンスの向上が見られると考えています。つまり、「小さな文字列は安い」とは言いません。 –

答えて

-2

これは、あなたがはい、私はそれが完全になんとかだと思う

static void Main(string[] args) 
{ 
    string input = "1 2 3"; 
    double[] output = input.Split(new char[] {' '},StringSplitOptions.RemoveEmptyEntries).Select(x => double.Parse(x)).ToArray(); 
} 
+2

ほど一般的ではありませんでした。これにより、3つの配列、つまりIEnumerableとFuncの割り当てが追加されました。 OPが単一の文字列割り当てでさえも許容しないと考えるなら、私はこれが要求に合っているとは思わない。彼らはその解決策が必要以上に、おそらく40倍遅くなるので、避難やポインタの更新をマークするために払っ二世代ごとに存続しますので、 – latkin

+0

さらに悪いことに、あなたは長い列でそれらを保つことによって住んでいた一時のすべてを作りました。 –

+0

私は知っていますが、その要求は「直接解析する」ためです。一時変数を避けることは、最適化コンパイラの速度を必ず上げる必要があります。 linqに行くのが最も効率的な方法ではありません。カラムが固定されていると、幅の部分文字列がSplit()メソッドより効率的ですが、カラムが固定されていないとSplit()がより効率的になります。 – jdweng

2

を行うことができますが最適です。あなたは構文解析を行う独自の関数を書くことができます、actualソースコードDouble.Parse()にそれを基にすることもできます。このコードは大きくて怖いものではなく、ニーズに合わせて最適化することができます。

+3

[実際の作業者メソッド](http://referencesource.microsoft.com/#mscorlib/system/number.cs,04291cc3a0b10032)は、深くネストされたループと分岐を持つ密で安全でないC#の200行です。これは補助ヘルパーのタイプ/メソッド以外にもあります。私はこれを気にしないで却下しません。 ( '/ unsafe'の要求は、アセンブリがもはや検証可能でなく、他にも様々な潜在的に不都合な制限があることを意味します) – latkin

+0

いくつかのタイプのタスクには、それが有益です。私は、部分文字列を持たないことは大きな最適化ではないと思うが、解析関数自体は最適化の大きな可能性を秘めている。 –

+0

"/安全でない"という要求。え?彼らは、パフォーマンスが不安定なコードを使用してしまったので、安全でないコードを使用してしまい、サブストリングをコピーして遅くなってしまうようなAPIにラップしてしまいます。ナッツ! –

1

あなたは数字、このようなもので、文字列の数字を解析することができます:あなたは、アカウントになど小数点を取るために、この上で展開することができ

string tmp = "1 2 3"; 
double result = CustomConvertToDouble(tmp, 0, 1); 
Console.WriteLine(result); // 1 

static double CustomConvertToDouble(string input, int startIndex, int length) 
{ 
    double result = 0d; 
    int lastDigitIndex = startIndex + length - 1; 
    int power = 0; 
    for (int i = lastDigitIndex; i >= startIndex; i--) 
    { 
     int digit = (input[i] - '0'); 
     result += (Math.Pow(10, power++)) * digit; 
    } 
    return result; 
} 

使用。

しかし、通常の方法でパフォーマンスのボトルネックになる可能性があるのか​​どうか、私は本当に疑問に思います。そのコードが本当にパフォーマンスに重大なものであれば、おそらく最善のルートは別の言語で書いているのでしょうか?

+0

これは、System.Double.Parse(str.Substring(0、1))よりも何倍も遅くなると思います。 – Rob

+0

申し訳ありませんが、私はその答えの一部を見ていない、私はコメントから削除しました – Rob

-1
for (int x = 0; x < input.Length; x++) 
{ 
    if(input[x] != ' ') 
     Console.WriteLine(Double.Parse(input[x].ToString())); 
} 

追加のEnumerableオブジェクトは作成されませんが、Double.Parseは文字列を除くため、toStringが必要です。

0

あなただけの一桁を探しているなら、それは十分に簡単です:

let readDigit s i = 
    let getDigit x = 
     if '0' <= x && x <= '9' 
     then byte x - 48uy // byte value of '0' 
     else failwith "Not a digit" 
    s |> Seq.item i |> getDigit |> double 

このF#実装がstringchar seqを実装していることを利用し、char値がbyte値に変換することができます。

私はそれが、Double.Parse(str.Substring(0, 1))を使用するよりも速いということを疑います。

+0

なぜ匿名のdownvoteですか? –

+0

私ではありませんでした。しかし、そのコードは非常に遅くなるでしょう! :-) –

関連する問題