模板元编程:1到n的总和

时间:2017-07-10 12:47:42

标签: c++ c++11 template-meta-programming

是否有通过模板元编程计算连续数字总和的智能方法? 这将是例如1到100的非模板算法。

import csv
import shapefile # pyshp module

def decoder(records):
    for record in records:
        rec = []
        for element in record:
            try:
                ele = element.decode('utf-8')
                rec += [ele]
            except: # since str and int have no attribute decode
                ele = element # done for consistency 
                rec += [ele]
        yield rec # easy generator

sf = shapefile.Reader('/path/to/a/shapefile')
my_file = '/path/to/a/csv'

records = [record for record in decoder(sf.iterRecords())]

with open(my_file, 'w', encoding='utf-8', newline='') as f:
    w = csv.writer(f, delimiter=',')
    for rec in records:
        w.writerow(rec)

我考虑使用可变参数添加函数并用参数列表填充它。但是我不确定如何创建参数列表。

int i = 0;
for (int n = 1; n <= 100; n++)
  i += n;

return i;

5 个答案:

答案 0 :(得分:6)

template <int n>
struct SumOfNumbers {
  static constexpr int value = n*(n+1)/2;
};

答案 1 :(得分:3)

如果代码置于constexpr函数内,则可以在编译时执行。

constexpr int sum(int x)
{
    int i = 0;
    for (int n = 1; n <= x; n++)
       i += n;

    return i;
}

如果你想在模板中使用它,你可以这样做:

template <int n>
struct sum_t {
  static constexpr int value = sum(n);
};

答案 2 :(得分:1)

虽然answer by Igor是解决问题的一个很好的答案,但以下方法更类似于for循环。

template <int n>
struct AddHelper { static const int value = n + AddHelper<n-1>::value; };

template <>
struct AddHelper<0> { static const int value = 0; };

template <int n>
int add() { return AddHelper<n>::value; }

答案 3 :(得分:1)

简单的方法是使用constexprconstexpr,特别是在C ++ 14中,允许您获取运行时代码并使其编译时间。

在C ++ 11中,你必须通过递归获得愚蠢。幸运的是2017年,即C ++ 14发布后的几年,所以你不必使用C ++ 11。如果您被迫,可以在问题template中标记constexpr实施。

然后

template<int...Is>
constexpr int add() { return add(0, Is...); }
如果您只是必须在<> s内传递参数,

就可以了。

有时人们在想要一般解决方案时会问这些问题,而只选择add只针对特定问题,而不是意识到它包含的问题会使问题变得更容易。

如果失败了,对二进制加法的左或右折叠可以起作用。二进制左或右折叠的缺点是它具有O(n)模板实例化深度和O(n ^ 2)总模板名称长度。这两者都限制了您可以添加的序列的长度,并使其非常慢并且消耗内存。

在C ++ 17中,处理它的方法是使用内置的折叠操作。如果要折叠的二进制操作不是内置的操作,请使用操作符重载和包装类型强制在执行语言内置折叠时调用它。这是C ++ 17,你的问题是C ++ 11,所以我不会详细介绍。

在C ++ 11中,您需要折叠一些任意对称二进制操作,您可以使用对数递归深度获取一个东西并将其分解为二叉树。

这很棘手;基本的想法是编写一个函数,在列表中将列表拆分为两个,然后使用前缀和后缀列表进行处理。

然后,在伪代码中,如果我们有where个索引,只有在list={L0, Ls...}被提及时才会L0(否则它只是一些列表)。

split( where, before, list )
if where=0, answer is before, list
if where=1, answer is before+{L0}, {Ls...}
otherwise, is:
  using tmp[2] = split((where+1)/2, before, list)
  answer is split( where/2, tmp[0], tmp[1])

诀窍是我们做了很多实例化,但递归深度很短。

一旦我们得到split,我们就可以获取一个元素列表,构建一个快速平衡的二叉树,并将二元运算符应用于每对元素,并再次以对数深度求解树。

老实说,这比升级编译器更麻烦。

TL; DR:将您的编译器升级到,这很容易。

如果失败,请尝试并添加constexpr。现在按原样使用,或转发

template<int...Is>
constexpr int add()

到它。

答案 4 :(得分:1)

  

我考虑使用可变参数添加函数并用参数列表填充它。但是我不知道如何创建参数列表。

首先,我建议您遵循Igor Tandetnik(+1)的基于高斯的解决方案。

第二:我不知道如何创建参数列表,但如果你接受std::integer_sequence(所以参数列表固定编译时间),从C ++ 14开始你可以使用{{ 1}}。

在这种情况下,您的std::make_integer_sequence函数无需递归,如以下示例所示

add()

在C ++ 17中,解决方案更简单

#include <utility>

template <typename T, T ... Is>
constexpr T add (std::integer_sequence<T, Is...> const & a)
 {
   using unused = int[];

   T sum {};

   (void)unused { 0, ( sum += Is, 0 )... };

   return sum;
 }


int main ()
 {
   constexpr int sum1 { (100 * (100 + 1)) >> 1 }; // Gauss
   constexpr int sum2 { add(std::make_integer_sequence<int, 100+1>{}) };

   static_assert( sum1 == sum2, "!");
 }