5

Tomas PetricekとJon Skeetの著書「Real-World Functional Programming」(2人のSO Gurus、btw、両者のおかげで) 次の関数は、277ページで、のような特別なもの、アカウント側の値を考慮して、アレイ上の三点の平均を計算する方法を紹介します:F#の機能的結合反復:パフォーマンスの問題と好ましい機能スタイル

let blurArray (arr:float[]) = 
    let res = Array.create arr.Length 0.0 
    res.[0] <- (arr.[0] + arr.[1])/2.0 
    res.[arr.Length-1] <- (arr.[arr.Length - 2] + arr.[arr.Length -1 ])/2.0 
    for i in 1 .. arr.Length - 2 do 
     res.[i] <- (arr.[i-1] + arr.[i] + arr.[i+1])/3.0 
    res 

私は機能があっても、外部の世界のために不変であることを理解内部的に突然変異と割り当てを採用する。また、それは本当に不可欠ですが、宣言的なスタイルを維持して構成し、採用することができます。それにもかかわらず、私は高次関数などにもっと慣れ親しむための運動として、機能的な解決策を考え出しました。私は解決策を報告し、私の質問を表明します。最初に私は

let computeElementsAverage (myArray:float[]) myIndex (myElement:float) = 
    match myIndex with 
    | 0 -> (myArray.[0..1] |> Array.average) 
    | lastInd when lastInd = (myArray.Length -1) -> (myArray.[lastInd-1..lastInd] |> Array.average) 
    | anIndex -> (myArray.[anIndex -1 .. anIndex + 1 ] |> Array.average) 

は、ヘルパー関数定義(私はindecesの(リストを抽象化している可能性が - >フロート)のマッチ句のパターンですが、私はそれやり過ぎと考えられます)。

その後blurArrayの同等は次のようになります。

let blurArray' (arr:float[]) = 
    let mappingFcn = arr |> computeElementsAverage 
    arr |> (Array.mapi mappingFcn) 

は、最後に私の質問:手続の最も推奨される機能的な方法であるもの

真っ先?私は特に、Array.mapiの要件のために、computeElementsAverageの要素型(float)の最後の要素を宣言しなければならないという事実は嫌いです。使用しない議論を避けて、より良い方法がありますか?

第2に:performancewise私のコードは、はるかに遅く、期待されていた。元のコードより1/10速く動作します。そのようなパフォーマンスに大きな打撃を与えることなく高次機能に依然として依存する他のソリューション?

最後に、いくつかの複数のインデクスに依存するデータ構造(配列など)全体で計算を実行する一般的な方法は何ですか?あなたが見ることができるように、私が思いついた方法は、mapiスキャン機能を使用しており、構造自体を含むクロージャを使用しています。あなたの好みの方法は何ですか?

PS:(blurArrayのオリジナルバージョンは、入力として、私はちょうど私のバージョンでList.averageを使用するフロートに[]を変更した[] int型を使用しています)

+1

パフォーマンスの比較は純粋ではありません。 'float'と' int'はペナルティを導入します。 –

+0

@Fyodor、なぜ私は元のコードでもfloatを使用しています。 blurArrayの元のバージョンはint [] - > int []で、私はそれをfloat [] - > float []に微調整しました。おかげで –

答えて

8

私は素晴らしく、より多くの機能代替が可能だと思いますArray.initを使用してください。この関数を使用すると、各場所の要素を計算するために使用される関数を指定して配列を作成できます。

これは元のコードとよく似ていますが、明示的な突然変異は必要ありません(今はArray.initに隠れています)。実際には、おそらく私はこの本の改訂版でこれを使うでしょう:-)。

let blurArray (arr:float[]) = 
    Array.init arr.Length (fun i -> 
    if i = 0 then (arr.[0] + arr.[1])/2.0 
    elif i = arr.Length - 1 then (arr.[arr.Length - 2] + arr.[arr.Length - 1])/2.0 
    else (arr.[i-1] + arr.[i] + arr.[i+1])/3.0) 

次に、あなたはあなたが、アレイ内の各点の近傍に何かをどこ書きたい多くの機能があることを決めることができました - あなたは近所のサイズを指定することもできます。あなたが何か書くことができる:ここで

let fillArray offset f (arr:float[]) = 
    Array.init arr.Length (fun i -> 
    f arr.[max 0 (i-offset) .. min (arr.Length-1) (i+offset)]) 

を、機能fはこれがに境界のおかげで出ていません(左&右に最大でoffset隣人と各ポイントのネイバーのサブアレイと呼ばれていますmaxおよびminのチェック)。今、あなたはのようにぼかし書くことができます。

arr |> fillArray 1 Seq.average 

fillArrayでは、少し非効率的になり、サブアレイを作成 - あなたは、おそらくより速くそれを作ることができArraySegmentを使用するか、ローカル可変に配列の関連部分をコピーすることによって、アレイ。しかし、それはかなり素敵で機能的に見えます!

+0

ありがとう、トマス。本当に素晴らしい本をおめでとう。 –