2017-11-24 7 views
0

나는 재귀 하강 파서를 구축하고 나는 목록을 작성 두 가지 규칙을 가지고에 의존 루프가있는 단일 규칙을 쉽게 이해할 수 있지만 컴파일러가이를 확인할 수있는 꼬리 호출 최적화를 수행 할 수 있다는 것을 알고 있습니다.하향식 재귀 하강 구문 분석 : 지금은 당신이에이 두 가지 규칙을 최적화 할 수 있다는 사실을 알고</p> <pre><code>ValueList -> TOKEN_IDENTIFER TOKEN_QUOTE ValueListP ValueListP -> ValueList | %EPSILON% </code></pre> <p>: 꼬리 호출 최적화

1)합니까 내 제공된 코드를 활용 꼬리 호출 최적화 :

void Parser::grammarValueList(std::deque<std::unique_ptr<ValueNode>>& arg1)                                         
{                                                            
    std::string var1 = m_currentToken.getValue().string;                                             
    if(acceptToken(Token::Type::TOKEN_IDENTIFIER))                                              
    {                                                          
      std::string var2 = m_currentToken.getValue().string;                                           
      if(acceptToken(Token::Type::TOKEN_QUOTE))                                             
      {                                                        
        arg1.push_back(std::unique_ptr<ValueNode>(new ValueNode(var1, var2)));                                   
        if(peekValueListP())                                                 
        {                                                      
          return grammarValueListP(arg1);                                            
        }                                                      
      }                                                        
    }                                                          

    throw ParseException("Error: did not expect \"" + m_currentToken.toString() + "\"");                                     
} 

void Parser::grammarValueListP(std::deque<std::unique_ptr<ValueNode>>& arg1)                                         
{                                                            
    if(peekValueList())                                                     
    {                                                          
      return grammarValueList(arg1);                                                
    }                                                          
    else                                                         
    {                                                          
      return;                                                      
    }                                                          

    throw ParseException("Error: did not expect \"" + m_currentToken.toString() + "\"");                                     
} 

그래서 나는 두 가지 질문이있다 : 여기에 내 현재 코드는?

2) 코드 조각이 꼬리 호출 최적화를 활용하더라도 프로그래머는 사소한 경우 프로그래머가 자체 최적화 (반복을 제거하고 루프로 대체)를 시도해야합니까?

+1

특정 컴파일러가 테일 호출 최적화를 수행하는지 확인하려면 방출 된 어셈블리 언어 코드를 확인하십시오. –

+1

컴파일러가 컴파일러가 될 수 있다는 것을 의미하지는 않습니다. C++ 표준은 컴파일러가 관찰 할 수없는 최적화를 구현할 수 있도록합니다. 컴파일러는 그렇게 할 필요가 없습니다. –

+1

간단한 해결책은 재귀 대신 반복을 사용하는 것입니다. – EJP

답변

4

아니요, grammarValueList은 꼬리 호출을 수행하지 않습니다.

로컬 변수가 std::string 인 두 개의 로컬 변수가 있는데, 이는 사소한 소멸자가 아닙니다. 그 소멸자는 메서드가 반환되기 바로 전에 호출되어야하는데, 이는 grammarValueListP이 호출 된 이후입니다. 따라서 grammarValueListP에 대한 호출이 꼬리 위치에 있지 않습니다.

은 물론 소멸자의 정의에 대한 액세스와 최적화가 조기에 가능이라고 가정 (함수의 표시 동작을 변경하지 않고 var1var2을 소멸하는 것이 가능하다는 것을 알아낼 수있다; 이것은 의존 부분적으로는 ValueNode 생성자 내부에서 일어나는 일에 대해). 하지만 대부분의 C++ 구현이 꼬리 호출을 최적화하기 위해 열심히 노력한다고 생각하지 않습니다.

저는 개인적으로 루프를 사용합니다. 소멸자 호출을 제거하더라도 컴파일러가 여전히 TCO를 찾지 못할 가능성이 있기 때문입니다. 이 명백하게 간단한 예제에서 볼 수 있듯이 C++의 테일 호출은 표면에서 보이는 것처럼 자주 하찮은 것이 아니며 옵티 마이저가이를 생성하도록 설득하는 것은 놀랍지 않게 어려울 수 있습니다.