문제

나는 그것을 스스로 알아 내고 싶지만 궁금해했다 수율 명령문이있는 함수를 열거자를위한 상태 머신으로 변환하기위한 알고리즘은 대략 무엇입니까? 예를 들어 C#을 어떻게 회전 시키는가 :

IEnumerator<string> strings(IEnumerable<string> args)
 { IEnumerator<string> enumerator2 = getAnotherEnumerator();     
   foreach(var arg in arg) 
    { enumerator2.MoveNext();
      yield return arg+enumerator.Current;
    } 
 }

이것으로 :

bool MoveNext()
 { switch (this.state)
    {
        case 0:
            this.state = -1;
            this.enumerator2 = getAnotherEnumerator();
            this.argsEnumerator = this.args.GetEnumerator();
            this.state = 1;
            while (this.argsEnumerator.MoveNext())
            {
                this.arg = this.argsEnumerator.Current;
                this.enumerator2.MoveNext();
                this.current = this.arg + this.enumerator2.Current;
                this.state = 2;
                return true;

              state1:
                this.state = 1;
            }
            this.state = -1;
            if (this.argsEnumerator != null) this.argsEnumerator.Dispose();
            break;

        case 2:
            goto state1;
    }
    return false;
}

물론 원래 코드에 따라 결과가 완전히 다를 수 있습니다.

도움이 되었습니까?

해결책

보고있는 특정 코드 샘플에는 일련의 변환이 포함됩니다. 이것은 알고리즘에 대한 대략적인 설명입니다. 컴파일러가 사용하는 실제 이름과 생성하는 정확한 코드는 다를 수 있습니다. 그러나 아이디어는 동일합니다.

첫 번째 변환은 "Foreach"변환 이며이 코드를 변환합니다.

foreach (var x in y)
{
   //body
}

이 코드로 :

var enumerator = y.GetEnumerator();
while (enumerator.MoveNext())
{
    var x = enumerator.Current;
    //body
}

if (y != null)
{
    enumerator.Dispose();
}

두 번째 변환은 함수 본문에서 모든 수익률 반환 문을 찾아 각 (상태 값)에 숫자를 할당하며 수율 직후 "goto 레이블"을 만듭니다.

세 번째 변환은 메소드 본문의 모든 로컬 변수 및 함수 인수를 폐쇄라는 객체로 들어 올립니다.

예제의 코드가 주어지면 다음과 비슷해 보입니다.

 class ClosureEnumerable : IEnumerable<string>
 {
    private IEnumerable<string> args;
    private ClassType originalThis;
    public ClosureEnumerator(ClassType origThis, IEnumerable<string> args)
    {
        this.args = args;
        this.origianlThis = origThis;
    }
    public IEnumerator<string> GetEnumerator()
    {
        return new Closure(origThis, args);
    }
 }

class Closure : IEnumerator<string>
{
    public Closure(ClassType originalThis, IEnumerable<string> args)
    {
        state = 0;
        this.args = args;
        this.originalThis = originalThis;
    }

    private IEnumerable<string> args;
    private IEnumerator<string> enumerator2;
    private IEnumerator<string> argEnumerator;

    //- Here ClassType is the type of the object that contained the method
    //  This may be optimized away if the method does not access any 
    //  class members
    private ClassType originalThis;

    //This holds the state value.
    private int state;
    //The current value to return
    private string currentValue;

    public string Current
    {
        get 
        {
            return currentValue;
        }
    }
}

그런 다음 방법 본문은 원래 메소드에서 Movenext라는 "Closure"내부의 메소드로 이동하여 bool을 반환하고 ienumerable.movenext를 구현합니다. 현지인에 대한 모든 접근은 "this"를 통해 라우팅되며, 모든 클래스 멤버에 대한 액세스는 this.originalthis를 통해 라우팅됩니다.

"수율 수익률 expr"은 다음과 같이 번역됩니다.

currentValue = expr;
state = //the state number of the yield statement;
return true;

수익률 중단 명세서는 다음과 같이 번역됩니다.

state = -1;
return false;

함수의 끝에 "암시 적"수율 중단 문이 있습니다. 그런 다음 상태 번호를보고 관련 레이블로 점프하는 절차의 시작 부분에서 스위치 명세서가 소개됩니다.

그런 다음 원래 방법은 다음과 같은 것으로 변환됩니다.

IEnumerator<string> strings(IEnumerable<string> args)
{
   return new ClosureEnumerable(this,args);
}

메소드 상태가 모두 객체로 밀려 나고 Movenext 메소드가 스위치 문 / 상태 변수를 사용한다는 사실은 반복자가 마지막 "수익률 리턴 직후에 제어가 지점으로 전달되는 것처럼 반복기가 동작하는 것을 허용하는 것입니다. "다음에"movenext "가 호출됩니다.

그러나 C# 컴파일러가 사용하는 변환이이를 수행하는 가장 좋은 방법이 아니라는 점을 지적하는 것이 중요합니다. 재귀 알고리즘과 함께 "수율"을 사용하려고 할 때 성능이 저하됩니다. 여기에서 더 좋은 방법을 간략하게 설명하는 좋은 논문이 있습니다.

http://research.microsoft.com/en-us/projects/specsharp/iterators.pdf

아직 읽지 않았다면 읽을 가치가 있습니다.

다른 팁

방금이 질문을 발견했습니다 - i 기사를 썼습니다 최근에. 그래도 여기에 언급 된 다른 링크를 기사에 추가해야합니다 ...

Raymond Chen은 이것에 대답합니다. http://blogs.msdn.com/b/oldnewthing/archive/2008/08/12/8849519.aspx

(파트 4가 아닌 시리즈의 1 부를 가리 키도록 편집)

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