まずこの問題の規模についてお詫びしますが、実際に機能的に考えることを試みています。これは私が処理しなければならなかったより難しい問題の1つです。ファンクションファイル "scanner"の作成方法
私は機能的な方法、特にF#で問題を処理する方法についていくつかの提案を得たいと思っていました。私は、ディレクトリのリストを調べ、正規表現パターンのリストを使ってディレクトリから検索されたファイルのリストをフィルタリングし、正規表現パターンの第2のリストを使用して、捕捉されたファイルのテキストに一致するものを見つけるプログラムを書いています。私は、ファイル名、行インデックス、列インデックス、パターン、与えられた正規表現パターンに一致する各テキストの一致した値を返すようにします。また、例外を記録する必要があり、3つの例外が考えられます。ディレクトリを開くことができず、ファイルを開くことができず、ファイルから内容を読み取ることができませんでした。これの最終的な要件は、マッチのために「スキャンされた」ファイルの量が非常に大きくなる可能性があるため、この全体が怠惰である必要があります。私は「純粋な」機能的な解決策について心配しているわけではなく、よく読んでうまく機能する「良い」ソリューションに興味があります。最終的な課題の1つは、Winformツールを使用してこのアルゴリズムをUIに追加したいので、C#と相互運用することです。ここに私の最初の試みであり、うまくいけば、これは問題を明確にします:任意の入力のための
open System.Text.RegularExpressions
open System.IO
type Reader<'t, 'a> = 't -> 'a //=M['a], result varies
let returnM x _ = x
let map f m = fun t -> t |> m |> f
let apply f m = fun t -> t |> m |> (t |> f)
let bind f m = fun t -> t |> (t |> m |> f)
let Scanner dirs =
returnM dirs
|> apply (fun dirExHandler ->
Seq.collect (fun directory ->
try
Directory.GetFiles(directory, "*", SearchOption.AllDirectories)
with | e ->
dirExHandler e directory
Array.empty))
|> map (fun filenames ->
returnM filenames
|> apply (fun (filenamepatterns, lineExHandler, fileExHandler) ->
Seq.filter (fun filename ->
filenamepatterns |> Seq.exists (fun pattern ->
let regex = new Regex(pattern)
regex.IsMatch(filename)))
>> Seq.map (fun filename ->
let fileinfo = new FileInfo(filename)
try
use reader = fileinfo.OpenText()
Seq.unfold (fun ((reader : StreamReader), index) ->
if not reader.EndOfStream then
try
let line = reader.ReadLine()
Some((line, index), (reader, index + 1))
with | e ->
lineExHandler e filename index
None
else
None) (reader, 0)
|> (fun lines -> (filename, lines))
with | e ->
fileExHandler e filename
(filename, Seq.empty))
>> (fun files ->
returnM files
|> apply (fun contentpatterns ->
Seq.collect (fun file ->
let filename, lines = file
lines |>
Seq.collect (fun line ->
let content, index = line
contentpatterns
|> Seq.collect (fun pattern ->
let regex = new Regex(pattern)
regex.Matches(content)
|> (Seq.cast<Match>
>> Seq.map (fun contentmatch ->
(filename,
index,
contentmatch.Index,
pattern,
contentmatch.Value))))))))))
感謝。更新
- ここでは、私が受け取ったフィードバックに基づいてすべての更新のソリューションです:
open System.Text.RegularExpressions
open System.IO
type ScannerConfiguration = {
FileNamePatterns : seq<string>
ContentPatterns : seq<string>
FileExceptionHandler : exn -> string -> unit
LineExceptionHandler : exn -> string -> int -> unit
DirectoryExceptionHandler : exn -> string -> unit }
let scanner specifiedDirectories (configuration : ScannerConfiguration) = seq {
let ToCachedRegexList = Seq.map (fun pattern -> new Regex(pattern)) >> Seq.cache
let contentRegexes = configuration.ContentPatterns |> ToCachedRegexList
let filenameRegexes = configuration.FileNamePatterns |> ToCachedRegexList
let getLines exHandler reader =
Seq.unfold (fun ((reader : StreamReader), index) ->
if not reader.EndOfStream then
try
let line = reader.ReadLine()
Some((line, index), (reader, index + 1))
with | e -> exHandler e index; None
else
None) (reader, 0)
for specifiedDirectory in specifiedDirectories do
let files =
try Directory.GetFiles(specifiedDirectory, "*", SearchOption.AllDirectories)
with e -> configuration.DirectoryExceptionHandler e specifiedDirectory; [||]
for file in files do
if filenameRegexes |> Seq.exists (fun (regex : Regex) -> regex.IsMatch(file)) then
let lines =
let fileinfo = new FileInfo(file)
try
use reader = fileinfo.OpenText()
reader |> getLines (fun e index -> configuration.LineExceptionHandler e file index)
with | e -> configuration.FileExceptionHandler e file; Seq.empty
for line in lines do
let content, index = line
for contentregex in contentRegexes do
for mmatch in content |> contentregex.Matches do
yield (file, index, mmatch.Index, contentregex.ToString(), mmatch.Value) }
ここでも、任意の入力は大歓迎です。
Parsecのような機能パーサーはありますか? –
これはたくさんのテキストです。それを読みやすくするために分割してみてください。 – Marcin
私は単にインターフェイスとオブジェクト式を使用してインスタンスを作成し、それをC#コードに公開します。 –