【问题标题】:C++ functional programming code snippetsC++ 函数式编程代码片段
【发布时间】:2016-07-12 07:46:52
【问题描述】:

我一直在从事一个名为:C++11/14 的函数式编程特性的项目(针对我在大学的一门学科)。 关于这些主题有几个现有的资源和类似的演示文稿,我找到了一个 不久前,其中包含几个我还没有完全理解的代码 sn-ps(并且它们可以以某种方式连接到函数式编程)。片段AB属于递归C属于惰性求值。我想在下面与您分享:

片段 A

#include <iostream>

template <int N>
struct Factorial {
    static int const val = N * Factorial<N - 1>::val;
};

template <>
struct Factorial <0> {
    static int const val = 1;
};

int main() {
    int factorial_of_6 = Factorial<6>::val;
    std::cout << factorial_of_6 << std::endl;
    return 0;
}

这里的重点是编译时评估(为了避免运行时计算并提高性能)吗? 或者还有其他优势吗?

片段 B

#include <iostream>

template <int ...>
struct my_sum;

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

template <int i, int ... tail>
struct my_sum <i, tail ...> {
    static const int value = i + my_sum<tail ...>::value;
};

int main() {
    int sum {my_sum<1, 2, 3, 4, 5>::value};
    std::cout << sum << std::endl;
    return 0;
}

同样的问题适用于上述。

这是另一个可能类似的 sn-p:

片段 C

#include <iostream>

template <typename... Args>
void some_function (Args ...) {
    std::cout << sizeof...(Args) << std::endl;
}

int main() {
    some_function ("Every little thing gonna be alright...", 1.0 / 0.0);
    return 0;
}

“演示文稿中说:C++ 是急切的,但以下将起作用。” 是不是在我不关心给定的表达式之前,我才能知道它们的数量?

请尽可能具体和详细,非常感谢您提前的耐心和帮助。 :)

【问题讨论】:

  • 对于 C,args 不是惰性求值的。有评估(然后使用 as-if 规则进行优化可能会丢弃它们)。

标签: c++ c++11 recursion functional-programming variadic-templates


【解决方案1】:

片段 A

这称为Template Metaprogramming,它基本上是一种使用模板在编译时生成代码的技术。这提高了运行时性能,因为计算不是在运行时完成的,而是在编译时完成的。

Snippet A 在编译时计算给定数字的阶乘:

template <int N>
struct Factorial {
    static int const val = N * Factorial<N - 1>::val;
};

这将struct Factorial 定义为接受int 的模板。在那个struct 中,有一个static const 变量。变量为static,因此您不必创建Factorial 的实例来访问它,您可以使用Factorial::val 而不是

Factorial factorial;
factorial.val;

变量是const,因为给定数字的阶乘总是相同的,如果不是const,项目将无法编译,因为编译器无法知道您是否更改其他地方的变量。

变量的值为N * Factorial&lt;N - 1::val;,它基本上将N 与前一个数字的阶乘相乘。这是因为阶乘的定义方式 (3! = 2! * 3 = 1! * 2 * 3 = 1 * 2 * 3 = 6)。

template <>
struct Factorial <0> {
    static int const val = 1;
};

这为N = 0 定义了一个完全专用的struct。这点真的很重要,否则前面函数中用到的递归永远不会停止。

然后,得到一个数字N 的阶乘很容易,Factorial&lt;N&gt;::val。这将在编译时计算。


片段 B

这也是Template Metaprogramming

template <int ...>
struct my_sum;

这定义了一个空模板struct,它接受一个int...(一个Parameter Pack),以便它可以被特化(见下一点)。

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

这专门用于structmy_sum,当没有给出模板参数时(这是因为Parameter Pack,它可以是空的,所以当Parameter Pack展开时模板参数将为空) . valuestaticconst,原因同上,使用initializer list 初始化0(对于intint i = 0; 和@987654359 没有区别@)。

template <int i, int ... tail>
struct my_sum <i, tail ...> {
    static const int value = i + my_sum<tail ...>::value;
};

这将struct my_sum 定义为一个模板,它接受两个模板参数,一个int 和一个int 参数包。这用于获取参数包的第一个值的值,因为您不能索引参数包(它不是数组)。然后将value初始化为i(包的第一个值)加上其他值的value作为参数包,展开(使用...):

int sum = my_sum<1, 2, 3>::value;

这调用my_sum&lt;int i, int... tail&gt;i1tail2, 3valuei + my_sum&lt;tail...&gt;::value,所以它是 1 + my_sum&lt;2, 3&gt;my_sum&lt;2, 3&gt; 再次调用相同的函数,2 + my_sum&lt;3&gt;::value。现在我们有1 + 2 + my_sum&lt;3&gt;::valuemy_sum&lt;3&gt;::value 再次调用相同的函数,但现在参数包为空!所以value1 + 2 + 3 + my_sum&lt;&gt;::valuemy_sum&lt;&gt;::value0(定义),所以 value = 1 + 2 + 3 + 0


片段 C

表达式已计算,但程序不会崩溃,因为计算时的表达式是 double。只有当表达式为int 时,它才会以Integer division by zero exception 崩溃。如果您要这样做:

int zero = 0; 
double d = 1.0 / zero;

那么d 的值将是inf

函数some_function是一个模板函数,它接受一个参数包作为模板参数。然后它调用sizeof... 计算参数包中的元素,并使用std::cout 输出。

【讨论】:

  • 非常感谢您的详细回复!但是如果您不介意,我想再问一个:我得到了您对 C 的解释,但是如果我使用整数,这是否有意义?表达式将被计算并且程序崩溃。这甚至类似于惰性评估吗? (就像上面提到的 Jarod42。)
  • @ZsoltLászló 我不这么认为,因为正如 Jarod42 所说,C++ 中没有内置的惰性求值
  • 明白,谢谢。顺便说一句,从那以后我已经设法运行代码:将 -O3 放入编译器选项可以避免异常(并且输出是预期的 2)。
猜你喜欢
  • 2011-08-05
  • 2010-09-14
  • 1970-01-01
  • 2014-03-20
  • 2014-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多