1

bigintインデックスでアクセスできるように、任意の整数 (つまり ) をその数字に変換する必要があります。

ただし、アルゴリズムの 2 つの可能な実装の間で迷っていることに気付きました。

open System

let toDigits (x:bigint) =
    x.ToString() 
    |> Seq.map (fun c -> (int c) - (int '0'))
    |> Seq.toArray

let toDigits' (x:bigint) =
    seq {
        let x' = ref x
        while !x' <> 0I do
            yield (int (!x' % 10I))
            x' := !x' / 10I
    } |> Seq.toArray |> Seq.rev

私がつぶやいたのはどれが最速ですか?質問に答えるのを助けるために、私は簡単なprofile方法を考案しました

let profile f times = 
    let x = ref 0
    while !x < times do
        incr x
        f (bigint !x) |> ignore

F# Interactive と混同すると#time、次の出力が得られます。

> profile toDigits 10000000;;
Real: 00:00:11.609, CPU: 00:00:11.606, GC gen0: 825, gen1: 1, gen2: 0
val it : unit = ()
> profile toDigits' 10000000;;
Real: 00:00:28.891, CPU: 00:00:28.844, GC gen0: 1639, gen1: 3, gen2: 0
val it : unit = ()

toDigitこれはの優位性を明確に示しました。しかし、その理由を知りたいので、仲間の F# オーバーフロー愛好家に、この時点からどうすればよいかを尋ねています。

典型的な Java プログラムでは、プロファイラー (たとえば、jvisualvm) を起動して、何らかの CPU サンプリング ビューでどのメソッドがホットなのかを教えてくれます。これは、通常のプロジェクトで .fs ファイルを使用して開発する場合とまったく同じように機能すると思います。私は F# Interactive にいるので、少し迷っています。.NET プロファイラー (この場合は ANTS メモリ プロファイラー) を F# Interactive に接続する必要がありますか? この種のソフトウェア開発のための特別なワークフローはありますか?

4

2 に答える 2

5

おそらくこれはあなたが探していた答えではないかもしれませんが、関数型プログラミングの世界では、タスクに適切な手段 (アルゴリズム、データ構造など) を選択することで、完全な OOP のようなマイクロ プログラミングよりもはるかに多くの利点がもたらされる可能性があります。 .NET プロファイラーが提供するレベルのコード分析。

私の主張を説明するために、デモのケースでは、関数と関数の両方でシーケンスを操作するという選択を正当化するのは困難です。代わりに配列空間内に留まることを選択した場合、同等の機能は次のように表現できます。toDigitstoDigits'

let toDigits'' (x: bigint) =
    x.ToString().ToCharArray() |> Array.map (fun c -> int(c) - int('0'))

ここで、観察できる FSI 提供のプロファイリングに目を向けます。

> profile toDigits 10000000;;
Real: 00:00:13.020, CPU: 00:00:13.000, GC gen0: 1649, gen1: 2, gen2: 0

> profile toDigits'' 10000000;;
Real: 00:00:02.334, CPU: 00:00:02.343, GC gen0: 604, gen1: 1, gen2: 0

したがって、操作するデータ構造の選択が改善されたという理由だけで、5.6 倍のスピードアップが得られ、FSI プロファイラーはこの事実の確認として機能します。

于 2013-06-15T19:56:20.497 に答える
1

典型的な Java プログラムでは、プロファイラー (たとえば、jvisualvm) を起動して、何らかの CPU サンプリング ビューでどのメソッドがホットなのかを教えてくれます。これは、通常のプロジェクトで .fs ファイルを使用して開発する場合とまったく同じように機能すると思います。私は F# Interactive にいるので、少し迷っています。

ファイルと F# Interactiveを使用fsxするからといって、プロジェクトのコンパイルを完全に放棄する必要があるわけではありません。fsx ファイルを直接コンパイルするには、に変更しBuild Actionます。いくつかの場所で条件付きコンパイルを使用する必要があります。File PropertiesCompile

#if INTERACTIVE
#time "on";;
#endif

コードをコンパイルする利点は次のとおりです。

  • Visual Studio プロファイラーを使用して、プログラムのホット スポットに関する統計を取得できます。
  • ILSpy を使用してプログラムを逆コンパイルできます。場合によっては、IL または同等の C# コードでさえ、プログラムの動作に関する洞察を得ることができます。

コードが進化するにつれて、コア関数をファイルに移動しfs、クイック プロファイリング関数をファイルに保持することを検討できfsxます。

あなたの例に戻って、の改善はtoDigits'参照の使用を避けることです:

let toDigits'' (x:bigint) =
    let rec loop x acc =
        if x <> 0I then
            loop (x/10I) (int(x%10I)::acc)
        else acc
    loop x [] |> List.toArray

結果は、toDigits''が より 1.5 倍速く、 よりtoDigits'1.5 倍遅いことを示していtoDigitsます。

toDigitで算術演算を使用しないため、打ち負かすのは困難bigintです。の明らかな欠点の 1 つは、負の stoDigitに対して無意味な結果が得られることです。bigint

于 2013-06-15T19:46:51.580 に答える