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
170
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
34
非类型参数会梦见 OOP 吗?
lichray
0
110
动态库,是得多动态?
lichray
0
64
Dynamically Loaded Libraries Outside the Standard
lichray
0
420
Thinking in Immediate: ImGUI
lichray
0
210
The Many Shades of reference_wrapper
lichray
0
210
Are We Macro-free Yet?
lichray
0
160
vvpkg: A cross-platform data deduplication library in C++14
lichray
0
150
Understanding Git
lichray
0
45
Other Decks in Programming
See All in Programming
PHPに関数型の魂を宿す〜PHP 8.5 で実現する堅牢なコードとは〜 #phpcon_hiroshima / phpcon-hiroshima-2025
shogogg
1
340
Cursorハンズオン実践!
eltociear
2
1.2k
AI時代に必須!状況言語化スキル / ai-context-verbalization
minodriven
2
180
スマホから Youtube Shortsを見られないようにする
lemolatoon
27
34k
AI駆動で0→1をやって見えた光と伸びしろ
passion0102
1
880
SODA - FACT BOOK(JP)
sodainc
1
8.9k
NIKKEI Tech Talk#38
cipepser
0
300
Building, Deploying, and Monitoring Ruby Web Applications with Falcon (Kaigi on Rails 2025)
ioquatix
4
2.6k
Claude Agent SDK を使ってみよう
hyshu
0
1.4k
pnpm に provenance のダウングレード を検出する PR を出してみた
ryo_manba
1
160
CSC509 Lecture 07
javiergs
PRO
0
250
フロントエンド開発のためのブラウザ組み込みAI入門
masashi
7
3.6k
Featured
See All Featured
Optimising Largest Contentful Paint
csswizardry
37
3.5k
We Have a Design System, Now What?
morganepeng
53
7.8k
The Invisible Side of Design
smashingmag
302
51k
KATA
mclloyd
PRO
32
15k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.7k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Fireside Chat
paigeccino
41
3.7k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
31
2.7k
Keith and Marios Guide to Fast Websites
keithpitt
411
23k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.5k
A better future with KSS
kneath
239
18k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
190
55k
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