2011-12-13 2 views
2

ここでは私の問題で、これは私のために働いていない理由...私は理解していない:)のF# - 関数合成を理解していない(ファイルが重複ファイルの機能を取得するために機能し得る変換)

をより具体的にはファイル取得機能があります(問題はありませんが、フィードバックは歓迎です)。

type DirectoryOptions = Directory of string * Option<SearchOption> 

type SearchOptions = 
    | SearchSubDirectories 
    | SearchCurrentDirectory 

let WithExtensionIn extlist filename = 
    let fileext = Path.GetExtension filename 
    extlist |> Seq.exists (fun e -> e = fileext) 

let GetFiles dir extlist = 
    match dir with 
     | Some diroptions -> 
      let directoryname, suboptions = diroptions 
      match suboptions with 
       | Some SearchSubDirectories | None -> 
        Directory.GetFiles(directoryname, "*.*", SearchOption.AllDirectories) 
        |> Seq.filter (WithExtensionIn extlist) 
       | Some SearchCurrentDirectory -> 
        Directory.GetFiles(directoryname, "*.*", SearchOption.TopDirectoryOnly) 
        |> Seq.filter (WithExtensionIn extlist) 
     | None -> 
      Directory.GetFiles(Directory.GetCurrentDirectory(), "*.*", SearchOption.AllDirectories) 
        |> Seq.filter (WithExtensionIn extlist) 

これを「ファイルの複製を取得」機能にしたいと考えています。私はできないかもしれないが、私は機能的な考え方に頭を向けようとしている。私の理解に基づいた私の現在の試みは機能しません。これは私の理解が間違っていることを意味し、これを解決する方法についていくつかの助けや説明をしたいと思います。私の理解は、関数の構成では、最も内側の関数はn個の入力パラメータを持つことができますが、出力を1つしか持たず、残りの関数は1つの入力を持つことができます。私は明確な入出力がないので、最初の関数がどのように解釈されているか(F#の合成の文脈では、使用するのが悪い単語かもしれません)は完全にはわかりません。私はこれがカレーの直接の影響だと信じています。ここで

私の現在の試みです:

let GetDuplicateFiles = 
    let LengthAndExtension file = 
     //this is faked for simplicity 
     (12, ".htm") 

    let GroupSizeGreaterThanOne group = 
     let _, values = group 
     Seq.length values > 1 

    let content file = 
     //again faked 
     () 

    let groups items = 
     snd items 

    GetFiles 
    >> Seq.groupBy LengthAndExtension 
    >> Seq.filter GroupSizeGreaterThanOne 
    >> Seq.collect groups 
    >> Seq.groupBy content 
    >> Seq.filter GroupSizeGreaterThanOne 
    >> Seq.collect groups 

これは、エラーはタイプ「されてくれSeq.groupBy LengthAndExtension にコンパイルエラーを与える」B - > seqは「タイプ」以降と互換性がありません< 'a>'

ご意見/ご感想は歓迎します。私はあなたが私が何を意味するか知っているなら、私はああハッピーモーメントを探していると思う。

答えて

2

フォワードコンポジション(>>)は新しい機能を作成し、最初の入力を2番目の入力に渡します。

署名は、問題を明らかにする:(>>) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3

それは単一の引数を取る二つの機能それぞれを受け付けます。しかしGetFilesは2つの引数をとります。簡単な解決策はGetFilesをタプルを取るように変更することです:let GetFiles (dir, extlist) = ...

+0

ないことを意味するものはF#関数の構成と数学的構成はわずかに異なり、F#に数学を模倣させる方法は、n個の入力をタプルにラップすることですか? Wierdしかし、大丈夫。私のコードは今動作します、ありがとう。 – Brad

+0

はい。その理由は、関数が複数の戻り値を持つことができないからです。タプルはそれを達成する方法です。 – Daniel

1

機能構成>>を使用して、私たちは:(f >> g) x = g(f(x))を持っています。これは、最初にfを入力に適用し、その後にgを出力に適用することを意味します。

関数合成を使用する簡単な方法は、まずパイプラインを使用して関数を書き出し、後でそれをリファクタリングすることです。私は通常x |> f1 |> f2 |> ... |> fnという形式で始まり、入力xを削除し、パイプライン(|>)を機能構成(>>)に変更して、正しい合成関数f1 >> f2 >> ... >> fnを持つようにします。しかし、それはあなたの例では明らかではありません。

let GetDuplicateFiles(dir, extlist) = 
    //... 
    GetFiles dir extlist 
    |> Seq.groupBy LengthAndExtension 
    |> Seq.filter GroupSizeGreaterThanOne 
    |> Seq.collect groups 
    |> Seq.groupBy content 
    |> Seq.filter GroupSizeGreaterThanOne 
    |> Seq.collect groups 

あなたがGetFiles(dir, extlist)のタプル形式にGetFiles dir extlistのカレーフォームからGetFilesを変更した場合、あなたは簡単に関数合成を使用するには、上記のトリックを適用することができます。

GroupSizeGreaterThanOne機能が若干変更されて
let GetDuplicateFiles = 
    //... 

// (dir, extlist) 
// |> 
    GetFiles 
    >> Seq.groupBy LengthAndExtension 
    >> Seq.choose GroupSizeGreaterThanOne 
    >> Seq.concat 
    >> Seq.groupBy content 
    >> Seq.choose GroupSizeGreaterThanOne 
    >> Seq.concat 

そして、サイドノートに

、あなたはトラバースシーケンスを1時間を節約するために Seq.choose ... >> Seq.concatSeq.filter ... >> Seq.collectを変更することができます

let GroupSizeGreaterThanOne (_, values) = 
    if Seq.length values > 1 then Some values else None 
+0

あなたのコードはよりクリーンだと思いますが、トラバースはどこに保存されていますか? – Brad

+0

'Seq.collect'は基本的に' Seq.map'の後に 'Seq.concat'が続きます。トラバーサルを保存することで、不要な 'Seq.map'を削除することができます。 – pad

+2

Seq.collectはシーケンスのモナド結合と同じではありませんか?また、Seq.collect、Seq.map、Seq.concatではなく、シーケンス自体のトラバースを保存しないという意味ですが、スタックに余計な呼び出しを保存しますか? – Brad

関連する問題