文章

【翻译】用于科学计算的 C++11/14 新特性之五

总结 C++11/14 中与科学计算相关的新特性。

原文地址 www.numbercrunch.de

【翻译】用于科学计算的 C++11/14 新特性之五

$\lambda$ functions

匿名函数,通常称为 $\lambda$ 函数,是科学编程语言(例如 Maple 和 Matlab)的常见特性。它们在需要将其他函数作为参数的函数中特别有用。例如,数值求根算法需要指定一个函数作为输入参数。

C++11 将 $\lambda$ 函数引入到了 C++ 中,而在 C++14 中它们得到了扩展,变得更加灵活。$\lambda$ 函数可以像仿函数一样赋值给变量。$\lambda$ 函数相对于仿函数的主要优势在于它们可以在需要的地方定义。此外,$\lambda$ 函数捕获当前作用域中的变量,因此它们代表了一个闭包。

$\lambda$ 函数的语法基本上有以下形式:

1
[ capture-list ] ( params ) -> ret { body }

可能为空的捕获列表指定了哪些变量被捕获,以及它们是如何被捕获的(按值或按引用)。然后指定参数和返回类型,接着是函数体的定义。如果函数体只包含一个带有表达式的单一返回语句,则可以省略返回类型。

$\lambda$ 函数的使用可能最好是通过提供一个具体的例子来解释。以下是一个通用的求根算法(试错法)的实现,它在某个区间内寻找某个函数的根。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <cmath>
#include <limits>
 
template<typename T, typename F>
T regula_falsi(T s, T t, F f) {
    typedef enum { none, left, right } side_t;
    side_t side(none);
    // starting values at endpoints of interval 
    T fs = f(s);
    T ft = f(t);
    T r;
    for (int n = 0; n < std::numeric_limits<T>::digits + 2; ++n) {
        r = (fs * t - ft * s) / (fs - ft);
        if (std::abs(t - s) < std::numeric_limits<T>::epsilon() * std::abs(t + s))
            break;
        T fr = f(r);
        if (fr * ft > 0) { // fr and ft have same sign, copy r to t
            t = r;
            ft = fr;
            if (side == left)
                fs /= 2;
            side = left;
        }
        else if (fs * fr > 0) { // fr and fs have same sign, copy r to s
            s = r;
            fs = fr;
            if (side == right)
                ft /= 2;
            side = right;
        }
        else { // fr*f_ very small (looks like zero)
            break;
        }
    }
    return r;
}
 
int main() {
  // assign lambda function to variable and use afterwards
    auto f = [](double x) { return std::cos(x) - x * x * x; };
    double x0 = regula_falsi(0., 1., f);
    // specify lambda function directly as an argument
    double x1 = regula_falsi(0., 3., [](double x) { return std::cos(x); });
    std::cout << std::setprecision(12)
        << x0 << '\n'
        << x1 << '\n';
    return 0;
}
本文由作者按照 CC BY 4.0 进行授权