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
23
非类型参数会梦见 OOP 吗?
lichray
0
85
动态库,是得多动态?
lichray
0
35
Dynamically Loaded Libraries Outside the Standard
lichray
0
280
Thinking in Immediate: ImGUI
lichray
0
170
The Many Shades of reference_wrapper
lichray
0
170
Are We Macro-free Yet?
lichray
0
160
vvpkg: A cross-platform data deduplication library in C++14
lichray
0
97
Understanding Git
lichray
0
42
Other Decks in Programming
See All in Programming
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
5
910
創造的活動から切り拓く新たなキャリア 好きから始めてみる夜勤オペレーターからSREへの転身
yjszk
1
130
Monixと常駐プログラムの勘どころ / Scalaわいわい勉強会 #4
stoneream
0
280
Amazon S3 NYJavaSIG 2024-12-12
sullis
0
100
競技プログラミングへのお誘い@阪大BOOSTセミナー
kotamanegi
0
360
RWC 2024 DICOM & ISO/IEC 2022
m_seki
0
210
Итераторы в Go 1.23: зачем они нужны, как использовать, и насколько они быстрые?
lamodatech
0
770
The Efficiency Paradox and How to Save Yourself and the World
hollycummins
1
440
責務を分離するための例外設計 - PHPカンファレンス 2024
kajitack
5
820
わたしの星のままで一番星になる ~ 出産を機にSIerからEC事業会社に転職した話 ~
kimura_m_29
0
180
PSR-15 はあなたのための ものではない? - phpcon2024
myamagishi
0
110
Semantic Kernelのネイティブプラグインで知識拡張をしてみる
tomokusaba
0
180
Featured
See All Featured
Fireside Chat
paigeccino
34
3.1k
The Pragmatic Product Professional
lauravandoore
32
6.3k
Build The Right Thing And Hit Your Dates
maggiecrowley
33
2.4k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Thoughts on Productivity
jonyablonski
67
4.4k
Being A Developer After 40
akosma
87
590k
Why Our Code Smells
bkeepers
PRO
335
57k
jQuery: Nuts, Bolts and Bling
dougneiner
61
7.5k
Become a Pro
speakerdeck
PRO
26
5k
A better future with KSS
kneath
238
17k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
28
4.4k
How to Think Like a Performance Engineer
csswizardry
22
1.2k
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