parent-no-raw-loops
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSean Parent Style Guide
Sean Parent 风格指南
Overview
概述
Sean Parent, former Principal Scientist at Adobe, transformed how many think about C++ with his "C++ Seasoning" and "Better Code" talks. His central thesis: raw loops are assembly language for algorithms. If you're writing a loop, you're probably missing an algorithm.
Sean Parent曾是Adobe的首席科学家,他通过“C++调味”和“更好的代码”系列演讲改变了许多人对C++的看法。他的核心论点是:原始循环是算法层面的汇编语言。如果你在写循环,那你很可能错过了一个可以使用的算法。
Core Philosophy
核心理念
"No raw loops."
"A goal of software engineering is to reduce code to its essence, to remove anything that doesn't contribute to the meaning."
Parent believes that code should be a direct expression of intent, and loops obscure intent by exposing mechanism.
“无原始循环。”
“软件工程的目标之一是将代码精简到本质,移除所有对代码意义无贡献的内容。”
Parent认为,代码应直接表达意图,而循环会通过暴露底层机制来掩盖意图。
Design Principles
设计原则
-
No Raw Loops: Every loop is a missed opportunity to use (or create) a named algorithm.
-
Algorithms Express Intent:says "search"; a for-loop says "increment and compare."
std::find_if -
Composition Over Iteration: Build complex operations from simple, well-named pieces.
-
Seek the Essence: Remove everything that doesn't contribute to meaning.
-
无原始循环:每一个循环都是一次使用(或创建)命名算法的错失机会。
-
算法表达意图:表达的是“搜索”;而for循环表达的是“递增并比较”。
std::find_if -
组合优先于迭代:通过简单、命名清晰的组件构建复杂操作。
-
追求本质:移除所有对代码意义无贡献的内容。
When Writing Code
代码编写规范
Always
始终遵循
- Use standard algorithms when they fit exactly
- Create named algorithms when standard ones don't fit
- Prefer algorithms that express the operation's semantic meaning
- Use range-based operations (C++20 ranges when available)
- Compose simple operations rather than write complex loops
- Name intermediate variables to document intent
- 当标准算法完全匹配需求时使用它们
- 当标准算法不适用时,创建命名算法
- 优先选择能表达操作语义的算法
- 使用基于范围的操作(如果可用则使用C++20 ranges)
- 组合简单操作而非编写复杂循环
- 为中间变量命名以明确意图
Never
绝对避免
- Write raw loops when an algorithm exists
for - Nest loops when composition would work
- Inline complex logic that deserves a name
- Sacrifice clarity for cleverness
- Leave unnamed concepts in code
- 当存在可用算法时仍编写原始循环
for - 可以使用组合时却嵌套循环
- 将值得命名的复杂逻辑内联
- 为了炫技而牺牲代码清晰度
- 在代码中保留未命名的概念
Prefer
优先选择
- over element-by-element loops
std::transform - over manual aggregation
std::accumulate - over manual reordering
std::partition - + erase over manual deletion
std::remove_if - Algorithm pipelines over nested loops
- 使用替代逐个元素处理的循环
std::transform - 使用替代手动聚合
std::accumulate - 使用替代手动重排
std::partition - 使用+ erase替代手动删除
std::remove_if - 使用算法管道替代嵌套循环
The Algorithm Catalog
算法目录
Existence Queries
存在性查询
| Need | Algorithm |
|---|---|
| Does any element satisfy P? | |
| Do all elements satisfy P? | |
| Does no element satisfy P? | |
| How many satisfy P? | |
| 需求 | 算法 |
|---|---|
| 是否存在满足条件P的元素? | |
| 是否所有元素都满足条件P? | |
| 是否没有元素满足条件P? | |
| 有多少元素满足条件P? | |
Finding
查找
| Need | Algorithm |
|---|---|
| Find first matching P | |
| Find first mismatch | |
| Find subsequence | |
| Binary search | |
| 需求 | 算法 |
|---|---|
| 查找第一个满足条件P的元素 | |
| 查找第一个不匹配的位置 | |
| 查找子序列 | |
| 二分查找 | |
Transforming
转换
| Need | Algorithm |
|---|---|
| Apply f to each element | |
| Fill with value | |
| Generate values | |
| Copy with filter | |
| 需求 | 算法 |
|---|---|
| 对每个元素应用函数f | |
| 填充指定值 | |
| 生成值 | |
| 带过滤的复制 | |
Reordering
重排
| Need | Algorithm |
|---|---|
| Sort | |
| Partition by P | |
| Rotate | |
| Remove matching P | |
| 需求 | 算法 |
|---|---|
| 排序 | |
| 根据条件P分区 | |
| 旋转 | |
| 移除满足条件P的元素 | |
Code Patterns
代码模式
Before and After: The Transformation
前后对比:代码转换
cpp
// RAW LOOP: What is this doing?
std::vector<int> result;
for (const auto& item : items) {
if (item.isValid()) {
result.push_back(item.getValue() * 2);
}
}
// ALGORITHM: Intent is clear
auto result = items
| std::views::filter(&Item::isValid)
| std::views::transform([](const Item& i) { return i.getValue() * 2; })
| std::ranges::to<std::vector>();
// Or without C++20 ranges:
std::vector<int> result;
std::transform(
items.begin(), items.end(),
std::back_inserter(result),
[](const Item& i) -> std::optional<int> {
return i.isValid() ? std::optional{i.getValue() * 2} : std::nullopt;
}
);
// (Then filter nullopt... or use a custom transform_if)cpp
// RAW LOOP: 这段代码在做什么?
std::vector<int> result;
for (const auto& item : items) {
if (item.isValid()) {
result.push_back(item.getValue() * 2);
}
}
// ALGORITHM: 意图清晰
auto result = items
| std::views::filter(&Item::isValid)
| std::views::transform([](const Item& i) { return i.getValue() * 2; })
| std::ranges::to<std::vector>();
// 或者不使用C++20 ranges的写法:
std::vector<int> result;
std::transform(
items.begin(), items.end(),
std::back_inserter(result),
[](const Item& i) -> std::optional<int> {
return i.isValid() ? std::optional{i.getValue() * 2} : std::nullopt;
}
);
// (之后过滤掉nullopt...或者使用自定义的transform_if)The Erase-Remove Idiom
Erase-Remove 惯用法
cpp
// RAW LOOP: Error-prone, unclear intent
for (auto it = vec.begin(); it != vec.end(); ) {
if (shouldRemove(*it)) {
it = vec.erase(it); // O(n) each time!
} else {
++it;
}
}
// ALGORITHM: O(n) total, clear intent
vec.erase(
std::remove_if(vec.begin(), vec.end(), shouldRemove),
vec.end()
);
// C++20: Even clearer
std::erase_if(vec, shouldRemove);cpp
// RAW LOOP: 容易出错,意图不清晰
for (auto it = vec.begin(); it != vec.end(); ) {
if (shouldRemove(*it)) {
it = vec.erase(it); // 每次操作都是O(n)复杂度!
} else {
++it;
}
}
// ALGORITHM: 总复杂度O(n),意图清晰
vec.erase(
std::remove_if(vec.begin(), vec.end(), shouldRemove),
vec.end()
);
// C++20写法:更加清晰
std::erase_if(vec, shouldRemove);Slide Algorithm (Parent's Signature)
Slide算法(Parent的标志性算法)
cpp
// Move a range to a new position within a sequence
template<typename I> // I models RandomAccessIterator
auto slide(I first, I last, I pos) -> std::pair<I, I> {
if (pos < first) return { pos, std::rotate(pos, first, last) };
if (last < pos) return { std::rotate(first, last, pos), pos };
return { first, last };
}
// Usage: Move selected items to position
auto [new_first, new_last] = slide(
selection_begin, selection_end,
drop_position
);cpp
// 将序列中的一个范围移动到序列内的新位置
template<typename I> // I 满足随机访问迭代器模型
auto slide(I first, I last, I pos) -> std::pair<I, I> {
if (pos < first) return { pos, std::rotate(pos, first, last) };
if (last < pos) return { std::rotate(first, last, pos), pos };
return { first, last };
}
// 使用示例:将选中的元素移动到指定位置
auto [new_first, new_last] = slide(
selection_begin, selection_end,
drop_position
);Gather Algorithm
Gather算法
cpp
// Move all elements satisfying predicate to position
template<typename I, typename P>
auto gather(I first, I last, I pos, P pred) -> std::pair<I, I> {
return {
std::stable_partition(first, pos, std::not_fn(pred)),
std::stable_partition(pos, last, pred)
};
}
// Usage: Gather all selected items around cursor
auto [sel_first, sel_last] = gather(
items.begin(), items.end(),
cursor_position,
[](const Item& i) { return i.selected; }
);cpp
// 将所有满足谓词条件的元素移动到指定位置
template<typename I, typename P>
auto gather(I first, I last, I pos, P pred) -> std::pair<I, I> {
return {
std::stable_partition(first, pos, std::not_fn(pred)),
std::stable_partition(pos, last, pred)
};
}
// 使用示例:将所有选中的元素聚集到光标位置周围
auto [sel_first, sel_last] = gather(
items.begin(), items.end(),
cursor_position,
[](const Item& i) { return i.selected; }
);Mental Model
思维模型
Parent thinks of code as mathematical composition:
- Name the operation: What am I really doing?
- Find the algorithm: Does this operation have a name?
- Compose primitives: Can I build this from smaller operations?
- Factor out patterns: Is this useful elsewhere?
Parent将代码视为数学组合:
- 为操作命名:我真正要做的是什么?
- 寻找合适的算法:这个操作有没有对应的命名算法?
- 组合原语:我能否通过更小的操作构建这个功能?
- 提炼模式:这个模式在其他地方是否有用?
The Refactoring Test
重构测试
When you see a loop, ask:
- Is this searching? → ,
find,search...any_of - Is this transforming? → ,
transform...copy_if - Is this reordering? → ,
sort,partition...rotate - Is this aggregating? → ,
accumulate...reduce - Is this comparing? → ,
equal...mismatch
If none fit exactly, write and name a new algorithm.
当你看到一个循环时,问自己:
- 这是在做搜索吗?→ 使用,
find,search...any_of - 这是在做转换吗?→ 使用,
transform...copy_if - 这是在做重排吗?→ 使用,
sort,partition...rotate - 这是在做聚合吗?→ 使用,
accumulate...reduce - 这是在做比较吗?→ 使用,
equal...mismatch
如果没有完全匹配的算法,编写并命名一个新的算法。