c++ - 自顶向下递归下降解析 : Relying on tail call optimization

标签 c++ parsing c++11 recursion optimization

我正在构建一个递归下降解析器,我有两个构建列表的规则:

ValueList  -> TOKEN_IDENTIFER TOKEN_QUOTE ValueListP

ValueListP -> ValueList
           |  %EPSILON%

现在我知道您可以轻松地将这两个规则优化为带有循环的单个规则,但我也知道编译器可以并且将会在它看到它的地方执行尾调用优化。这是我当前的代码:

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() + "\"" );                                                                                                                                                
}

所以我有两个问题:

1) 我提供的代码是否利用尾调用优化?

2) 即使一段代码确实利用了尾调用优化,作为程序员,我们是否应该在微不足道的情况下尝试自己进行优化(删除递归并用循环代替)?

最佳答案

不,grammarValueList 不执行尾调用。

问题是有两个 std::string 类型的局部变量,它们有一个非平凡的析构函数。这些析构函数必须在方法返回之前调用,也就是在调用 grammarValueListP 之后。所以对 grammarValueListP 的调用不在尾部位置。

当然,可以访问析构函数定义的优化器可能会发现可以过早地析构 var1var2 而不会改变函数的可见行为(假设这是可能的;它部分取决于 ValueNode 构造函数内部发生的情况)。但我不相信大多数 C++ 实现会努力优化尾调用。

就我个人而言,我会使用循环,因为即使您设法消除了析构函数调用,编译器仍有可能找不到 TCO。从这个看似简单的示例中可以看出,C++ 中的尾调用通常并不像表面上看起来那么微不足道,而且说服优化器产生尾调用可能出奇地困难。

关于c++ - 自顶向下递归下降解析 : Relying on tail call optimization,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47480501/

相关文章:

c++ - 从 Qt 外部运行时 Qt5 应用程序崩溃

java - 用于解析简单的基于文本的数据文件的正则表达式

c++ - 在不使用类的情况下隐藏可查询的程序状态?

c++ - 在成员为 const 和列表初始化的情况下,对非静态成员的使用无效?

c++ - 按值传递与按右​​值引用传递

python - 等效的 C++ 时区转换?

c++ - opencv:如何用距中心的距离填充椭圆形状

ios - 仅将 XML 中的 20 个最近项目添加到 NSMutableArray

Java SyndFeedInput 解析提要

C++ 不允许转换为右值引用