サブフォルダを含む、フォルダ内のファイルの取得(F#)

下記のようなフォルダ構造があった際に、file1.txt、file2.txt、file1_1.txtのパスを取得する関数をF#で実装します。
f:id:any-programming:20170207232413p:plain

breadthFirstSearch

汎用的な幅優先探索を実装します。

開始点と、要素から子要素を取得する関数を入力とします。

Queueを用いて手続き的な実装をしました。

module Seq =
    let breadthFirstSearch (children : 'a -> 'a seq) (root : 'a) =
        seq { let queue = System.Collections.Generic.Queue<'a>([ root ])
              while 0 < queue.Count do
                  let item = queue.Dequeue()
                  children item |> Seq.iter queue.Enqueue
                  yield item }


breadthFirstSearch(Seqモジュールのみで実装)

参考例として、whileを使わずに実装してみました。

module Seq =
    let breadthFirstSearch (children : 'a -> 'a seq) (root : 'a) =
        Seq.initInfinite id
        |> Seq.scan (fun queue _ -> queue.Tail @ (children queue.Head |> Seq.toList)) [ root ]
        |> Seq.takeWhile (List.isEmpty >> not)
        |> Seq.map List.head


descendantFiles

フォルダを入力で受け取り、ファイルパスのSeqを返す関数を実装します。

open System.IO

let descendantFiles path =
    Seq.breadthFirstSearch Directory.EnumerateDirectories path
    |> Seq.collect Directory.EnumerateFiles


動作確認

FolderBrowserDialogを利用するため、System.Windows.Forms.dllを参照に追加します。

f:id:any-programming:20170207234330p:plain

[<EntryPoint>]
open System.Windows.Forms

let main argv = 
    use dialog = new FolderBrowserDialog()
    if dialog.ShowDialog() = DialogResult.OK then
        descendantFiles dialog.SelectedPath 
        |> Seq.iter (printfn "%s")

f:id:any-programming:20170207234340p:plain