Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Lambda 和它们的环境

Zhihao Yuan
December 25, 2020

Lambda 和它们的环境

C++ lambda 表达式的应用、理论,和闭包的生命期问题。

视频: https://bilibili.com/video/BV1BX4y1K7x8

Zhihao Yuan

December 25, 2020
Tweet

More Decks by Zhihao Yuan

Other Decks in Programming

Transcript

  1. PURECPP 4 PYTHON 嵌套函数 def banner(s: str) -> None: def

    asterisks() -> None: print('*' * len(s)) asterisks() print(s) asterisks()
  2. PURECPP 5 C++ 嵌套函数 void banner(string_view s) { auto asterisks

    = [=] { println(string(s.size(), '*')); }; asterisks(); println(s); asterisks(); }
  3. PURECPP 8 试着读一下这个 auto m = xt::load_npz(filename); matrix_1_ = std::move(m["matrix_1"]).cast<double>();

    indices_ = std::move(m["indices"]).cast<int64_t>(); vec_1_ = std::move(m["vec_1"]).cast<double>(); vec_2_ = std::move(m["vec_2"]).cast<double>(); m2_data_ = std::move(m["m2_data"]).cast<double>(); m2_ind_ = std::move(m["m2_ind"]).cast<int>(); m2_ptr_ = std::move(m["m2_ptr"]).cast<int>();
  4. PURECPP 10 再试着读一下这个 auto m = xt::load_npz(filename); auto move_into =

    [&]<class T>(T &dest, auto key) { using t = xt::xvalue_type_t<T>; dest = std::move(m[key]).cast<t>(); }; move_into(matrix_1_, "matrix_1"); move_into(indices_, "indices");
  5. PURECPP 11 再试着读一下这个 auto m = xt::load_npz(filename); auto move_into =

    /* ... */ { /* ... */ }; move_into(matrix_1_, "matrix_1"); move_into(indices_, "indices");
  6. PURECPP 13 如果没有短语… if (offset < 1) // 0.0031415 {

    auto len = std::distance(p, last); std::move(p, last, p + 2 - offset); p[0] = '0'; p[1] = '.'; p = std::fill_n(p + 2, -offset, '0') + len; } else if (offset < decimal_length) // 3.1415 {
  7. PURECPP 14 一句话就能占一个段落 auto end_of_integer_part = p + offset; std::move(end_of_integer_part,

    last, std::next(end_of_integer_part)); *end_of_integer_part = '.'; p = std::next(last); } else if (offset > decimal_length) // 31415000 p = std::fill_n(last, offset - std::distance(p, last), '0'); else // 31415 p = ptr;
  8. PURECPP 15 定义短语 auto add_leading_zeros = [](char *p, /* ...

    */ }; auto insert_decimal_point = [](char *p, /* ... */ }; auto add_trailing_zeros = [](char *p, /* ... */ };
  9. PURECPP 17 让逻辑主体保持流畅 if (offset < 1) p = add_leading_zeros(p,

    ptr, offset); // 0.0031415 else if (offset < decimal_length) p = insert_decimal_point(p, ptr, offset); // 3.1415 else if (offset > decimal_length) p = add_trailing_zeros(p, ptr, offset); // 31415000 else p = ptr; // 31415
  10. PURECPP 19 PYTHON 的例子 def fib(): a, b = 0,

    1 def next(): nonlocal a, b a, b = (b, a + b) return a return next
  11. >>> f = fib() >>> f() 1 >>> f() 1

    >>> f() 2 >>> f() 3 >>> f() 5 >>> f() 8 >>> f() 13
  12. PURECPP 21 C++ 的闭包…? auto fib() { int a =

    0, b = 1; return [&] { tie(a, b) = tuple(b, a + b); return a; }; }
  13. PURECPP 27 如果活动记录不是栈呢? bar f = next fib a b

    bar def fib(): a, b = 0, 1 def next(): nonlocal a, b a, b = (b, a + b) return a return next fib a b
  14. PURECPP 28 活动记录可以成为嵌套函数的环境 def fib(): a, b = 0, 1

    def next(): nonlocal a, b a, b = (b, a + b) return a return next 封闭嵌套函数词法作用域的函数
  15. PYIMGUI update() 60 times per second def cell_inspector(): i, j

    = (0, 0) def index_control(A): nonlocal i, j # ... def update(A): # ... return update
  16. def cell_inspector(): i, j = (0, 0) def index_control(A): nonlocal

    i, j M, N = A.shape _, i = imgui.drag_int('i', i, max_value=M - 1) _, j = imgui.drag_int('j', j, max_value=N - 1) def cell_info(A): v = A[i, j] imgui.text('value: {}'.format(v)) imgui.text('fraction: {}'.format(Fraction(v).limit_denominator(10000))) def update(A): imgui.begin('Cell Inspector') index_control(A) cell_info(A) imgui.end() return update
  17. def cell_inspector(): i = 0 j = 0 def index_control(A):

    nonlocal i, j # ... def update(A): # ... return update
  18. class cell_inspector: def __init__(self): self.i = 0 self.j = 0

    def __index_control(self, A): # ... def __call__(self, A): # ...
  19. class cell_inspector { int i = 0; int j =

    0; void index_control(auto &A) { /* ... */ } public: void operator()(auto &A) { /* ... */ } }; [i = 0, j = 0](auto &A) mutable { /* ... */ }
  20. PURECPP 40 用 LAMBDA 实现抽象数据类型 def pair(a, b): return lambda

    m: m(a, b) def first(g): return g(lambda a, _: a) def second(g): return g(lambda _, b: b) 用法: >>> z = pair(3, 4) >>> first(z) 3 >>> second(z) 4
  21. PURECPP 41 闭包 • 闭包可以主动把环境解包传给另一个函数 • 能使用函数 A 的环境的函数不一定是 A

    词 法作用域内定义的函数 • 闭包的「闭」字指的是被环境封闭的变量, 而非被词法作用域封闭的嵌套函数
  22. PURECPP 42 用 LAMBDA 实现抽象数据类型(C++) auto pair(auto a, auto b)

    { return [=](auto m) { return m(a, b); }; } auto first(auto const &g) { return g([](auto &a, auto &) { return a; }); } auto second(auto const &g) { return g([](auto &, auto &b) { return b; }); }
  23. PURECPP 44 反思 def fib(): a, b = 0, 1

    def next(): nonlocal a, b a, b = (b, a + b) return a return next fib a = 0 b = 1 next = function
  24. PURECPP 45 反思 def fib(): a, b = 0, 1

    def next(): nonlocal a, b a, b = (b, a + b) return a return next struct fib { int a; int b; int (*next)(); };
  25. PURECPP 46 对比解开闭包的实现 auto fib() { int a = 0,

    b = 1; return [=](auto m) mutable { return m(a, b); }; } auto fib_next(auto &__this) { return __this([](auto &a, auto &b) { return get<0>(tie(a, b) = tuple(b, a + b)); }); }
  26. PURECPP 47 简单对象模型 VS. C++ 对象模型? struct fib { int

    a; int b; int (*next)(); }; struct fib { int a; int b; }; int fib_next(*__this);
  27. PURECPP 49 总结 • LAMBDA 给 C++ 编程带来了上下文和短语 • 值捕捉列表给

    C++ 闭包对象创建了环境 • 环境的意义在于封闭变量