먼저이 문제의 규모에 대해 사과하겠습니다.하지만 실제로 기능적으로 생각하려고합니다. 그리고이 문제는 제가 해결해야 할 더 까다로운 문제 중 하나입니다.기능 파일 "스캐너"작성 방법
기능적인 방식으로, 특히 F #에서 발생한 문제를 어떻게 처리 할 수 있는지에 대한 제안을 받고 싶습니다. 나는 디렉터리 목록을 살펴보고 정규식 패턴 목록을 사용하여 디렉터리에서 검색된 파일 목록을 필터링하고 두 번째 정규식 패턴 목록을 사용하여 수집 된 파일의 텍스트에서 일치하는 항목을 찾는 프로그램을 작성하고 있습니다. 이 파일에서 주어진 정규 표현식 패턴과 일치하는 각 텍스트 조각에 대해 파일 이름, 줄 인덱스, 열 인덱스, 패턴 및 일치 값을 반환하기를 원합니다. 또한 예외를 기록해야하고 세 가지 가능한 예외 시나리오가 있습니다. 디렉토리를 열 수 없거나 파일을 열 수 없으며 파일의 내용을 읽지 못했습니다. 마지막 요구 사항은 성냥을 위해 "스캔 된"파일의 양이 매우 클 수 있기 때문에이 모든 게으른 일일 필요가 있습니다. 나는 잘 읽으며 잘 수행하는 "좋은"솔루션에 관심이있는만큼 "순수한"기능적 솔루션에 대해서 너무 걱정하지 않습니다. 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) }
다시 말하지만, 모든 입력을 환영합니다 : -
업데이트 여기에 내가받은 피드백을 기반으로 업데이트 된 솔루션입니다.
파섹과 같은 기능 파서를 보았습니까? –
이것은 많은 텍스트입니다. 그것을 읽기 쉽게 깨뜨려보십시오. – Marcin
나는 단순히 인터페이스와 Object Expression을 사용하여 인스턴스를 만들고이를 C# 코드에 표시합니다. –