【问题标题】:How can I find the depth of a recursive function in C++如何在 C++ 中找到递归函数的深度
【发布时间】:2011-05-02 15:56:23
【问题描述】:

如何在 C++ 中的递归函数中找到当前深度而不传递上一层?即是否有可能知道函数被调用了多少次而不使用参数来跟踪级别并在每次调用函数时将该数字作为参数传递?

例如我的递归函数如下所示:

DoSomething(int level)
{
  print level;
  if (level > 10)
    return;
  DoSomething(++level);
}

main
{
  DoSomething(0);
}

【问题讨论】:

  • 你可能想看看this discussion。最重要的是,可能有一种特定于编译器/平台的方式来获取回溯并使用它......但它显然是非常特定于编译器/平台的,并且可能容易受到内联等事情的影响。无论如何,可能值得一看。

标签: c++ recursion


【解决方案1】:

基于JoshD 已经给出的答案:

void recursive() 
{ 
    static int calls = 0;
    static int max_calls = 0;
    calls++;
    if (calls > max_calls)
        max_calls = calls;

    recursive();

    calls--;
}

这会在递归函数完成后重置计数器,但仍会跟踪递归的最大深度。

除了快速测试外,我不会使用这样的静态变量,很快就会被删除。如果您确实需要持续跟踪这一点,有更好的方法。

【讨论】:

    【解决方案2】:

    您可以在函数中使用静态变量...

    void recursive()
    {
     static int calls = 0;
     calls++;
     recursive();
    }
    

    当然,当您开始新的始发呼叫时,这将继续计数......

    【讨论】:

    • 是的,这是个问题。我将如何重置通话?
    【解决方案3】:

    如果你希望它是可重入和线程安全的,为什么不呢:

    void rec(int &level)  // reference to your level var
    {
       // do work
    
       rec(++level); // go down one level
    }
    
    main()
    {
       //and you call it like
       int level=0;
       rec(level);
    
       cout<<level<<" levels."<<endl;
    }
    

    没有静态/全局变量来搞乱线程,您可以为不同的递归链使用不同的变量来解决重入问题。

    【讨论】:

    • 是的,当一个普通的基于堆栈的变量就可以了
    • static 的使用提供了最初的灵感。然后,当然,应该考虑多线程,这意味着在进行递归调用时必须将某些东西传播到参数中。这是我第一次发现需要计算递归深度。
    【解决方案4】:

    如果您不关心线程安全,可以使用局部静态变量。

    虽然,这只会在您第一次运行递归例程时为您提供正确的计数。更好的技术是包含内部静态变量的 RAII 保护类型类。在递归例程开始时,构造保护类。构造函数会增加内部静态变量,而析构函数会减少它。这样,当您创建一个新的堆栈帧时,计数器会加一,当您从每个堆栈帧返回时,计数器会减一。

    struct recursion_guard
    {
        recursion_guard() { ++counter; }
    
        ~recursion_guard() { --counter; }
    
        static int counter;
    };
    
    int recursion_guard::counter = 0;
    
    void recurse(int x)
    {
        recursion_guard rg;
        if (x > 10) return;
        recurse(x + 1);
    }
    
    int main()
    {
        recurse(0);
        recurse(0);
    }
    

    但是请注意,这仍然不是线程安全的。如果您需要线程安全,您可以使用boost::thread_specific_ptr 或 C++0x 线程本地工具将静态存储变量替换为线程本地存储变量。

    【讨论】:

    • 我在想这样的事情,但你打败了我。请注意,guard-class 仍然存在线程不安全的问题。
    • @Mark Ransom,真的。一个简单的改进是将静态存储变量替换为线程本地存储变量,使用boost::thread_specific_ptr 或 C++0x 线程本地工具。
    【解决方案5】:

    如果可以在编译时确定,您也可以将级别作为模板参数传入。您也可以使用函数对象。这无疑是最好的选择 - 更少的麻烦,并且应尽可能避免使用静态变量。

    struct DoSomething {
        DoSomething() {
            calls = 0;
        }
        void operator()() {
            std::cout << calls;
            calls++;
            if (calls < 10)
                return operator()();
            return;
        }
        int calls;
    };
    
    int main() {
        DoSomething()(); // note the double ().
        std::cin.get();
    }
    

    【讨论】:

      【解决方案6】:

      level 转换为能够包含参数和(可能)函数的新对象(通常是模板)的实例变量。那么你可以重用递归累加器接口。

      【讨论】:

        【解决方案7】:

        您也可以尝试使用全局变量来记录深度。

        var depth = 0;
        
        DoSomething()
        {
          print ++depth;
          if (depth > 10)
            return;
          DoSomething();
        }
        
        main
        {
          DoSomething(0);
        }
        

        【讨论】:

        • 全局只是一个范围较松的静态变量。
        • 我会说静态实际上只是一个全局<.>
        【解决方案8】:

        当我感觉到需要一些递归时,我来到这里,因为我正在实现一个可以验证证书链中信任链的函数。这不是 X.509,而是证书的颁发者密钥必须与签名者的公钥匹配的基础知识。

        bool verify_chain(std::vector<Cert>& chain,
                      Cert* certificate,
                      unsigned char* pOrigin = nullptr, int depth = 0)
        {
            bool flag = false;
        
            if (certificate == nullptr) {
                // use first element in case parameter is null
                certificate = &chain[0];        
            }
        
            if (pOrigin == nullptr) {
                pOrigin = certificate->pubkey;            
            } else {
                if (std::memcmp(pOrigin, certificate->pubkey, 32) == 0) {
                    return false; // detected circular chain
                }
            }
        
            if (certificate->hasValidSignature()) {
                if (!certificate->isRootCA()) {
                    Cert* issuerCert = certificate->getIssuer(chain);
                    if (issuerCert) {
                        flag = verify_chain(chain, issuerCert, pOrigin, depth+1);
                    }
                } else {
                    flag = true; 
                }
            }
        
            if (pOrigin && depth == 1) {
                pOrigin = nullptr;
            }
        
            return flag;
        }
        

        我需要知道递归深度,以便正确清理pOrigin。在调用堆栈展开期间位于右侧堆栈帧。

        我使用pOrigin 来检测一个循环链,没有它递归调用可以永远持续下去。例如,

        • cert0 签署 cert1
        • cert1 签署 cert2
        • cert2 签署 cert0

        我后来意识到一个简单的for-loop 可以在只有一个公共链的简单情况下做到这一点。

        bool verify_chain2(std::vector<Cert> &chain, Cert& cert)
        {
            Cert *pCert = &cert;
            unsigned char *startkey = cert.pubkey;
        
            while (pCert != nullptr) {
                if (pCert->hasValidSignature()) {
                    if (!pCert->isRootCA()) {
                        pCert = pCert->getIssuer(chain);
                        if (pCert == nullptr
                        || std::memcmp(pCert->pubkey, startkey, 32) == 0) {
                            return false;
                        }
                        continue;
                    } else {
                        return true;
                    }
                } else {
                    return false;
                }
            }
        
            return false;
        }
        

        但是当没有一个公共链而是该链在每个证书内时,递归是必须的。我欢迎任何cmets。谢谢。

        【讨论】:

        • 如果您有新问题,请点击 按钮提出问题。如果有助于提供上下文,请包含指向此问题的链接。 - From Review
        猜你喜欢
        • 2017-12-05
        • 2015-04-14
        • 2016-06-14
        • 2017-05-09
        • 1970-01-01
        • 2018-10-22
        • 1970-01-01
        • 2012-03-12
        • 1970-01-01
        相关资源
        最近更新 更多