문제

내가 쓰는 프로그램을 다음과 같다:

  • 모든 파일을 찾을 올바른 확장자 지정된 디렉토리
  • Foreach,의 모든 항목을 찾을 수 지정된 문자열에서 해당 파일
  • 각 라인 인쇄

나는 다음과 같이 기능적 방법으로,의 시리즈로 발전기능(는 것 call yield return 고 하나만 반환에 시간 지연로드),도록 내 코드를 읽을 다음과 같다:

IEnumerable<string> allFiles = GetAllFiles();
IEnumerable<string> matchingFiles = GetMatches( "*.txt", allFiles );
IEnumerable<string> contents = GetFileContents( matchingFiles );
IEnumerable<string> matchingLines = GetMatchingLines( contents );

foreach( var lineText in matchingLines )
  Console.WriteLine( "Found: " + lineText );

이 모든 것은 괜찮지만,무엇이고 싶지 인쇄 일부 통계에 끝입니다.무언가 이것을 좋아한다:

Found 233 matches in 150 matching files. Scanned 3,297 total files in 5.72s

문제가 쓰는 코드에'순수한 기능성'스타일의 위와 같은 각 항목은 게으르게이 로드됩니다.
당신만이 알고 얼마나 많은 파일이 경기에서 총을 때까지 최종 foreach 루프가 완료되기 때문에 하나의 항목만 이제까지 yielded 시간에 코드가 없이 어떤 위치 추적을 유지하는 방법은 많은 것을 발견했습니다.를 호출하는 경우 LINQ 의 matchingLines.Count() 방법,그것은 다시 열거 수집!

나는 생각할 수 있는 많은 방법으로 이 문제를 해결하기 위해,그러나 그들 모두 보다 다소 추한입니다.그것으로는 뭔가 사람들 수밖에 없 수행하기 전에,그리고 나는 확실히 있을 것이 좋은 디자인 패턴을 보여주는 좋은 방법이 있습니다.

어떤 아이디어가?환호

도움이 되었습니까?

해결책

비슷한 맥락에서 다른 답변,하지만 약간 더 일반적인 접근 방법을...

...왜 만들기 장식 클래스할 수 있는 포장 기존페이 구현 및 계산 통계로 전달이 다른 항목이를 통해.

Counter 클래스 I 던지 함께 하지만 당신을 만들 수 있다 변화에 대한 다른 종류의 집계를 너무입니다.

public class Counter<T> : IEnumerable<T>
{
    public int Count { get; private set; }

    public Counter(IEnumerable<T> source)
    {
        mSource = source;
        Count = 0;
    }

    public IEnumerator<T> GetEnumerator()
    {
        foreach (var T in mSource)
        {
            Count++;
            yield return T;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        foreach (var T in mSource)
        {
            Count++;
            yield return T;
        }
    }

    private IEnumerable<T> mSource;
}

을 만들 수 있습은 세 인스턴스 Counter:

  1. 중 하나를 포장 GetAllFiles() 계산 총 파일
  2. 중 하나를 포장 GetMatches() 의 수를 계산 일치하는 파일고
  3. 중 하나를 포장 GetMatchingLines() 의 수를 계산 일치하는 라인입니다.

열쇠와 이 방법은 당신이 레이어링이 여러 책임에 귀하의 기존하는 클래스/법- GetMatchingLines() 방법만을 처리 일치하는,당신이 요구되지 않을 추적하고 통계뿐만 아니라.

설명 코멘트에 대한 응답으로 Mitcham:

최종 코드는 다음과 같이 보일 것이다:

var files = new Counter<string>( GetAllFiles());
var matchingFiles = new Counter<string>(GetMatches( "*.txt", files ));
var contents = GetFileContents( matchingFiles );
var linesFound = new Counter<string>(GetMatchingLines( contents ));

foreach( var lineText in linesFound )
    Console.WriteLine( "Found: " + lineText );

string message 
    = String.Format( 
        "Found {0} matches in {1} matching files. Scanned {2} files",
        linesFound.Count,
        matchingFiles.Count,
        files.Count);
Console.WriteLine(message);

참고로 이것은 여전히 기능적인 접근 방식에 사용되는 변수는 변경할 수 없 (더 변수보다),및 전반적인 기능이 없는 부작용이 나타납니다.

다른 팁

프로세스를 프로세스를 '매치 업체'클래스로 캡슐화하여 메서드가 진행될 때 통계를 캡처해야합니다.

public class Matcher
{
  private int totalFileCount;
  private int matchedCount;
  private DateTime start;
  private int lineCount;
  private DateTime stop;

