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
Zhihao Yuan
December 25, 2020
Programming
240
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
39
Callable Objects in Java, C#, Rust, and C++
lichray
0
71
非类型参数会梦见 OOP 吗?
lichray
0
170
动态库,是得多动态?
lichray
0
110
Dynamically Loaded Libraries Outside the Standard
lichray
0
480
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
190
Other Decks in Programming
See All in Programming
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
1
260
XP祭りでしか伝わらないフリップネタ #xpjug
murabayashi
0
140
App Intentsのビルドプロセスを支える技術
kntkymt
0
290
選挙速報を多くのユーザーへ 届ける Live Activities 設計
hamayokokuririn
0
110
型解析で実現する Go の言語内 DSL / Conference に Go! タイムテーブルの歩き方 for Gophers
mazrean
0
140
Foundry Localでエージェント開発
seosoft
0
180
AGENTS.md Is Not Enough:Build Skills, Don't Download Them
lx_t
0
110
Claude Codeを組織的に動かして月400PRを実現した話
happy_ryo
0
270
thread_parallel_with_free-threaded_Python_and_NumPy.pdf
riku_sakamoto
0
310
Streamlitで実現する自然言語データアプリ開発
ayumu_yamaguchi
0
210
標準パッケージに uuid が追加された 背景から見る Go らしい意思決定 / go_127_uuid_decision
convto
5
6.3k
『寄り添うラジオ』をAIで作る 体験価値から逆算した、会話しないUXと品質設計
theoriatec2024
3
150
Featured
See All Featured
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.2k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.6k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
From π to Pie charts
rasagy
0
360
Skip the Path - Find Your Career Trail
mkilby
1
230
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
Product Roadmaps are Hard
iamctodd
55
13k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
Building a Scalable Design System with Sketch
lauravandoore
464
34k
Accessibility Awareness
sabderemane
1
210
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