Сначала позвольте мне извиниться за масштаб этой проблемы, но я действительно пытаюсь мыслить функционально, и это одна из самых сложных проблем, с которыми мне приходилось работать.
Я хотел получить некоторые предложенияо том, как я мог бы справиться с проблемой, которая у меня есть, функционально, особенно в F #.Я пишу программу для просмотра списка каталогов и использую список шаблонов регулярных выражений для фильтрации списка файлов, извлеченных из каталогов, и использую второй список шаблонов регулярных выражений для поиска совпадений в тексте полученных файлов.Я хочу, чтобы эта вещь возвращала имя файла, индекс строки, индекс столбца, шаблон и соответствующее значение для каждого фрагмента текста, который соответствует заданному шаблону регулярных выражений.Кроме того, исключения должны быть записаны, и есть 3 возможных сценария исключений: не удается открыть каталог, не удается открыть файл, не удалось прочитать содержимое из файла.Последним требованием является то, что объем файлов, «отсканированных» на совпадения, может быть очень большим, поэтому все это должно быть ленивым.Я не слишком беспокоюсь о «чистом» функциональном решении, так как меня интересует «хорошее» решение, которое хорошо читается и работает хорошо.Последняя проблема - заставить его взаимодействовать с C #, потому что я хотел бы использовать инструменты winform, чтобы прикрепить этот алгоритм к интерфейсу пользователя.Вот моя первая попытка, и, надеюсь, это прояснит проблему:
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) }
Опять же, любой вход приветствуется.