スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#入門基本編落穂拾い その44 ファイル、フォルダ処理(3)

今回はDiscriminated unionを利用して、ファイル、フォルダを一度メモリ上に保存してから、これを対象として、関数を適用することを考えてみます。ファイル、フォルダについて、それぞれFileInfo,DirectoryInfoを情報として付加しておくことにします。
次のように定義します。
 
open System.IO
 
type Entry =
  | File of System.IO.FileInfo
  | Dir of System.IO.DirectoryInfo * list<Entry>
 
実際のファイルフォルダからEntry型の値を生成する関数を定義します。
 
let rec mkEntry(path : string)  =
 
    if System.IO.File.Exists (path) then //pathがファイル名を表す場合
        File(new System.IO.FileInfo(path))
 
    else //path がディレクトリ名の場合
       let  subEntries =
                [for entry in Directory.GetFileSystemEntries(path) do
                    yield mkEntry(entry)]
       Dir(new System.IO.DirectoryInfo(path) ,subEntries)
 
val mkEntry : string -> Entry
 
次のようなフォルダに適用してみます。

347-1.jpg 
 
 
なおそれぞれのフォルダ内にはフォルダ名と同名で拡張子がtxtのテキストファイルが保存されているとします。(前回までと同じものです。)
 
> let t = mkEntry @"s:\BlogTest";;
 
val t : Entry =
  Dir
    (s:\BlogTest,
     [Dir
        (s:\BlogTest\SF1,
         [File s:\BlogTest\SF1\SF1.txt;
          Dir
            (s:\BlogTest\SF1\SF1SF1,
             [File s:\BlogTest\SF1\SF1SF1\SF1SF1.txt;
              Dir
                (s:\BlogTest\SF1\SF1SF1\SF1SF1SF1,
                 [File s:\BlogTest\SF1\SF1SF1\SF1SF1SF1\SF1SF1SF1.txt]);
              Dir
                (s:\BlogTest\SF1\SF1SF1\SF1SF1SF2,
                 [File s:\BlogTest\SF1\SF1SF1\SF1SF1SF2\SF1SF1SF2.txt])]);
          Dir
            (s:\BlogTest\SF1\SF1SF2,[File s:\BlogTest\SF1\SF1SF2\SF1SF2.txt])]);
      Dir
        (s:\BlogTest\SF2,
         [File s:\BlogTest\SF2\SF2.txt;
          Dir
            (s:\BlogTest\SF2\SF2SF1,[File s:\BlogTest\SF2\SF2SF1\SF2SF1.txt])])])
            
            
さて、それではこれに対して関数を適用して、「ファイルの大きさの和」や、「ファイル名一覧」、「一番最近にアクセスされたファイルまたはフォルダ」などを求めてみたいと思います。ひとつひとつ関数を定義するのではなく、Entry型の値の上をtraverse(渡り歩く)していき、値をまとめるという役目をする関数を定義しておき、これを利用することにします。
なお、調べていくのはファイルの場合はFileInfoで、ディレクトリの場合はDirectoryInfoです。型が違うので、ワンクッションいれて同じ型に変換し(下の定義ではgfとgd)それを対象に処理していくようにしています。
 
> let rec holdEntry f acc  (gf:FileInfo -> 'a) (gd:DirectoryInfo -> 'a) (node:Entry) =
    match node with
    | File(fi) -> 
            (f acc (gf fi))
    | Dir (di,entries) ->
            let subAcc = List.fold (fun acc2 x -> holdEntry f acc2  gf gd x) acc entries
            (f subAcc (gd di));;
 
val holdEntry :
  ('a -> 'a0 -> 'a) ->
    'a -> (FileInfo -> 'a0) -> (DirectoryInfo -> 'a0) -> Entry -> 'a
 
 
では使ってみます。
 
(ファイルの大きさの合計を求める)
 
> holdEntry (+) 0L (fun  fi -> fi.Length) (fun _ -> 0L) t;;
 
val it : int64 = 42L
 
(ディレクトリ(フォルダ)名の一覧を求める)
 
> holdEntry (fun acc (name:option<string>) ->if name.IsSome then name.Value :: acc else acc)
          []
          (fun _ -> None) 
          (fun di  -> Some(di.FullName) ) 
           t;;
 
val it : string list =
  ["s:\BlogTest"; "s:\BlogTest\SF2"; "s:\BlogTest\SF2\SF2SF1";
   "s:\BlogTest\SF1"; "s:\BlogTest\SF1\SF1SF2"; "s:\BlogTest\SF1\SF1SF1";
   "s:\BlogTest\SF1\SF1SF1\SF1SF1SF2"; "s:\BlogTest\SF1\SF1SF1\SF1SF1SF1"]
 
(ファイル名とその大きさの一覧を求める)
 
> holdEntry (fun acc (n_l:option<string*int64>) ->if n_l.IsSome then n_l.Value :: acc else acc)
          []
          (fun fi -> Some((fi.Name,fi.Length))) 
          (fun _  -> None) 
           t;;
 
val it : (string * int64) list =
  [("SF2SF1.txt", 6L); ("SF2.txt", 3L); ("SF1SF2.txt", 6L);
   ("SF1SF1SF2.txt", 9L); ("SF1SF1SF1.txt", 9L); ("SF1SF1.txt", 6L);
   ("SF1.txt", 3L)]
 
(ファイルまたはフォルダが一番最近にアクセスされた時刻)
 
> holdEntry (fun acc (dt:System.DateTime) ->
                if acc = None then Some(dt)
                else
                    if System.DateTime.Compare(dt,acc.Value) < 0 then acc
                    else Some(dt))
          None
          (fun fi -> fi.LastAccessTime) 
          (fun di  ->di.LastAccessTime) 
           t;;
 
val it : System.DateTime option =
  Some 2010/07/06 7:59:05 {Date = 2010/07/06 0:00:00;
                           Day = 6;
                           DayOfWeek = Tuesday;
                           DayOfYear = 187;
                           Hour = 7;
                           Kind = Local;
                           Millisecond = 806;
                           Minute = 59;
                           Month = 7;
                           Second = 5;
                           Ticks = 634139999458066406L;
                           TimeOfDay = 07:59:05.8066406;
                           Year = 2010;}
 
 
(一番最近にアクセスされたファイルまたはフォルダ名とその時刻)
 
> holdEntry (fun acc (n_d:string*System.DateTime) ->
                if acc = None then Some(n_d)
                else
                    if System.DateTime.Compare(snd n_d ,snd acc.Value) < 0 then acc
                    else Some(n_d))
          None
          (fun fi -> (fi.Name,fi.LastAccessTime)) 
          (fun di  ->(di.Name,di.LastAccessTime)) 
          t;;
          
val it : (string * System.DateTime) option =
  Some ("SF1SF1", 2010/07/06 7:59:05 {Date = 2010/07/06 0:00:00;
                                      Day = 6;
                                      DayOfWeek = Tuesday;
                                      DayOfYear = 187;
                                      Hour = 7;
                                      Kind = Local;
                                      Millisecond = 806;
                                      Minute = 59;
                                      Month = 7;
                                      Second = 5;
                                      Ticks = 634139999458066406L;
                                      TimeOfDay = 07:59:05.8066406;
                                      Year = 2010;})
 
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。