  public IEnumerable<string> Match()
  {
     return GetMatchedFiles();
     System.Console.WriteLine(string.Format(
       "Found {0} matches in {1} matching files." + 
       " {2} total files scanned in {3}.", 
       lineCount, matchedCount, 
       totalFileCount, (stop-start).ToString());
  }

  private IEnumerable<File> GetMatchedFiles(string pattern)
  {
     foreach(File file in SomeFileRetrievalMethod())
     {
        totalFileCount++;
        if (MatchPattern(pattern,file.FileName))
        {
          matchedCount++;
          yield return file;
        }
     }
  }
}

작업 작업을 코딩해야하기 때문에 거기서 멈출 것입니다. 그러나 일반적인 아이디어가 있습니다. '순수한'기능 프로그램의 요점은 부작용이 없다는 것입니다.이 유형의 정적 계산은 부작용입니다.

두 가지 아이디어를 생각할 수 있습니다

  1. 열거 자로부터 컨텍스트 객체와 반환 (문자열 + 컨텍스트)을 전달합니다 - 순수한 기능적 솔루션

  2. 통계에 스레드 로컬 스토리지를 사용합니다 (통계)CallContext), 당신은 화려하고 컨텍스트의 스택을 지원할 수 있습니다. 그래서 당신은 이와 같은 코드를 가질 것입니다.

    using (var stats = DirStats.Create())
    {
        IEnumerable<string> allFiles = GetAllFiles();
        IEnumerable<string> matchingFiles = GetMatches( "*.txt", allFiles );
        IEnumerable<string> contents = GetFileContents( matchingFiles );
        stats.Print()
        IEnumerable<string> matchingLines = GetMatchingLines( contents );
        stats.Print();
    } 
    

코드를 거꾸로 뒤집어 기뻐하면 푸시 Linq에 관심이있을 수 있습니다. 기본 아이디어는 "풀"모델을 뒤집는 것입니다. IEnumerable<T> 관찰자와 함께 "푸시"모델로 바꾸십시오. 파이프 라인의 각 부분은 일반적으로 파이프 라인의 새로운 부분을 형성하는 수많은 관찰자 (이벤트 핸들러 사용)를 지나서 데이터를 효과적으로 푸시합니다. 이것은 여러 집계를 동일한 데이터에 연결하는 정말 쉬운 방법을 제공합니다.

보다 이 블로그 항목 자세한 내용은. 나는 얼마 전에 런던에서 그것에 대해 이야기했다 - 나의 대화 페이지 샘플 코드, 슬라이드 데크, 비디오 등에 대한 몇 가지 링크가 있습니다.

재미있는 작은 프로젝트이지만 약간의 머리를 잡아야합니다.

나는 Bevan의 코드를 가져 와서 만족할 때까지 그것을 리팩토링했습니다. 재미있는 것들.

public class Counter
{
    public int Count { get; set; }
}

public static class CounterExtensions
{
    public static IEnumerable<T> ObserveCount<T>
      (this IEnumerable<T> source, Counter count)
    {
        foreach (T t in source)
        {
            count.Count++;
            yield return t;
        }
    }

    public static IEnumerable<T> ObserveCount<T>
      (this IEnumerable<T> source, IList<Counter> counters)
    {
        Counter c = new Counter();
        counters.Add(c);
        return source.ObserveCount(c);
    }
}


public static class CounterTest
{
    public static void Test1()
    {
        IList<Counter> counters = new List<Counter>();
  //
        IEnumerable<int> step1 =
            Enumerable.Range(0, 100).ObserveCount(counters);
  //
        IEnumerable<int> step2 =
            step1.Where(i => i % 10 == 0).ObserveCount(counters);
  //
        IEnumerable<int> step3 =
            step2.Take(3).ObserveCount(counters);
  //
        step3.ToList();
        foreach (Counter c in counters)
        {
            Console.WriteLine(c.Count);
        }
    }
}

예상대로 출력 : 21, 3, 3

이러한 기능이 자신의 기능이라고 가정하면, 내가 생각할 수있는 유일한 것은 방문자 패턴입니다. 각 일이 발생할 때 다시 전화하는 추상 방문자 기능을 전달합니다. 예를 들어 : Ilinevisitor를 GetFileContents로 전달합니다 (파일을 라인으로 나누는 것으로 가정합니다). Ilinevisitor는 Onvisitline (String Line)과 같은 메소드를 갖고 Ilinevisitor를 구현하고 적절한 통계를 유지할 수 있습니다. ilinematchvisitor, ifilevisitor 등으로 헹구고 반복하거나 각 경우에 다른 의미 론적 인 onvisit () 메소드가있는 단일 ivisitor를 사용할 수 있습니다.

귀하의 기능은 각각 방문자를 데려 가야하고 적절한 시간에 onvisit ()라고 부르며 성가신 것처럼 보일 수 있지만 적어도 방문자는 여기서하는 일을 제외하고는 많은 흥미로운 일을하는 데 사용될 수 있습니다. . 실제로 Onvisitline (String Line)의 일치를 확인하는 방문자를 전달하여 GetMatchingLines를 작성하지 않을 수 있습니다.

이것은 이미 고려한 못생긴 것 중 하나입니까?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top