Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Lambda 和它们的环境
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Zhihao Yuan
December 25, 2020
Programming
260
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Lambda 和它们的环境
C++ lambda 表达式的应用、理论,和闭包的生命期问题。
视频:
https://bilibili.com/video/BV1BX4y1K7x8
Zhihao Yuan
December 25, 2020
More Decks by Zhihao Yuan
See All by Zhihao Yuan
DWARF 眼中的 C++
lichray
0
54
Callable Objects in Java, C#, Rust, and C++
lichray
0
74
非类型参数会梦见 OOP 吗?
lichray
0
180
动态库,是得多动态?
lichray
0
120
Dynamically Loaded Libraries Outside the Standard
lichray
0
490
Thinking in Immediate: ImGUI
lichray
0
260
The Many Shades of reference_wrapper
lichray
0
260
Are We Macro-free Yet?
lichray
0
180
vvpkg: A cross-platform data deduplication library in C++14
lichray
0
200
Other Decks in Programming
See All in Programming
作れる、でも消せるか?──安全に「枯らして消す」機能削除術 / Drain, Then Delete
chobishiba
2
280
golang-bob.pdf
iwatsukayura
0
130
Androidだけじゃない、DroidKaigiで広がった私の技術スタック
akkeylab
0
130
ハーネス設計入門 〜 基礎知識の整理から実務へのステップアップ 〜
kinopeee
22
22k
【加筆修正版】Laravel のアプリケーションをどこにデプロイするか #phpcon_ehime
akase244
0
150
C#の現在地 進化の歴史と、AI時代の.NET Everywhere
neuecc
5
5.1k
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
470
Event is what happens
moro
2
530
最近”作らせてる” ローカルAIアプリの話
akit37
0
460
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
410
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
490
wkhtmltopdfの次どうするか問題2026
willnet
2
1.9k
Featured
See All Featured
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
370
Designing for Performance
lara
612
70k
Making Projects Easy
brettharned
120
6.8k
Raft: Consensus for Rubyists
vanstee
142
7.8k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
The SEO identity crisis: Don't let AI make you average
varn
0
590
Designing for humans not robots
tammielis
254
26k
Deep Space Network (abreviated)
tonyrice
0
370
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Amusing Abliteration
ianozsvald
1
340
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
750
The Curious Case for Waylosing
cassininazir
1
570
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