【问题标题】:How do I store the intermediate results of a recursive function using C++ templates at compile time?如何在编译时使用 C++ 模板存储递归函数的中间结果?
【发布时间】:2015-10-30 04:19:06
【问题描述】:

我问了How do I capture the results of a recursive function at compile-time?,但我认为我的方法是错误的。

我有一个这样的程序:

#include <iostream>
#include <list>

std::list<unsigned int> recursive_case(std::list<unsigned int>& result, unsigned int& i) {
    result.push_front(1 + (i % 10));
    i /= 10;
    return i != 0 ? recursive_case(result, i) : result;
}

std::list<unsigned int> initial_case(unsigned int i) {
    std::list<unsigned int> result;
    result.push_back(i % 10);
    i /= 10;
    return i != 0 ? recursive_case(result, i) : result;
}

int main() {
    auto list = initial_case(123);
    bool first = true;
    for (auto i: list) {
        if (first) {
            first = false;
        } else {
            std::cout << ", ";
        }
        std::cout << i;
    }
    std::cout << std::endl;
}

输出是2, 3, 3

我想在 编译时 执行上述计算并获得相同的输出(循环迭代和输出打印将在运行时,即从 for 循环开始的所有内容)。模板似乎是一种可能性(这就是我将这个问题标记为这样的原因),但我愿意接受任何可以在编译时完成工作的东西。

【问题讨论】:

    标签: c++ templates recursion template-meta-programming constexpr


    【解决方案1】:

    您可以在编译时使用constexpr 计算列表。我将递归转换为迭代,并根据需要使用indices trick 调用calculate

    #include <iostream>
    #include <array>
    #include <iterator>
    #include <utility>
    
    constexpr std::size_t count_digits(std::size_t N, std::size_t Count = 0)
    {
      return (N > 0) ? count_digits(N/10, Count+1) : Count; 
    }
    
    constexpr std::size_t ipow(std::size_t N, std::size_t Base)
    {
      return (N > 0) ? Base*ipow(N-1,Base) : 1; 
    }
    
    constexpr std::size_t calculate(std::size_t n, std::size_t i)
    {
        std::size_t p = ipow(i,10);
        std::size_t t = (n/p) % 10; 
        return i > 0 ? (t+1) : t;
    }
    
    template<std::size_t Num, std::size_t C, std::size_t... Is>
    constexpr std::array<std::size_t, C> build_list(std::index_sequence<Is...>)
    {
      return {{ calculate(Num, C-Is-1)... }};
    }
    
    template <std::size_t Num, std::size_t C = count_digits(Num)>
    constexpr auto build_list()
    {
      return build_list<Num, C>(std::make_index_sequence<C>{});
    }
    
    
    int main()
    {
        constexpr auto list = build_list<123>();
    
        for(auto e : list)
        {
            std::cout << e << " ";
        }
    
        return 0;
    }
    

    输出:

    2 3 3 
    

    live example

    【讨论】:

      【解决方案2】:

      这是一个解决方案。

      #include <iostream>
      
      // Print one digit.
      template <unsigned int N, bool Initial> struct AtomicPrinter
      {
         static void print()
         {
            std::cout << N%10;
         }
      };
      
      template <unsigned int N> struct AtomicPrinter<N, false>
      {
         static void print()
         {
            std::cout << 1 + N%10 << ", ";
         }
      };
      
      // Recursive printer for a number
      template <unsigned int N, bool Initial> struct Printer
      {
         static void print()
         {
            Printer<N/10, false>::print();
            AtomicPrinter<N, Initial>::print();
         }
      };
      
      // Specialization to end recursion.
      template <bool TF> struct Printer<0, TF>
      {
         static void print()
         {
         }
      };
      
      void printList()
      {
         Printer<123, true>::print();
         std::cout << std::endl;
      }
      
      int main() {
         printList();
      }
      

      如果需要将数字的打印与构建数字列表分开,您可以使用:

      #include <iostream>
      #include <list>
      
      template <unsigned int N, bool Initial> struct Digit
      {
         static void get(std::list<int>& l)
         {
            l.push_back(N%10);
         }
      };
      
      template <unsigned int N> struct Digit<N, false>
      {
         static void get(std::list<int>& l)
         {
            l.push_back(1 + N%10);
         }
      };
      
      template <unsigned int N, bool Initial> struct Digits
      {
         static void get(std::list<int>& l)
         {
            Digits<N/10, false>::get(l);
            Digit<N, Initial>::get(l);
         }
      };
      
      template <bool TF> struct Digits<0, TF>
      {
         static void get(std::list<int>& l)
         {
         }
      };
      
      void printList()
      {
         std::list<int> l;
         Digits<123, true>::get(l);
         bool first = true;
         for (auto i: l) {
            if (first) {
               first = false;
            } else {
               std::cout << ", ";
            }
            std::cout << i;
         }
         std::cout << std::endl;
      }
      
      int main() {
         printList();
      }
      

      【讨论】:

      • 我不能在 constexpr 函数中使用 std::list。不过感谢您的尝试。
      【解决方案3】:

      您可以在编译时使用以下内容来拆分数字:

      #include <utility>
      #include <iostream>
      
      template <char... Cs>
      std::integer_sequence<char, Cs...> operator "" _seq() { return {}; }
      
      template <char...Cs>
      void print(std::integer_sequence<char, Cs...>)
      {
          const char* sep = "";
          for (const auto& c : {Cs...}) {
              std::cout << sep << c;
              sep = ", ";
          }
      }
      
      int main() {
          auto seq = 123_seq;
          print(seq);
      }
      

      Demo

      【讨论】:

      • 很好,但它并没有真正解决 OP 的问题:“如何存储递归函数的中间结果...”
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-08
      • 2017-06-28
      • 1970-01-01
      • 1970-01-01
      • 2020-05-22
      • 2015-05-30
      • 1970-01-01
      相关资源
      最近更新 更多