Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Lambda 和它们的环境
Search
Zhihao Yuan
December 25, 2020
Programming
1
140
Lambda 和它们的环境
C++ lambda 表达式的应用、理论,和闭包的生命期问题。
视频:
https://bilibili.com/video/BV1BX4y1K7x8
Zhihao Yuan
December 25, 2020
Tweet
Share
More Decks by Zhihao Yuan
See All by Zhihao Yuan
Callable Objects in Java, C#, Rust, and C++
lichray
0
21
非类型参数会梦见 OOP 吗?
lichray
0
80
动态库,是得多动态?
lichray
0
35
Dynamically Loaded Libraries Outside the Standard
lichray
0
250
Thinking in Immediate: ImGUI
lichray
0
160
The Many Shades of reference_wrapper
lichray
0
160
Are We Macro-free Yet?
lichray
0
160
vvpkg: A cross-platform data deduplication library in C++14
lichray
0
92
Understanding Git
lichray
0
42
Other Decks in Programming
See All in Programming
レガシーな Android アプリのリアーキテクチャ戦略
oidy
1
170
Snowflake x dbtで作るセキュアでアジャイルなデータ基盤
tsoshiro
2
430
讓數據說話:用 Python、Prometheus 和 Grafana 講故事
eddie
0
350
/←このスケジュール表に立ち向かう フロントエンド開発戦略 / A front-end development strategy to tackle a single-slash schedule.
nrslib
1
590
macOS でできる リアルタイム動画像処理
biacco42
7
1.8k
現場で役立つモデリング 超入門
masuda220
PRO
13
2.9k
Vaporモードを大規模サービスに最速導入して学びを共有する
kazukishimamoto
4
4.3k
Server Driven Compose With Firebase
skydoves
0
400
C#/.NETのこれまでのふりかえり
tomokusaba
1
160
僕がつくった48個のWebサービス達
yusukebe
18
17k
役立つログに取り組もう
irof
26
8.6k
Java ジェネリクス入門 2024
nagise
0
600
Featured
See All Featured
How to Think Like a Performance Engineer
csswizardry
19
1.1k
It's Worth the Effort
3n
183
27k
The Cult of Friendly URLs
andyhume
78
6k
Building Your Own Lightsaber
phodgson
102
6k
How to train your dragon (web standard)
notwaldorf
88
5.7k
Making Projects Easy
brettharned
115
5.9k
VelocityConf: Rendering Performance Case Studies
addyosmani
325
24k
GitHub's CSS Performance
jonrohan
1030
460k
For a Future-Friendly Web
brad_frost
175
9.4k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Building Flexible Design Systems
yeseniaperezcruz
327
38k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
43
6.6k
Transcript
PURECPP LAMBDA 和它们的环境 ZHIHAO YUAN, SIMPLEROSE INC HPC ENGINEER
PURECPP 嵌套函数
PURECPP 3 LAMBDA 不只是匿名函数 1. 嵌套函数 2. 函数对象 3. 匿名函数
PURECPP 4 PYTHON 嵌套函数 def banner(s: str) -> None: def
asterisks() -> None: print('*' * len(s)) asterisks() print(s) asterisks()
PURECPP 5 C++ 嵌套函数 void banner(string_view s) { auto asterisks
= [=] { println(string(s.size(), '*')); }; asterisks(); println(s); asterisks(); }
PURECPP 6 嵌套函数重要吗? • 「差不多是 LOCAL CLASS 的语法糖」 • 「在
PYTHON 里也没怎么用过」
PURECPP 7 「上下文」 • 我们平常说话是有上下文的 • 程序有上下文更加易读
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>();
PURECPP 9 再试着读一下这个 auto m = xt::load_npz(filename); move_into(matrix_1_, "matrix_1"); move_into(indices_,
"indices");
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");
PURECPP 11 再试着读一下这个 auto m = xt::load_npz(filename); auto move_into =
/* ... */ { /* ... */ }; move_into(matrix_1_, "matrix_1"); move_into(indices_, "indices");
PURECPP 12 「短语」 • 自然语言需要短语来隐去细节 • 绝不是说只使用一次的函数就不能做成函数
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 {
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;
PURECPP 15 定义短语 auto add_leading_zeros = [](char *p, /* ...
*/ }; auto insert_decimal_point = [](char *p, /* ... */ }; auto add_trailing_zeros = [](char *p, /* ... */ };
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
PURECPP 18 「指代」 • 匿名化复杂的工作 • 从函数中返回嵌套函数 对于 C++ 来说,闭包的
生命期会成为一个问题
PURECPP 19 PYTHON 的例子 def fib(): a, b = 0,
1 def next(): nonlocal a, b a, b = (b, a + b) return a return next
>>> f = fib() >>> f() 1 >>> f() 1
>>> f() 2 >>> f() 3 >>> f() 5 >>> f() 8 >>> f() 13
PURECPP 21 C++ 的闭包…? auto fib() { int a =
0, b = 1; return [&] { tie(a, b) = tuple(b, a + b); return a; }; }
Wat 2015 当听说 C++ Lambda 表达 式的引用捕捉在闭包对象 返回之后会成为悬垂引用 时,C. Pitcher
的脸。
PURECPP 概念
PURECPP 24 什么是活动记录 • 函数调用时私有状态的集合 • 取决于语言运行时如何表示本地变量,它可 能是「调用栈」 • 递归函数被调用时,可能有多个活动记录
PURECPP 25 栈的含义是先进后出 void bar() { auto f = fib();
f(); } bar fib f bar
PURECPP 26 如果活动记录不是栈呢? def bar(): f = fib() f() bar
f fib bar fib
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
PURECPP 28 活动记录可以成为嵌套函数的环境 def fib(): a, b = 0, 1
def next(): nonlocal a, b a, b = (b, a + b) return a return next 封闭嵌套函数词法作用域的函数
PURECPP 29 什么是环境 • 活动记录的集合 • 生命期不跟随函数调用
PURECPP 30 什么是闭包 • 带有环境的函数
PURECPP 面向对象
闭包是一个只有 APPLY 方法的对象 – GUY STEELE
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
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
PURECPP 35 闭包很像 OOP 所定义的对象 • 把数据和操作组合在了一起 • 数据是环境
def cell_inspector(): i = 0 j = 0 def index_control(A):
nonlocal i, j # ... def update(A): # ... return update
class cell_inspector: def __init__(self): self.i = 0 self.j = 0
def __index_control(self, A): # ... def __call__(self, A): # ...
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 { /* ... */ }
PURECPP 39 例子:闭包和 OOP 的另一个类比方式 • 用 LAMBDA 实现抽象数据类型 PAIR
• 有 FIRST 和 SECOND 两个方法
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
PURECPP 41 闭包 • 闭包可以主动把环境解包传给另一个函数 • 能使用函数 A 的环境的函数不一定是 A
词 法作用域内定义的函数 • 闭包的「闭」字指的是被环境封闭的变量, 而非被词法作用域封闭的嵌套函数
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; }); }
PURECPP 43 C++ 闭包对象的环境 • 对于 C++ LAMBDA 表达式来说,值捕捉列表 组成了闭包对象的环境
• 推广到类,函数对象的数据部分即环境
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
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)(); };
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)); }); }
PURECPP 47 简单对象模型 VS. C++ 对象模型? struct fib { int
a; int b; int (*next)(); }; struct fib { int a; int b; }; int fib_next(*__this);
PURECPP 结语
PURECPP 49 总结 • LAMBDA 给 C++ 编程带来了上下文和短语 • 值捕捉列表给
C++ 闭包对象创建了环境 • 环境的意义在于封闭变量
PURECPP 50 思考题 Q: C++ 里有没有自身活动记录不遵循先进后 出的函数? A: 有的,COROUTINE(协程)
QUESTIONS? @LICHRAY