2014-01-07 2 views
0

최근에 PriorityQueue 클래스의 다양한 구현물을 가지고 놀았으며 완전히 이해하지 못하는 몇 가지 문제가 발생했습니다.While 루프 내에서의 수익률 사용 (PriorityQueue 구현)

다음

, 내가 실행하고 단위 테스트에서 코드 조각입니다 :

 PriorityQueue<Int32> priorityQueue = new PriorityQueue<Int32>(); 
     Randomizer r = new Randomizer(); 
     priorityQueue.AddRange(r.GetInts(Int32.MinValue, Int32.MaxValue, r.Next(300, 10000))); 

     priorityQueue.PopFront(); // Gets called, and works correctly 

     Int32 numberToPop = priorityQueue.Count/3; 
     priorityQueue.PopFront(numberToPop); // Does not get called, an empty IEnumberable<T> (T is an Int32 here) is returned 

내가 코멘트에서 언급 한 바와 같이, PopFront()가 호출되는 올바르게 작동하지만 나는 PopFront을 (를 호출 할 때 numberToPop), 메서드가 전혀 호출되지 않습니다. 메서드를 입력하지도 않습니다.

는 여기에 방법이 있습니다 :

public T PopFront() 
    { 
     if (items.Count == 0) 
     { 
      throw new InvalidOperationException("No elements exist in the queue"); 
     } 

     T item = items[0]; 
     items.RemoveAt(0); 
     return item; 
    } 

    public IEnumerable<T> PopFront(Int32 numberToPop) 
    { 
     Debug.WriteLine("PriorityQueue<T>.PopFront({0})", numberToPop); 
     if (numberToPop > items.Count) 
     { 
      throw new ArgumentException(@"The numberToPop exceeds the number 
              of elements in the queue", "numberToPop"); 
     } 

     while (numberToPop-- > 0) 
     { 
      yield return PopFront(); 
     } 
    } 

지금, 이전에, 나는이 같은 오버로드 PopFront 기능을 구현했다 : 예상대로

public IEnumerable<T> PopFront(Int32 numberToPop) 
    { 
     Console.WriteLine("PriorityQueue<T>.PopFront({0})", numberToPop); 
     if (numberToPop > items.Count) 
     { 
      throw new ArgumentException(@"The numberToPop exceeds the number 
              of elements in the queue", "numberToPop"); 
     } 

     var poppedItems = items.Take(numberToPop); 
     Clear(0, numberToPop); 
     return poppedItems; 
    } 

이전 구현 (위)했다. 모든 말로, 나는 분명히 yield 문을 사용하는 것이 잘못되었다는 것을 알고 있습니다. PopFront() 함수에서 요소를 반환하지 않기 때문에 가능성이 높습니다.하지만 실제로 알고있는 것은 PopFront (Int32 numberToPop)도 호출되지 않으며 호출되지 않으면 왜 빈 IEnumerable을 반환합니까?

이 문제가 발생하는 이유에 대한 도움이나 설명은 크게 감사드립니다.

+1

yield 반환 값에 의해 반환 된 열거 형을 열거해야합니다. – asawyer

+0

한 번에 두 개 이상의 항목을 팝하기를 원하는 사람들. –

+2

이전 코드도 잘못된 것입니다 (전체 구현이 없으면 확실하게 말할 수 없습니다). 'Take '는 실행을 연기 할 것이므로 다른 팝이 발생할 때까지 결과를 반복하지 않으면 나쁜 일이 발생할 수 있습니다. 이와 같은 방법은 열을 열심히 수정하여 열심히 결과를 구체화해야합니다. 실행 연기는 하위 버그의 제조법입니다. – Servy

답변

4

yield return을 사용하면 컴파일러에서 상태 시스템을 만듭니다. 귀하의 방법으로 반환 된 IEnumerable<T> (foreach 또는 ToList)을 열거하기 시작할 때까지 코드 실행이 시작되지 않습니다. foreach는 루프의 반복 켜기 yield documentation

가입일

는 MoveNext 메서드는 요소라고한다. 이 호출은 다음 yield 반환 문에 도달 할 때까지 MyIteratorMethod의 본문을 실행합니다. yield return 문에 의해 반환 된 식은 루프 본문에서 사용할 요소 변수의 값뿐만 아니라 IEnumerable 인 요소의 Current 속성도 결정합니다.

foreach 루프를 반복 할 때마다 iterator 본문의 실행은 중단 된 지점부터 계속되며 yield return 문에 도달하면 다시 중지됩니다. foreach 루프는 iterator 메서드 또는 yield break 문이 끝날 때 완료됩니다.

+0

아, 잡았다, 나는 컴파일러가 상태 머신을 만들었다는 사실을 모르고 있었고, 근본적으로 그것은 시작되기를 기다리는 작업과 비슷하다. –

+0

나는 5 분 더 기다려야한다. –

+1

@DavidVenegoni 위의 Servy의 조언을 놓치지 마세요. 아주 좋은 아이디어는 아닙니다. – asawyer