parent-no-raw-loops

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Sean 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

设计原则

  1. No Raw Loops: Every loop is a missed opportunity to use (or create) a named algorithm.
  2. Algorithms Express Intent:
    std::find_if
    says "search"; a for-loop says "increment and compare."
  3. Composition Over Iteration: Build complex operations from simple, well-named pieces.
  4. Seek the Essence: Remove everything that doesn't contribute to meaning.
  1. 无原始循环:每一个循环都是一次使用(或创建)命名算法的错失机会。
  2. 算法表达意图
    std::find_if
    表达的是“搜索”;而for循环表达的是“递增并比较”。
  3. 组合优先于迭代:通过简单、命名清晰的组件构建复杂操作。
  4. 追求本质:移除所有对代码意义无贡献的内容。

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
    for
    loops when an algorithm exists
  • Nest loops when composition would work
  • Inline complex logic that deserves a name
  • Sacrifice clarity for cleverness
  • Leave unnamed concepts in code
  • 当存在可用算法时仍编写原始
    for
    循环
  • 可以使用组合时却嵌套循环
  • 将值得命名的复杂逻辑内联
  • 为了炫技而牺牲代码清晰度
  • 在代码中保留未命名的概念

Prefer

优先选择

  • std::transform
    over element-by-element loops
  • std::accumulate
    over manual aggregation
  • std::partition
    over manual reordering
  • std::remove_if
    + erase over manual deletion
  • Algorithm pipelines over nested loops
  • 使用
    std::transform
    替代逐个元素处理的循环
  • 使用
    std::accumulate
    替代手动聚合
  • 使用
    std::partition
    替代手动重排
  • 使用
    std::remove_if
    + erase替代手动删除
  • 使用算法管道替代嵌套循环

The Algorithm Catalog

算法目录

Existence Queries

存在性查询

NeedAlgorithm
Does any element satisfy P?
std::any_of
Do all elements satisfy P?
std::all_of
Does no element satisfy P?
std::none_of
How many satisfy P?
std::count_if
需求算法
是否存在满足条件P的元素?
std::any_of
是否所有元素都满足条件P?
std::all_of
是否没有元素满足条件P?
std::none_of
有多少元素满足条件P?
std::count_if

Finding

查找

NeedAlgorithm
Find first matching P
std::find_if
Find first mismatch
std::mismatch
Find subsequence
std::search
Binary search
std::lower_bound
,
std::upper_bound
需求算法
查找第一个满足条件P的元素
std::find_if
查找第一个不匹配的位置
std::mismatch
查找子序列
std::search
二分查找
std::lower_bound
,
std::upper_bound

Transforming

转换

NeedAlgorithm
Apply f to each element
std::transform
Fill with value
std::fill
Generate values
std::generate
Copy with filter
std::copy_if
需求算法
对每个元素应用函数f
std::transform
填充指定值
std::fill
生成值
std::generate
带过滤的复制
std::copy_if

Reordering

重排

NeedAlgorithm
Sort
std::sort
,
std::stable_sort
Partition by P
std::partition
,
std::stable_partition
Rotate
std::rotate
Remove matching P
std::remove_if
需求算法
排序
std::sort
,
std::stable_sort
根据条件P分区
std::partition
,
std::stable_partition
旋转
std::rotate
移除满足条件P的元素
std::remove_if

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:
  1. Name the operation: What am I really doing?
  2. Find the algorithm: Does this operation have a name?
  3. Compose primitives: Can I build this from smaller operations?
  4. Factor out patterns: Is this useful elsewhere?
Parent将代码视为数学组合
  1. 为操作命名:我真正要做的是什么?
  2. 寻找合适的算法:这个操作有没有对应的命名算法?
  3. 组合原语:我能否通过更小的操作构建这个功能?
  4. 提炼模式:这个模式在其他地方是否有用?

The Refactoring Test

重构测试

When you see a loop, ask:
  1. Is this searching? →
    find
    ,
    search
    ,
    any_of
    ...
  2. Is this transforming? →
    transform
    ,
    copy_if
    ...
  3. Is this reordering? →
    sort
    ,
    partition
    ,
    rotate
    ...
  4. Is this aggregating? →
    accumulate
    ,
    reduce
    ...
  5. Is this comparing? →
    equal
    ,
    mismatch
    ...
If none fit exactly, write and name a new algorithm.
当你看到一个循环时,问自己:
  1. 这是在做搜索吗?→ 使用
    find
    ,
    search
    ,
    any_of
    ...
  2. 这是在做转换吗?→ 使用
    transform
    ,
    copy_if
    ...
  3. 这是在做重排吗?→ 使用
    sort
    ,
    partition
    ,
    rotate
    ...
  4. 这是在做聚合吗?→ 使用
    accumulate
    ,
    reduce
    ...
  5. 这是在做比较吗?→ 使用
    equal
    ,
    mismatch
    ...
如果没有完全匹配的算法,编写并命名一个新的算法