指定ディレクトリ以下のファイル名リストの作成
年末はがりがりErlang勉強中.アキュムレータを使えるようになってきてから,だいぶ関数型脳になってきたっぽい.相変わらず楽しい言語である.
指定されたディレクトリ以下を走査して,ファイル名のリストを作成するコードを書いた.
- カレントディレクトリをfile:list_dir/1を使って眺める
- ファイルタイプがファイルならばリストに追加,ディレクトリなら再帰的に処理を行う.
- ファイルタイプ判定には↓の記事の方法を利用.
- ファイル名はカレントディレクトリからの相対パスで記述
- シンボリックリンク等による再帰的な走査が起こることは想定しない.
list_dir_all (Dir) -> list_dir_all(Dir, []). list_dir_all (Dir, Acc0) -> {ok, FileList} = file:list_dir(Dir), lists:foldl( fun(F, Acc) -> FilePath = string:concat(string:concat(Dir, "/"), F), case file_type(FilePath) of regular -> [FilePath|Acc]; directory -> Acc ++ list_dir_all(FilePath, Acc); _ -> error end end, Acc0, FileList).
使えるようになったばかりのlists:foldl大活躍.
少し困ったことに気がつく.複数階層になってファイル数が多いようなディレクトリを対象にするとヒープ領域が溢れてしまったよ,というエラーメッセージと共にerlangシェルが落ちてしまう.
Crash dump was written to: erl_crash.dump eheap_alloc: Cannot allocate 1425410620 bytes of memory (of type "old_heap").
再帰処理による弊害なのか.でも,foldl使って末尾再帰にしているしなぁ.
わかる人には一瞬でわかるんだろうけれど,原因はまだわかっていない.
指定ディレクトリ以下のファイル名を「リスト」で必要なことってほとんどないなぁ...フツウにディレクトリ走査しながら関数を適用すればリストを作成するコストもかからないし.
ということに,ここまで書いて気がついた.
ちょっとした変更で済むので,書いてみた.
foreach_file_all (Func, Dir) -> {ok, FileList} = file:list_dir(Dir), lists:foreach( fun(F) -> FilePath = string:concat(string:concat(Dir, "/"), F), case file_type(FilePath) of regular -> Func(FilePath); directory -> foreach_file_all(Func, FilePath); _ -> error end end, FileList).