<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://summertbd.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://summertbd.github.io/" rel="alternate" type="text/html" /><updated>2026-08-11T17:53:50+08:00</updated><id>https://summertbd.github.io/feed.xml</id><title type="html">summerTBD 的博客</title><subtitle>summerTBD 的技术博客，记录算法学习与编程思考</subtitle><author><name>summerTBD</name></author><entry><title type="html">周期</title><link href="https://summertbd.github.io/posts/%E5%91%A8%E6%9C%9F/" rel="alternate" type="text/html" title="周期" /><published>2026-08-11T00:00:00+08:00</published><updated>2026-08-11T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/%E5%91%A8%E6%9C%9F</id><content type="html" xml:base="https://summertbd.github.io/posts/%E5%91%A8%E6%9C%9F/"><![CDATA[<h3 id="题目描述">题目描述</h3>

<p>本题来自 AcWing 143：给定一个字符串 s，对于每个前缀 s[1..i]（1 ≤ i ≤ n），求出它的最小循环元长度以及循环次数。</p>

<h3 id="kmp-next-数组回顾">KMP next 数组回顾</h3>

<p>next[i] 的定义是：字符串 s[1..i] 的<strong>最长相等真前后缀</strong>的长度。也就是说，令 j = next[i]，则 s[1..j] = s[i-j+1..i]，且 j 是所有满足此条件的值中最大的。</p>

<h3 id="核心观察">核心观察</h3>

<p>令 m = i - next[i]，j = next[i]。</p>

<p>我们将前缀 s[1..i] 分成三段：</p>

<ul>
  <li><strong>前段 A</strong>：s[1..m]，长度 m</li>
  <li><strong>中段 B</strong>：s[m+1..j]，长度 j - m（即前后缀重叠的部分）</li>
  <li><strong>后段 C</strong>：s[j+1..i]，长度 i - j = m</li>
</ul>

<p>因为 next[i] = j，所以<strong>前缀 s[1..j] = 后缀 s[m+1..i]</strong>。对齐方式如下：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre>| 位置：  1  ...  m | m+1  ...  j | j+1  ...  i |
| ----------------- ||------------|  |----------|
前缀：  s[1] ... s[m]    s[m+1] ... s[j]
                       s[m+1] ... s[j]    s[j+1] ... s[i]
                       |================|
                            中段 B 自重复
</pre></td></tr></tbody></table></code></pre></div></div>

<p>代入具体例子来理解：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre>例：s = "ababab"，i = 6，next[6] = 4，m = 2，j = 4

前缀 s[1..4] = "abab" = 后缀 s[3..6]

位置：  1  2  3  4  5  6
        |-----|--------|
| 前缀：  a  b       | a  b | s[1..4] |
| ------------------ | ---- |
| 后缀：        a  b | a  b | s[3..6] |
              =  =     =  =
              a  b  =  a  b   ← 重叠区 s[3..4] = s[1..2]
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="证明最小循环元长度--i---nexti">证明：最小循环元长度 = i - next[i]</h3>

<p>假设最小循环元长度为 len。我们证明 len = m = i - next[i]。</p>

<p><strong>第一步：len 必须将中段 B 的长度 (j - m) 整除</strong></p>

<p>中段 B = s[m+1..j] 既是前缀 s[1..j] 的前 j-m 个字符，也是后缀 s[m+1..i] 的后 j-m 个字符。如果整个串 s[1..i] 有周期 len，那么 s[1..j] 也有周期 len，B 作为 s[1..j] 的前缀，其长度必须是 len 的整数倍——否则 B 的首尾会出现不匹配，与 B 同时出现在前缀和后缀中矛盾。</p>

<p><strong>第二步：反证法证明 len = m</strong></p>

<p>假设 len ≠ m，分两种情况讨论：</p>

<p><strong>情况1：假设 len &lt; m</strong></p>

<p>如果最小周期 len 比 m 还小，那么前段 A（长度 m）包含至少一个完整周期。由于 B 的起始位置 m+1 也是周期的对齐点，B 的结束位置 j = m + (j-m) 也是周期的对齐点。这意味着 s[1..j+len] 也是 s 的前缀且等于后缀 s[len+1..i+len]，从而存在长度 &gt; j 的公共前后缀，与 next[i] = j（j 是最长相等真前后缀长度）矛盾。</p>

<p>图示说明：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre>假设 len &lt; m，周期对齐如下：

位置：  1  ...  len  ...  m  ...  j  ...  i
        |--------|--------|------|------|
前缀：  s[1] ... s[len] ... s[m] ... s[j]
              |======================|
后缀：              s[1] ... s[m] ... s[j]
              |======================|
          重叠区延伸到 j+len &gt; j，
          与 next[i]=j 矛盾
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>情况2：假设 len &gt; m</strong></p>

<p>如果最小周期 len 比 m 还大，那么重叠区 B 的长度 j - m 就小于 len，B 不可能被 len 整除（不够一个完整周期）。但另一方面，s[1..j] 被 len 整除地划分（因为 s[1..j] 是 s[1..i] 的前缀，继承周期性），所以 s[1..j] 可以被 len 整除地划分。由 s[1..j] = s[m+1..i]，后段 s[j+1..i] 也必须有同样的周期结构。但后段长度 = i - j = m &lt; len，不够一个完整周期，矛盾。</p>

<p>图示说明：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre>假设 len &gt; m，周期对齐如下：

位置：  1  ...  m  ...  j  ...  i
        |--------|------|------|
前缀：  s[1] ... s[m] ... s[j]
                       s[m+1] ... s[j]    s[j+1] ... s[i]
                       |----------------|
          后段 s[j+1..i] 长度 = m &lt; len，
          不够一个完整周期，矛盾
</pre></td></tr></tbody></table></code></pre></div></div>

<p>综上，len = m = i - next[i]。</p>

<h3 id="结论">结论</h3>

<p>对于每个前缀 s[1..i]，令 m = i - next[i]：</p>

<ul>
  <li>若 m 整除 i，则该前缀的最小循环元长度为 <strong>m</strong>，循环次数为 <strong>i / m</strong></li>
  <li>若 m 不整除 i，则该前缀不是纯周期串，最小循环元长度为 <strong>i</strong>，循环次数为 <strong>1</strong></li>
</ul>

<h3 id="算法步骤">算法步骤</h3>

<ol>
  <li>预处理 next 数组，时间复杂度 O(n)</li>
  <li>对每个 i（1 ≤ i ≤ n），计算 m = i - next[i]</li>
  <li>若 m 整除 i，输出 m 和 i/m；否则输出 i 和 1</li>
</ol>

<h3 id="代码实现">代码实现</h3>

<p>#include <cstdio>
#include <iostream>
using namespace std;</iostream></cstdio></p>

<p>const int maxn = 1000002; // it is 1000000 + 1 + 1
char str[maxn];
int nex[maxn];</p>

<p>int main() {
    int cnt = 0;
    int n = 0;</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="rouge-code"><pre>while (true) {
    scanf("%d", &amp;n);
    if (!n) {
        goto flag_end;
    }

    printf("Test case #%d\n", ++cnt);
    scanf("%s", str + 1);

    nex[0] = nex[1] = 0;
    for (int i = 2, j = 0; i &lt;= n; ++i) {
        while (j &gt; 0 &amp;&amp; str[i] != str[j + 1]) {
            j = nex[j];
        }

        if (str[i] == str[j + 1]) {
            ++j;
        }

        nex[i] = j;

        const bool circular_element_exits = (i % (i - nex[i]) == 0) &amp;&amp; nex[i] &gt; 0;
        if (circular_element_exits) {
            printf("%d %d\n", i, i / (i - nex[i]));
        }
    }

    printf("\n");
}
</pre></td></tr></tbody></table></code></pre></div></div>

<p>flag_end:
    return 0;
}</p>]]></content><author><name>summerTBD</name></author><category term="数学，算法" /><category term="数学，KMP算法" /><summary type="html"><![CDATA[题目描述]]></summary></entry><entry><title type="html">farmpiggie_and_Subset_Sum</title><link href="https://summertbd.github.io/posts/farmpiggie_and_Subset_Sum/" rel="alternate" type="text/html" title="farmpiggie_and_Subset_Sum" /><published>2026-08-04T00:00:00+08:00</published><updated>2026-08-04T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/farmpiggie_and_Subset_Sum</id><content type="html" xml:base="https://summertbd.github.io/posts/farmpiggie_and_Subset_Sum/"><![CDATA[<h3 id="题目链接">题目链接</h3>
<p>https://codeforces.com/problemset/problem/2246/A</p>

<h3 id="个人的一些看法">个人的一些看法</h3>
<p>这是一道A题，从A题的角度来看他似乎不够格，但做这道题时我发现它的思考角度是我很少考虑的，但却经常被使用的思路，那就是奇偶性，Codeforces Round 1108 (Div. 2)有很多题目都考察了奇偶性。</p>

<h3 id="思路">思路</h3>
<p>其实题目已经暗示了我们要考虑奇偶性，在题目中有一个even单词被加黑了，这是很明显的暗示。但这不是我们的目的，我们讨论真正的思路。我们不希望它的和有概率为1，那该怎么做，如果你在思考怎么避免成为1，那你很可能想错了，1是一个元素，他有很多种情况，你得考虑每一个元素的变化对整体的影响，那不妨以更高的角度思考这个问题，如果1是某个集合的元素，而这个集合被排除那也意味着1也会被排除，最简单的角度就是奇偶性，1是奇数，如果我可以保证它的和不为奇数，他就一定不能为1。那我可以考虑让他们的每一项都只能为偶数，又因为𝑆𝑖={−𝑖⋅𝑝𝑖,0,𝑖⋅𝑝𝑖}，我们可以让i为奇数时，让pi为偶数，当i为偶数时，pi为奇数。</p>

<h3 id="代码">代码</h3>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
</pre></td><td class="rouge-code"><pre>

<span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="k">using</span> <span class="n">ll</span> <span class="o">=</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">;</span>
<span class="k">using</span> <span class="n">ull</span> <span class="o">=</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">;</span>

<span class="k">namespace</span> <span class="p">{</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">N</span> <span class="o">=</span> <span class="mf">1e6</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">INF</span> <span class="o">=</span> <span class="mh">0x3f3f3f3f</span><span class="p">;</span>
<span class="p">}</span> <span class="c1">// namespace</span>

<span class="kt">void</span> <span class="nf">solve</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">n</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">n</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="sc">' '</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="sc">'\n'</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">ios</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">_</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">_</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">_</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">solve</span><span class="p">();</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>


</pre></td></tr></tbody></table></code></pre></div></div>]]></content><author><name>summerTBD</name></author><category term="数学，算法" /><category term="数学，算法" /><summary type="html"><![CDATA[题目链接 https://codeforces.com/problemset/problem/2246/A]]></summary></entry><entry><title type="html">Feelings of Making an App</title><link href="https://summertbd.github.io/posts/Feelings_OF_making_an_APP/" rel="alternate" type="text/html" title="Feelings of Making an App" /><published>2026-07-05T00:00:00+08:00</published><updated>2026-07-05T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/Feelings_OF_making_an_APP</id><content type="html" xml:base="https://summertbd.github.io/posts/Feelings_OF_making_an_APP/"><![CDATA[<h3 id="c-is-hard">C++ Is Hard</h3>

<p>在这最近的两个月，我决定自己做一些项目（做了两个）来加深自己对程序员的理解、了解工程思维。最初我想的是，既然我用得最多的是 C++，那不妨就用 C++ 实现自己的宏图。但可惜第一关都差点过不了——最初我用的是 Visual Studio，目的是写一个贪吃蛇，可才写到一半准备编译排查语法错误时，神奇的事情发生了：由于 Unicode 字符的原因，导致它不能正确识别我的代码。那没辙，就准备在 VS Code 写。这下确实没有 UTF-8 格式的困扰了，可是又出了问题——需要用 <code class="language-plaintext highlighter-rouge">#include &lt;windows.h&gt;</code>，但我是在 WSL 下的，报错了。行吧，取消 WSL，可还是发生了意外：的确没有格式的烦恼了，但 C++ 无法正确识别 Unicode，输出乱码了。不得已，我回到了 Visual Studio，去调用 Windows 系统的 API，才最终解决。</p>

<h3 id="rebuilt-by-rust">Rebuilt by Rust</h3>

<p>我是性情中人，被 C++ 惹生气了，决定用我擅长的另一门语言——Rust 来重构。那为什么选择 Rust 呢？我会 C/C++、Rust、Python，由于上面的经历，我优先考虑 Rust 和 Python。它们有两个好处——安全：一个有 GC，一个有 Ownership。虽然我也用 Python 做过一些项目，不得不说的确方便，可是我有性能洁癖，再加上 Rust 的语法也很简洁，以及 Rust 的「能编译就一定安全」的语法检查，不仅对人类减少了心智负担，还对 AI 也很友好。我在网上查了一下相关的 Rust 代码作为借鉴。不过用 Rust 的确改变了我一个习惯，那就是拆分文件。因为用 C++ 写题惯了，经常一个文件几百行甚至几千行，看着不多，可这些都是个人文件，只有我一个人管理。</p>

<h3 id="learning-egui">Learning egui</h3>

<p>egui 应该是我的梦中情框架，为什么？因为它是纯 Rust 实现的。如果你了解过我，就会发现我的代码风格就是 Rust 的标准风格，哪怕写 C++，也是这个风格。为什么要用这个框架？原因很简单——我想做一个电子版便利贴（现已发布：<a href="https://github.com/summerTBD/SimpleNote">https://github.com/summerTBD/SimpleNote</a>）。在整个过程中，我感受最深的一点是：构思的重要性。我要实现想象中的功能，就得先确定很多事情——怎么触发、UI 界面是什么样的。其次就是，学习模板代码是怎么实现的，看多了就有思路了。但光有思路还不行，还需要动手操作，让身体和大脑做到协同。</p>]]></content><author><name>summerTBD</name></author><category term="开发" /><category term="博客" /><category term="生活" /><category term="开发" /><summary type="html"><![CDATA[C++ Is Hard]]></summary></entry><entry><title type="html">Vox 语言参考手册</title><link href="https://summertbd.github.io/posts/Vox/" rel="alternate" type="text/html" title="Vox 语言参考手册" /><published>2026-06-08T00:00:00+08:00</published><updated>2026-06-08T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/Vox</id><content type="html" xml:base="https://summertbd.github.io/posts/Vox/"><![CDATA[<h1 id="vox-语言参考手册">Vox 语言参考手册</h1>

<p><strong>版本</strong>：0.7.0<br />
<strong>设计哲学</strong>：显式、正交、无魔法。无必要不增实体。自由优先，安全可选。<br />
<strong>编译目标</strong>：转译至 C（原型阶段），最终目标为自举并直接生成机器码。</p>

<hr />

<h2 id="目录">目录</h2>

<ol>
  <li><a href="#1-设计原则">设计原则</a></li>
  <li><a href="#2-词法结构">词法结构</a></li>
  <li><a href="#3-基本语法">基本语法</a></li>
  <li><a href="#4-变量与声明">变量与声明</a></li>
  <li><a href="#5-类型系统">类型系统</a></li>
  <li><a href="#6-函数">函数</a></li>
  <li><a href="#7-控制流">控制流</a></li>
  <li><a href="#8-结构体与方法">结构体与方法</a></li>
  <li><a href="#9-枚举与模式匹配">枚举与模式匹配</a></li>
  <li><a href="#10-引用与借用">引用与借用</a></li>
  <li><a href="#11-所有权与转移">所有权与转移</a></li>
  <li><a href="#12-指针与内存管理">指针与内存管理</a></li>
  <li><a href="#13-数组与切片">数组与切片</a></li>
  <li><a href="#14-输入输出">输入输出</a></li>
  <li><a href="#15-模块系统">模块系统</a></li>
  <li><a href="#16-错误处理">错误处理</a></li>
  <li><a href="#17-与-c-的互操作">与 C 的互操作</a></li>
  <li><a href="#18-测试">测试</a></li>
  <li><a href="#19-语法糖与脱糖规则">语法糖与脱糖规则</a></li>
  <li><a href="#20-arena-区域分配">Arena 区域分配</a></li>
  <li><a href="#21-附录">附录</a></li>
</ol>

<hr />

<h2 id="1-设计原则">1. 设计原则</h2>

<ol>
  <li><strong>显式优先，可控可免</strong>：所有行为优先在代码字面上可见。完全可控、可预测的隐式（如关键字引导的默认行为）不在禁止之列。不存在隐式类型转换、隐式构造函数、隐式 <code class="language-plaintext highlighter-rouge">self</code>。</li>
  <li><strong>正交优于合并</strong>：每个特性只做一件事，且彼此独立。你可以自由组合它们，而不是被迫接受一个”全家桶”。</li>
  <li><strong>无魔法</strong>：语言不提供任何需要”背后特殊处理”的语法。所有高级语法（如 <code class="language-plaintext highlighter-rouge">try</code>、方法调用）都可通过明确的脱糖规则还原为基础语法。</li>
  <li><strong>控制权归程序员</strong>：内存何时分配、何时释放、数据如何布局，都由你决定。语言提供工具（如 <code class="language-plaintext highlighter-rouge">defer</code>），但绝不替你作主。</li>
  <li><strong>无必要不增实体</strong>：不会因为”别的语言有”就加一个特性。每个特性必须解决真实痛点，且不能被已有特性轻易替代。</li>
  <li><strong>性能可预测</strong>：不存在”看似轻量，实则触发隐式分配”的操作。每一行代码的时间与空间成本，都应能从字面上合理推断。</li>
</ol>

<hr />

<h2 id="2-词法结构">2. 词法结构</h2>

<h3 id="21-关键字">2.1 关键字</h3>
<p>以下为保留关键字，不可用作标识符。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre>const var fn struct enum match
if else while for return yield break continue
defer import pub extern
as parse null true false
and or not
test macro comptime asm global move
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="22-标识符">2.2 标识符</h3>
<p>由字母、数字、下划线组成，必须以字母或下划线开头。不允许数字开头。</p>

<h3 id="23-字面量">2.3 字面量</h3>
<ul>
  <li><strong>整数字面量</strong>：十进制 <code class="language-plaintext highlighter-rouge">42</code>，十六进制 <code class="language-plaintext highlighter-rouge">0x2A</code>，八进制 <code class="language-plaintext highlighter-rouge">0o52</code>，二进制 <code class="language-plaintext highlighter-rouge">0b101010</code>，可加下划线 <code class="language-plaintext highlighter-rouge">1_000_000</code>。</li>
  <li><strong>浮点字面量</strong>：<code class="language-plaintext highlighter-rouge">3.14</code>，<code class="language-plaintext highlighter-rouge">1.0e-9</code>。</li>
  <li><strong>字符串字面量</strong>：用双引号括起，支持转义 <code class="language-plaintext highlighter-rouge">\n</code>、<code class="language-plaintext highlighter-rouge">\t</code>、<code class="language-plaintext highlighter-rouge">\\</code>、<code class="language-plaintext highlighter-rouge">\"</code>，类型为 <code class="language-plaintext highlighter-rouge">[]const u8</code>。</li>
  <li><strong>布尔字面量</strong>：<code class="language-plaintext highlighter-rouge">true</code>、<code class="language-plaintext highlighter-rouge">false</code>。</li>
  <li><strong>空指针字面量</strong>：<code class="language-plaintext highlighter-rouge">null</code>，类型可赋值给任意 <code class="language-plaintext highlighter-rouge">*T</code> / <code class="language-plaintext highlighter-rouge">*mut T</code> 以及 <code class="language-plaintext highlighter-rouge">&amp;T</code> / <code class="language-plaintext highlighter-rouge">&amp;mut T</code>。</li>
  <li><strong>单元字面量</strong>：<code class="language-plaintext highlighter-rouge">()</code>，类型 <code class="language-plaintext highlighter-rouge">unit</code>。</li>
</ul>

<h3 id="24-运算符与分隔符">2.4 运算符与分隔符</h3>
<p>运算符及其优先级见<a href="#21-附录">附录</a>。</p>

<p><strong>逻辑运算符</strong>（Python 风格）：<code class="language-plaintext highlighter-rouge">and</code> <code class="language-plaintext highlighter-rouge">or</code> <code class="language-plaintext highlighter-rouge">not</code>（关键字）<br />
<strong>位运算符</strong>：<code class="language-plaintext highlighter-rouge">&amp;</code> <code class="language-plaintext highlighter-rouge">|</code> <code class="language-plaintext highlighter-rouge">^</code> <code class="language-plaintext highlighter-rouge">~</code><br />
<strong>引用运算符</strong>：<code class="language-plaintext highlighter-rouge">&amp;</code> <code class="language-plaintext highlighter-rouge">&amp;mut</code><br />
<strong>分隔符</strong>：<code class="language-plaintext highlighter-rouge">{ } ( ) [ ] ; , . =&gt; : ..</code></p>

<h3 id="25-注释">2.5 注释</h3>
<ul>
  <li>单行注释：<code class="language-plaintext highlighter-rouge">//</code> 至行尾。</li>
  <li>多行注释：<code class="language-plaintext highlighter-rouge">/* ... */</code>，可嵌套。</li>
</ul>

<hr />

<h2 id="3-基本语法">3. 基本语法</h2>

<h3 id="31-语句与表达式">3.1 语句与表达式</h3>
<ul>
  <li><strong>语句语境</strong>：独立语句、循环体、<code class="language-plaintext highlighter-rouge">if</code> 作为语句时等，不产生值。</li>
  <li><strong>求值语境</strong>：赋值右值、函数实参、<code class="language-plaintext highlighter-rouge">return</code> 右值、<code class="language-plaintext highlighter-rouge">match</code> 臂等，必须用 <code class="language-plaintext highlighter-rouge">yield</code>（块内）或 <code class="language-plaintext highlighter-rouge">return</code>（函数）提供值。</li>
</ul>

<h3 id="32-分号">3.2 分号</h3>
<p><strong>语句必须以分号结尾</strong>。表达式语句、变量声明、赋值、<code class="language-plaintext highlighter-rouge">return</code>、<code class="language-plaintext highlighter-rouge">yield</code>、<code class="language-plaintext highlighter-rouge">defer</code>、<code class="language-plaintext highlighter-rouge">global</code>、<code class="language-plaintext highlighter-rouge">move</code> 均需分号。块 <code class="language-plaintext highlighter-rouge">{}</code> 之后通常不加分号，除非块作为表达式语句的一部分。</p>

<hr />

<h2 id="4-变量与声明">4. 变量与声明</h2>

<h3 id="41-不可变变量与可变变量">4.1 不可变变量与可变变量</h3>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="c1">// 不可变变量（默认）</span>
<span class="k">const</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="k">const</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span>       <span class="c1">// 类型推导</span>

<span class="c1">// 可变变量</span>
<span class="n">var</span> <span class="n">counter</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>        <span class="c1">// 允许重新赋值</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>              <span class="c1">// 错误：不可变变量不可赋值</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">const</code> 声明必须初始化，且后续不可修改绑定。</li>
  <li><code class="language-plaintext highlighter-rouge">var</code> 声明允许重新赋值，但类型必须匹配。</li>
</ul>

<h3 id="42-变量遮蔽">4.2 变量遮蔽</h3>

<p>Vox 支持<strong>跨作用域遮蔽</strong>（内层作用域可以声明与外层同名的变量），但<strong>禁止同作用域重复声明</strong>。</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>

<span class="c1">// 跨作用域遮蔽：允许</span>
<span class="k">if</span> <span class="n">cond</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">x</span><span class="p">:</span> <span class="nb">f64</span> <span class="o">=</span> <span class="mf">3.14</span><span class="p">;</span>   <span class="c1">// 合法：新作用域，新变量</span>
<span class="p">}</span>

<span class="c1">// 同作用域重复声明：禁止</span>
<span class="n">var</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>         <span class="c1">// 错误：标识符 'x' 已在当前作用域声明</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>设计理由</strong>：同作用域遮蔽会使旧变量隐式”消失”，与显式原则相悖。若需要重新绑定，请使用新名字或块表达式隔离中间变量。</p>

<h3 id="43-全局变量">4.3 全局变量</h3>

<ul>
  <li>模块顶层的 <code class="language-plaintext highlighter-rouge">const</code> 若无循环依赖，应在编译期求值，值存入只读数据段。</li>
  <li>模块顶层的 <code class="language-plaintext highlighter-rouge">var</code> 在进入 <code class="language-plaintext highlighter-rouge">main</code> 前初始化，初始化顺序按依赖拓扑排序。跨模块的可变全局状态由程序员自行保证同步安全。</li>
</ul>

<h4 id="431-全局变量的函数内访问-global">4.3.1 全局变量的函数内访问 (<code class="language-plaintext highlighter-rouge">global</code>)</h4>

<p>默认情况下，函数内部无法直接读写模块顶层的变量——它们必须被显式引入。<br />
使用 <code class="language-plaintext highlighter-rouge">global</code> 语句可以解除这一限制：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="c1">// 模块顶层</span>
<span class="n">var</span> <span class="n">counter</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="k">fn</span> <span class="nf">increment</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">global</span> <span class="n">counter</span><span class="p">;</span>       <span class="c1">// 声明我要用外部的 counter</span>
    <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">read_counter</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>
    <span class="n">global</span> <span class="n">counter</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">counter</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>批量声明</strong>：可以在一个 <code class="language-plaintext highlighter-rouge">global</code> 语句中声明多个变量，用逗号分隔。</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">global</span> <span class="n">counter</span><span class="p">,</span> <span class="n">MAX</span><span class="p">,</span> <span class="n">app_name</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>规则</strong>：</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">global</code> 只能用于当前模块顶层已声明的 <code class="language-plaintext highlighter-rouge">const</code> 或 <code class="language-plaintext highlighter-rouge">var</code>。</li>
  <li>不能通过 <code class="language-plaintext highlighter-rouge">global</code> 修改 <code class="language-plaintext highlighter-rouge">const</code> 变量。</li>
  <li>从其他模块导入的变量（如 <code class="language-plaintext highlighter-rouge">math.PI</code>）使用模块路径访问，不需要 <code class="language-plaintext highlighter-rouge">global</code>。</li>
  <li>若函数内未使用 <code class="language-plaintext highlighter-rouge">global</code> 声明，直接对与全局变量同名的标识符赋值，将视为对未定义变量的错误。</li>
  <li><code class="language-plaintext highlighter-rouge">global</code> 声明本身不分配内存，不产生任何运行时开销——它是纯粹的编译期名字绑定指令，翻译后消失。</li>
</ul>

<h3 id="44-move-赋值">4.4 <code class="language-plaintext highlighter-rouge">move</code> 赋值</h3>

<p>Vox 通过 <code class="language-plaintext highlighter-rouge">move</code> 关键字提供<strong>显式的所有权转移</strong>。赋值默认是复制（仅对纯数据安全），当需要将资源的所有权从一个变量转移到另一个变量时，使用 <code class="language-plaintext highlighter-rouge">move</code>：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">a</span> <span class="o">=</span> <span class="n">BigData</span> <span class="p">{</span> <span class="o">...</span> <span class="p">};</span>
<span class="n">var</span> <span class="n">b</span> <span class="o">=</span> <span class="k">move</span> <span class="n">a</span><span class="p">;</span>     <span class="c1">// ✅ 显式所有权转移</span>
<span class="nf">print</span><span class="p">(</span><span class="n">a</span><span class="p">);</span>           <span class="c1">// ❌ 编译错误：a 的所有权已被 move</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">move</code> 是一个<strong>表达式操作符</strong>，产生被移变量的值。详细语义见第 <a href="#11-所有权与转移">11 节</a>。</p>

<hr />

<h2 id="5-类型系统">5. 类型系统</h2>

<h3 id="51-基础类型">5.1 基础类型</h3>
<p>| 类型 | 描述 |
| :— | :— |
| <code class="language-plaintext highlighter-rouge">i8</code>、<code class="language-plaintext highlighter-rouge">i16</code>、<code class="language-plaintext highlighter-rouge">i32</code>、<code class="language-plaintext highlighter-rouge">i64</code> | 有符号整数 |
| <code class="language-plaintext highlighter-rouge">u8</code>、<code class="language-plaintext highlighter-rouge">u16</code>、<code class="language-plaintext highlighter-rouge">u32</code>、<code class="language-plaintext highlighter-rouge">u64</code> | 无符号整数 |
| <code class="language-plaintext highlighter-rouge">f32</code>、<code class="language-plaintext highlighter-rouge">f64</code> | 浮点类型 |
| <code class="language-plaintext highlighter-rouge">bool</code> | 布尔型 (<code class="language-plaintext highlighter-rouge">true</code>, <code class="language-plaintext highlighter-rouge">false</code>) |
| <code class="language-plaintext highlighter-rouge">unit</code> | 单元类型，字面量 <code class="language-plaintext highlighter-rouge">()</code>，仅一个值 |</p>

<h3 id="52-复合类型">5.2 复合类型</h3>
<ul>
  <li><strong>定长数组</strong>：<code class="language-plaintext highlighter-rouge">[T; N]</code>，详见第 13 节。</li>
  <li><strong>切片</strong>：<code class="language-plaintext highlighter-rouge">[]T</code> 和 <code class="language-plaintext highlighter-rouge">[]const T</code>，详见第 13 节。</li>
  <li><strong>引用</strong>：<code class="language-plaintext highlighter-rouge">&amp;T</code>（不可变引用）、<code class="language-plaintext highlighter-rouge">&amp;mut T</code>（可变引用）。详见第 10 节。</li>
  <li><strong>指针</strong>：<code class="language-plaintext highlighter-rouge">*T</code>（指向不可变数据）、<code class="language-plaintext highlighter-rouge">*mut T</code>（指向可变数据）。另见第 12 节。</li>
  <li><strong>结构体</strong>：<code class="language-plaintext highlighter-rouge">struct { ... }</code>，详见第 8 节。</li>
  <li><strong>枚举</strong>：<code class="language-plaintext highlighter-rouge">enum Name { Variant1, Variant2(负载类型), ... }</code>，详见第 9 节。</li>
</ul>

<h3 id="53-可选类型">5.3 可选类型</h3>
<p>标准库定义 <code class="language-plaintext highlighter-rouge">Option&lt;T&gt;</code> 枚举：</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">enum</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="nf">Some</span><span class="p">(</span><span class="n">T</span><span class="p">),</span>
    <span class="nb">None</span><span class="p">,</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>任何可能缺失的值应使用 <code class="language-plaintext highlighter-rouge">Option&lt;T&gt;</code> 表达。</p>

<h3 id="54-类型转换">5.4 类型转换</h3>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">as</code> 操作符</strong>：用于安全的位模式转换或数值宽化/窄化。<code class="language-plaintext highlighter-rouge">"123" as i32</code> 是编译错误。</li>
  <li><strong><code class="language-plaintext highlighter-rouge">parse</code> 函数</strong>：将字符串解析为数值，返回 <code class="language-plaintext highlighter-rouge">Result&lt;T, ParseError&gt;</code>。</li>
</ul>

<h3 id="55-类型推导">5.5 类型推导</h3>
<p>允许在初始化时省略类型标注，推导范围限定为局部变量和函数返回值。函数参数必须显式标注。</p>

<hr />

<h2 id="6-函数">6. 函数</h2>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">i32</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>函数体是求值语境，<strong>必须显式使用 <code class="language-plaintext highlighter-rouge">return</code> 提供返回值</strong>。</li>
  <li>所有执行路径都必须遇到 <code class="language-plaintext highlighter-rouge">return</code>，否则编译错误。</li>
  <li>不支持函数重载。Vox 鼓励用<strong>明确的函数名</strong>或<strong>结构体封装参数</strong>代替重载。</li>
  <li>函数参数默认不可变，如需可变需在参数名前加 <code class="language-plaintext highlighter-rouge">var</code>（例如 <code class="language-plaintext highlighter-rouge">fn change(var x: i32)</code>）。</li>
</ul>

<h3 id="61-返回类型推导">6.1 返回类型推导</h3>
<p>可从第一个 <code class="language-plaintext highlighter-rouge">return</code> 语句推导返回类型，但推荐显式标注。</p>

<h3 id="62-参数过多问题的推荐解法">6.2 参数过多问题的推荐解法</h3>
<p>当函数参数超过 3–4 个时，推荐将相关参数组织进一个结构体：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">struct</span> <span class="n">Config</span> <span class="p">{</span>
    <span class="n">host</span><span class="p">:</span> <span class="p">[]</span><span class="k">const</span> <span class="nb">u8</span><span class="p">,</span>
    <span class="n">port</span><span class="p">:</span> <span class="nb">u16</span><span class="p">,</span>
    <span class="n">timeout</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">connect</span><span class="p">(</span><span class="n">cfg</span><span class="p">:</span> <span class="n">Config</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="n">Connection</span><span class="p">,</span> <span class="n">Error</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>这种方式显式、自文档化，且不引入任何额外运行时开销。</p>

<hr />

<h2 id="7-控制流">7. 控制流</h2>

<h3 id="71-if-表达式--语句">7.1 <code class="language-plaintext highlighter-rouge">if</code> 表达式 / 语句</h3>
<p><code class="language-plaintext highlighter-rouge">if</code> 可作为语句或表达式，取决于所处语境。</p>
<ul>
  <li>作为<strong>语句</strong>：不要求所有分支产生值，不要求必有 <code class="language-plaintext highlighter-rouge">else</code>。</li>
  <li>作为<strong>表达式</strong>（求值语境）：<code class="language-plaintext highlighter-rouge">if</code> 必须包含 <code class="language-plaintext highlighter-rouge">else</code>，且每个分支必须用 <code class="language-plaintext highlighter-rouge">yield</code> 提供值。</li>
</ul>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="c1">// if 作为表达式</span>
<span class="n">var</span> <span class="n">status</span> <span class="o">=</span> <span class="k">if</span> <span class="n">cond</span> <span class="p">{</span>
    <span class="k">yield</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">yield</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>

<span class="c1">// if 作为语句</span>
<span class="k">if</span> <span class="n">flag</span> <span class="p">{</span>
    <span class="nf">do_work</span><span class="p">();</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="72-while-循环语句">7.2 <code class="language-plaintext highlighter-rouge">while</code> 循环（语句）</h3>
<p><code class="language-plaintext highlighter-rouge">while</code> 是语句，不返回值。</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">while</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span> <span class="p">{</span>
    <span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">5</span> <span class="p">{</span>
        <span class="k">continue</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">8</span> <span class="p">{</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="73-for-循环语句">7.3 <code class="language-plaintext highlighter-rouge">for</code> 循环（语句）</h3>
<p><code class="language-plaintext highlighter-rouge">for</code> 也是语句，不返回值。</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">for</span> <span class="n">item</span> <span class="k">in</span> <span class="n">collection</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">for</span> <span class="n">var</span> <span class="n">idx</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">len</span> <span class="p">{</span>
    <span class="n">arr</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">=</span> <span class="n">idx</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>迭代变量默认 <code class="language-plaintext highlighter-rouge">const</code>，如需可变用 <code class="language-plaintext highlighter-rouge">for var</code>。<code class="language-plaintext highlighter-rouge">break</code> 和 <code class="language-plaintext highlighter-rouge">continue</code> 可用标签。</p>

<h3 id="74-标签化的-break--continue">7.4 标签化的 <code class="language-plaintext highlighter-rouge">break</code> / <code class="language-plaintext highlighter-rouge">continue</code></h3>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="nv">'outer</span><span class="p">:</span> <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="mi">10</span> <span class="p">{</span>
    <span class="k">for</span> <span class="n">j</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="mi">10</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">matrix</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">==</span> <span class="n">target</span> <span class="p">{</span>
            <span class="k">break</span> <span class="nv">'outer</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="75-yield-表达式">7.5 <code class="language-plaintext highlighter-rouge">yield</code> 表达式</h3>
<p>用在<strong>求值语境下的内嵌块</strong>（<code class="language-plaintext highlighter-rouge">if</code>/<code class="language-plaintext highlighter-rouge">match</code>/独立块）中，提供块的值并退出该块。</p>
<ul>
  <li>如果在语句语境中出现 <code class="language-plaintext highlighter-rouge">yield</code>，编译错误。</li>
</ul>

<h3 id="76-独立块表达式">7.6 独立块表达式</h3>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">x</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">temp</span> <span class="o">=</span> <span class="nf">compute</span><span class="p">();</span>
    <span class="k">yield</span> <span class="n">temp</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>该块必须在所有出口用 <code class="language-plaintext highlighter-rouge">yield</code> 提供值。</p>

<hr />

<h2 id="8-结构体与方法">8. 结构体与方法</h2>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">struct</span> <span class="n">Point</span> <span class="p">{</span>
    <span class="n">x</span><span class="p">:</span> <span class="nb">f64</span><span class="p">,</span>
    <span class="n">y</span><span class="p">:</span> <span class="nb">f64</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1">// 方法定义：第一个参数显式为 self</span>
<span class="k">fn</span> <span class="n">Point</span><span class="nf">.length</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Point</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">f64</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nf">sqrt</span><span class="p">((</span><span class="k">self</span><span class="py">.x</span> <span class="o">*</span> <span class="k">self</span><span class="py">.x</span> <span class="o">+</span> <span class="k">self</span><span class="py">.y</span> <span class="o">*</span> <span class="k">self</span><span class="py">.y</span><span class="p">)</span> <span class="k">as</span> <span class="nb">f64</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>方法归属 struct 命名空间，通过 <code class="language-plaintext highlighter-rouge">Type.method()</code> 调用。</li>
  <li>无函数重载，无隐式 <code class="language-plaintext highlighter-rouge">self</code>。</li>
  <li><code class="language-plaintext highlighter-rouge">self</code> 参数可以是 <code class="language-plaintext highlighter-rouge">&amp;T</code>（不可变方法）、<code class="language-plaintext highlighter-rouge">&amp;mut T</code>（可变方法）或 <code class="language-plaintext highlighter-rouge">*T</code> / <code class="language-plaintext highlighter-rouge">*mut T</code>（底层指针方法）。</li>
  <li>调用语法糖：<code class="language-plaintext highlighter-rouge">p.length()</code> 脱糖为 <code class="language-plaintext highlighter-rouge">Point.length(&amp;p)</code>。</li>
  <li>若方法需要修改结构体，使用 <code class="language-plaintext highlighter-rouge">self: &amp;mut T</code>：
    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="n">Point</span><span class="nf">.set_x</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Point</span><span class="p">,</span> <span class="n">val</span><span class="p">:</span> <span class="nb">f64</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">self</span><span class="py">.x</span> <span class="o">=</span> <span class="n">val</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div>    </div>
  </li>
</ul>

<hr />

<h2 id="9-枚举与模式匹配">9. 枚举与模式匹配</h2>

<h3 id="91-枚举声明">9.1 枚举声明</h3>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">enum</span> <span class="n">OptionInt</span> <span class="p">{</span>
    <span class="nf">Some</span><span class="p">(</span><span class="nb">i32</span><span class="p">),</span>
    <span class="nb">None</span><span class="p">,</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>变体可携带负载类型。</p>

<h3 id="92-match-表达式">9.2 <code class="language-plaintext highlighter-rouge">match</code> 表达式</h3>
<ul>
  <li><code class="language-plaintext highlighter-rouge">match</code> 是表达式，必须位于求值语境或作为语句都可以（语句时各分支可无 <code class="language-plaintext highlighter-rouge">yield</code>）。</li>
  <li>在求值语境下，每个分支的块必须以 <code class="language-plaintext highlighter-rouge">yield</code> 提供值。</li>
  <li><strong>穷尽性</strong>：编译器强制匹配所有变体，否则编译错误。</li>
</ul>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">result</span> <span class="o">=</span> <span class="k">match</span> <span class="n">opt</span> <span class="p">{</span>
    <span class="nf">Some</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="k">yield</span> <span class="n">v</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nb">None</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="k">yield</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<hr />

<h2 id="10-引用与借用">10. 引用与借用</h2>

<h3 id="101-引用类型">10.1 引用类型</h3>

<p>Vox 提供两种引用类型，与裸指针并列但<strong>自带编译器安全检查（借用检查）</strong>：</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">类型</th>
      <th style="text-align: left">含义</th>
      <th style="text-align: center">可读</th>
      <th style="text-align: center">可写</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;T</code></td>
      <td style="text-align: left">不可变引用</td>
      <td style="text-align: center">✅</td>
      <td style="text-align: center">❌</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;mut T</code></td>
      <td style="text-align: left">可变引用</td>
      <td style="text-align: center">✅</td>
      <td style="text-align: center">✅</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*T</code></td>
      <td style="text-align: left">不可变裸指针</td>
      <td style="text-align: center">✅（不检查）</td>
      <td style="text-align: center">❌</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*mut T</code></td>
      <td style="text-align: left">可变裸指针</td>
      <td style="text-align: center">✅（不检查）</td>
      <td style="text-align: center">✅（不检查）</td>
    </tr>
  </tbody>
</table>

<p>引用的核心契约：”借用”——你暂时借走访问权，但不拥有数据，用完后必须归还。</p>

<h3 id="102-创建与解引用">10.2 创建与解引用</h3>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="k">const</span> <span class="n">data</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>

<span class="c1">// 创建引用</span>
<span class="k">const</span> <span class="n">r</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">;</span>          <span class="c1">// 不可变引用</span>
<span class="n">var</span> <span class="n">val</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="n">var</span> <span class="n">rm</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">val</span><span class="p">;</span>    <span class="c1">// 可变引用</span>

<span class="c1">// 解引用</span>
<span class="n">var</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="o">*</span><span class="n">r</span><span class="p">;</span>                <span class="c1">// 通过引用读取</span>
<span class="o">*</span><span class="n">rm</span> <span class="o">=</span> <span class="mi">99</span><span class="p">;</span>                       <span class="c1">// 通过可变引用写入</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">&amp;expr</code> 创建不可变引用，<code class="language-plaintext highlighter-rouge">&amp;mut expr</code> 创建可变引用。</li>
  <li>解引用操作符 <code class="language-plaintext highlighter-rouge">*</code> 用于通过引用访问其所指向的值。</li>
  <li>切片引用同理：<code class="language-plaintext highlighter-rouge">&amp;arr[0..3]</code> 的类型是 <code class="language-plaintext highlighter-rouge">&amp;[]i32</code>。</li>
</ul>

<h3 id="103-借用规则">10.3 借用规则</h3>

<p>编译器强制以下规则，确保引用不会导致数据竞争或悬空访问：</p>

<p><strong>规则 1（读写分离）</strong>：任意时刻，对于一个值，要么存在<strong>任意多个</strong> <code class="language-plaintext highlighter-rouge">&amp;T</code> 引用，要么存在<strong>唯一一个</strong> <code class="language-plaintext highlighter-rouge">&amp;mut T</code> 引用，不能同时存在。</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">data</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>

<span class="k">const</span> <span class="n">a</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">;</span>          <span class="c1">// ✅ 不可变引用 1</span>
<span class="k">const</span> <span class="n">b</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">;</span>          <span class="c1">// ✅ 不可变引用 2（与 a 共存）</span>
<span class="nf">print</span><span class="p">(</span><span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">*</span><span class="n">b</span><span class="p">);</span>                   <span class="c1">// ✅ 多个只读读者，安全</span>

<span class="n">var</span> <span class="n">c</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">data</span><span class="p">;</span>    <span class="c1">// ❌ 编译错误：已有不可变引用活跃</span>
<span class="o">*</span><span class="n">c</span> <span class="o">=</span> <span class="mi">99</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">data</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>

<span class="n">var</span> <span class="n">rw</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">data</span><span class="p">;</span>   <span class="c1">// ✅ 唯一可变引用</span>
<span class="o">*</span><span class="n">rw</span> <span class="o">=</span> <span class="mi">99</span><span class="p">;</span>

<span class="k">const</span> <span class="n">r</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">;</span>          <span class="c1">// ✅ 此时 rw 已不再使用，可创建新的不可变引用</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>规则 2（原值冻结）</strong>：当 <code class="language-plaintext highlighter-rouge">&amp;mut T</code> 引用存在且活跃时，<strong>原值被冻结</strong>——不可通过原值路径读或写。</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">data</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>

<span class="n">var</span> <span class="n">rw</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">data</span><span class="p">;</span>   <span class="c1">// rw 借走了 data 的可变访问权</span>
<span class="nf">print</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>                     <span class="c1">// ❌ 编译错误：data 已被可变借用冻结</span>
<span class="o">*</span><span class="n">rw</span> <span class="o">=</span> <span class="mi">99</span><span class="p">;</span>                        <span class="c1">// ✅ 只能通过借用者访问</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>规则 3（存活期不超过被借者）</strong>：引用的生命周期不能超过被引用变量的生命周期。</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">r</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span><span class="p">;</span>                     <span class="c1">// 声明一个引用变量</span>

<span class="p">{</span>
    <span class="n">var</span> <span class="n">local</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
    <span class="n">r</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">local</span><span class="p">;</span>                  <span class="c1">// ❌ 编译错误：local 离开作用域后 r 将悬空</span>
<span class="p">}</span>

<span class="nf">print</span><span class="p">(</span><span class="o">*</span><span class="n">r</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="104-引用与裸指针的选择">10.4 引用与裸指针的选择</h3>

<table>
  <thead>
    <tr>
      <th style="text-align: left">场景</th>
      <th style="text-align: left">推荐使用</th>
      <th style="text-align: left">理由</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left">想要编译器保障安全</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;T</code> / <code class="language-plaintext highlighter-rouge">&amp;mut T</code></td>
      <td style="text-align: left">借用检查器防止悬空和数据竞争</td>
    </tr>
    <tr>
      <td style="text-align: left">需要指针运算</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*T</code> / <code class="language-plaintext highlighter-rouge">*mut T</code></td>
      <td style="text-align: left">引用不提供算术运算</td>
    </tr>
    <tr>
      <td style="text-align: left">与 C 代码交互</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*T</code> / <code class="language-plaintext highlighter-rouge">*mut T</code></td>
      <td style="text-align: left">直接映射 C 指针</td>
    </tr>
    <tr>
      <td style="text-align: left">实现自定义分配器</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*mut T</code></td>
      <td style="text-align: left">底层内存操作需要完全控制</td>
    </tr>
    <tr>
      <td style="text-align: left">对 null 语义的需求</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*T</code> / <code class="language-plaintext highlighter-rouge">*mut T</code></td>
      <td style="text-align: left">引用不可为 null</td>
    </tr>
  </tbody>
</table>

<p>引用和裸指针可以自由互转——这种转换是显式的，编译器不做隐藏的转换：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">const</span> <span class="n">data</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
<span class="k">const</span> <span class="n">r</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">;</span>
<span class="k">const</span> <span class="n">p</span><span class="p">:</span> <span class="o">*</span><span class="nb">i32</span> <span class="o">=</span> <span class="n">r</span> <span class="k">as</span> <span class="o">*</span><span class="nb">i32</span><span class="p">;</span>       <span class="c1">// 引用 → 裸指针：显式转换</span>
<span class="k">const</span> <span class="n">r2</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;*</span><span class="n">p</span><span class="p">;</span>            <span class="c1">// 裸指针 → 引用：需确保此时 p 仍有效</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="105-函数中的引用">10.5 函数中的引用</h3>

<p>引用可以作为函数参数和返回值类型：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="c1">// 读取值（不可变借用）</span>
<span class="k">fn</span> <span class="nf">get_value</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>
    <span class="k">return</span> <span class="o">*</span><span class="n">data</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// 修改值（可变借用）</span>
<span class="k">fn</span> <span class="nf">increment</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">i32</span><span class="p">)</span> <span class="p">{</span>
    <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="o">*</span><span class="n">data</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// 返回引用</span>
<span class="k">fn</span> <span class="nf">first</span><span class="p">(</span><span class="n">arr</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="nb">i32</span><span class="p">])</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="nb">i32</span> <span class="p">{</span>
    <span class="k">return</span> <span class="o">&amp;</span><span class="n">arr</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>多个引用参数时的生命周期省略规则</strong>（由编译器自动推断，无需手动标注）：</p>
<ul>
  <li>如果只有一个引用参数，其生命周期自动关联到返回值的引用。</li>
  <li>如果有多个引用参数，且返回值是引用类型，编译器要求显式标注以消除歧义（未来版本可能引入简化标注语法）。</li>
</ul>

<h3 id="106-方法中的-self-引用">10.6 方法中的 self 引用</h3>

<p>方法定义时，<code class="language-plaintext highlighter-rouge">self</code> 参数的类型决定了该方法是否允许修改结构体：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="k">struct</span> <span class="n">Counter</span> <span class="p">{</span>
    <span class="n">value</span><span class="p">:</span> <span class="nb">i32</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1">// 不可变方法：只能读取</span>
<span class="k">fn</span> <span class="n">Counter</span><span class="nf">.read</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Counter</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">self</span><span class="py">.value</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// 可变方法：可以修改</span>
<span class="k">fn</span> <span class="n">Counter</span><span class="nf">.add</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Counter</span><span class="p">,</span> <span class="n">delta</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">self</span><span class="py">.value</span> <span class="o">=</span> <span class="k">self</span><span class="py">.value</span> <span class="o">+</span> <span class="n">delta</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="107-引用与-defer-的关系">10.7 引用与 defer 的关系</h3>

<p>引用和 <code class="language-plaintext highlighter-rouge">defer</code> 是<strong>两个独立的机制，各司其职</strong>：</p>

<ul>
  <li><strong>引用（<code class="language-plaintext highlighter-rouge">&amp;T</code> / <code class="language-plaintext highlighter-rouge">&amp;mut T</code>）</strong>：管理”访问权”——谁能读、谁能写、能借用多久。</li>
  <li><strong><code class="language-plaintext highlighter-rouge">defer</code></strong>：管理”资源”——文件、堆内存、锁等资源何时释放。</li>
</ul>

<p>两者互补，互不替代：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">process_with_resource</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">buf</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
    <span class="n">defer</span> <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span>      <span class="c1">// defer 管释放</span>

    <span class="n">var</span> <span class="n">view</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[]</span><span class="nb">u8</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">buf</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">512</span><span class="p">];</span>   <span class="c1">// 引用管访问安全</span>
    <span class="nf">process</span><span class="p">(</span><span class="n">view</span><span class="p">);</span>
    <span class="c1">// view 超出作用域，借用结束</span>
    <span class="c1">// 但 buf 直到函数退出才由 defer 释放</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="108-引用的转译策略到-c">10.8 引用的转译策略（到 C）</h3>

<p>引用在转译到 C 时，<strong>直接映射为同类型的指针</strong>，零运行时开销：</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">Vox</th>
      <th style="text-align: left">C</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;T</code></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">T*</code>（const 限定，若原类型不可变）</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;mut T</code></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">T*</code></td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;expr</code></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;expr</code>（C 的取地址）</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*r</code>（解引用）</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*r</code>（C 的解引用）</td>
    </tr>
  </tbody>
</table>

<p>借用检查是纯编译期机制，转译后完全消失，不影响生成的 C 代码。</p>

<hr />

<h2 id="11-所有权与转移">11. 所有权与转移</h2>

<p>Vox 引入<strong>显式所有权转移（<code class="language-plaintext highlighter-rouge">move</code> 语义）</strong>，与引用借用规则（第 10 节）共同构成完整的所有权体系：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>引用（&amp;T / &amp;mut T）  →  借用：暂时借走访问权，用完归还
move 转移           →  转让：永久转让所有权，原变量失效
赋值（=）           →  复制：仅对安全类型（纯数据）允许
</pre></td></tr></tbody></table></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">move</code> 关键字是 Vox 设计哲学的延续——<strong>所有所有权操作在代码字面上显式可见</strong>。</p>

<h3 id="111-move-表达式">11.1 <code class="language-plaintext highlighter-rouge">move</code> 表达式</h3>

<p><code class="language-plaintext highlighter-rouge">move</code> 是一个<strong>一元表达式操作符</strong>，将其操作数变量的所有权转移到新变量：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">data</span><span class="p">:</span> <span class="p">[</span><span class="nb">i32</span><span class="p">;</span> <span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">];</span>
<span class="n">var</span> <span class="n">transferred</span> <span class="o">=</span> <span class="k">move</span> <span class="n">data</span><span class="p">;</span>   <span class="c1">// data 的所有权转移到 transferred</span>
<span class="nf">print</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>                <span class="c1">// ❌ 编译错误：data 已被 move，不可再使用</span>
<span class="nf">print</span><span class="p">(</span><span class="n">transferred</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>         <span class="c1">// ✅ 转移后的变量正常可用</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">move</code> 的可见效果</strong>：</p>
<ul>
  <li>源变量在 move 后<strong>被冻结</strong>——编译器禁止任何对源变量的读写访问。</li>
  <li>目标变量获得数据的所有权，其生命周期结束时负责资源的释放（若有）。</li>
  <li><code class="language-plaintext highlighter-rouge">move</code> 是一个纯编译期概念，<strong>不产生任何运行时操作</strong>——它只改变编译器对变量活跃状态的跟踪。</li>
</ul>

<h3 id="112-为什么不是隐式-move">11.2 为什么不是隐式 move？</h3>

<p><code class="language-plaintext highlighter-rouge">var b = a</code> <strong>不</strong>隐含 move 语义。这与 Rust 不同，是设计上的主动选择：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">a</span> <span class="o">=</span> <span class="n">SomeStruct</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="mi">10</span> <span class="p">};</span>
<span class="n">var</span> <span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span>     <span class="c1">// ✅ 当前：复制语义（a 仍有效）</span>
<span class="n">var</span> <span class="n">c</span> <span class="o">=</span> <span class="k">move</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// ✅ move 语义（a 失效）</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>理由</strong>：如果 <code class="language-plaintext highlighter-rouge">var b = a</code> 这行字面上没有说 <code class="language-plaintext highlighter-rouge">a</code> 会失效，那么 <code class="language-plaintext highlighter-rouge">a</code> 就不应该失效。”显式优于隐式”原则要求：<strong>所有权转移必须通过 <code class="language-plaintext highlighter-rouge">move</code> 关键字明确表达</strong>。</p>

<h3 id="113-隐式复制规则哪些类型可以安全复制">11.3 隐式复制规则（哪些类型可以安全复制）</h3>

<p>并非所有类型都允许隐式复制。编译器根据类型是否包含资源自动判断：</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">类型类别</th>
      <th style="text-align: left">示例</th>
      <th style="text-align: center">隐式复制 <code class="language-plaintext highlighter-rouge">var b = a</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left"><strong>原始类型</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">i32</code>, <code class="language-plaintext highlighter-rouge">f64</code>, <code class="language-plaintext highlighter-rouge">bool</code>, <code class="language-plaintext highlighter-rouge">unit</code></td>
      <td style="text-align: center">✅ 允许</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>纯数据 struct</strong>（无指针、无资源）</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">struct Point { x: f32, y: f32 }</code></td>
      <td style="text-align: center">✅ 允许</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>定长数组</strong>（元素为可复制类型）</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">[i32; 10]</code></td>
      <td style="text-align: center">✅ 允许</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>带指针字段的 struct</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">struct Node { data: *mut u8 }</code></td>
      <td style="text-align: center">❌ 禁止（编译器报错）</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>带 <code class="language-plaintext highlighter-rouge">defer</code> 资源的类型</strong></td>
      <td style="text-align: left">函数内持有 <code class="language-plaintext highlighter-rouge">defer free(p)</code> 的变量</td>
      <td style="text-align: center">❌ 禁止（编译器报错）</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>引用类型</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;T</code>, <code class="language-plaintext highlighter-rouge">&amp;mut T</code></td>
      <td style="text-align: center">✅ 允许（引用是 Copy 的）</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>切片</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">[]T</code>, <code class="language-plaintext highlighter-rouge">[]const T</code></td>
      <td style="text-align: center">❌ 禁止（胖指针涉及数据引用）</td>
    </tr>
  </tbody>
</table>

<p>当编译器禁止隐式复制时，程序员必须使用 <code class="language-plaintext highlighter-rouge">move</code> 或显式 <code class="language-plaintext highlighter-rouge">.clone()</code>：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">buf</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="n">defer</span> <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span>     <span class="c1">// 资源类型</span>

<span class="n">var</span> <span class="n">other</span> <span class="o">=</span> <span class="n">buf</span><span class="p">;</span>                 <span class="c1">// ❌ 编译错误：不能隐式复制资源类型</span>
<span class="n">var</span> <span class="n">other</span> <span class="o">=</span> <span class="k">move</span> <span class="n">buf</span><span class="p">;</span>            <span class="c1">// ✅ 所有权转移</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="114-move-在函数参数传递中">11.4 <code class="language-plaintext highlighter-rouge">move</code> 在函数参数传递中</h3>

<p><code class="language-plaintext highlighter-rouge">move</code> 也可用于函数调用时显式转移所有权：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">consume</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// 函数获得所有权</span>
    <span class="n">defer</span> <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
    <span class="nf">process</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">var</span> <span class="n">buf</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="nf">consume</span><span class="p">(</span><span class="k">move</span> <span class="n">buf</span><span class="p">);</span>               <span class="c1">// ✅ 显式转移所有权给函数</span>
<span class="nf">print</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span>                      <span class="c1">// ❌ 编译错误：buf 已被 move</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>函数参数默认不转移所有权</strong>——传参默认是复制（对允许复制的类型）或编译错误（对资源类型）。需要转移时必须显式使用 <code class="language-plaintext highlighter-rouge">move</code>。</p>

<h3 id="115-move-在返回值中">11.5 <code class="language-plaintext highlighter-rouge">move</code> 在返回值中</h3>

<p>从函数返回变量时，若变量是资源类型，编译器要求：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">create_buffer</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">buf</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">256</span><span class="p">);</span>
    <span class="n">defer</span> <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span> <span class="c1">// 不，defer 会在 return 时执行！</span>
    <span class="k">return</span> <span class="n">buf</span><span class="p">;</span>                  <span class="c1">// ❌ 编译错误：defer 会在 return 前释放 buf</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>正确写法：<strong>资源类型的返回必须通过 <code class="language-plaintext highlighter-rouge">move</code> 解除 <code class="language-plaintext highlighter-rouge">defer</code> 绑定</strong>：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">create_buffer</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">buf</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">256</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">buf</span><span class="p">;</span>                  <span class="c1">// 无 defer：返回所有权，由调用者负责释放</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>编译器规则：若变量有活跃的 <code class="language-plaintext highlighter-rouge">defer</code> 资源释放，则不可被返回或 <code class="language-plaintext highlighter-rouge">move</code> 到函数参数中。</p>

<h3 id="116-move-与引用的交互">11.6 <code class="language-plaintext highlighter-rouge">move</code> 与引用的交互</h3>

<p>一个变量在<strong>被借用期间</strong>不能 <code class="language-plaintext highlighter-rouge">move</code>：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">data</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
<span class="n">var</span> <span class="n">r</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">;</span>            <span class="c1">// 借用 data</span>
<span class="n">var</span> <span class="n">moved</span> <span class="o">=</span> <span class="k">move</span> <span class="n">data</span><span class="p">;</span>          <span class="c1">// ❌ 编译错误：data 已被借用（引用 r 活跃）</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>当借用结束时（引用不再使用），可以 <code class="language-plaintext highlighter-rouge">move</code>：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">data</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
<span class="p">{</span>
    <span class="n">var</span> <span class="n">r</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">i32</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">;</span>        <span class="c1">// 借用开始</span>
    <span class="nf">print</span><span class="p">(</span><span class="o">*</span><span class="n">r</span><span class="p">);</span>
<span class="p">}</span>                               <span class="c1">// 借用结束，r 超出作用域</span>
<span class="n">var</span> <span class="n">moved</span> <span class="o">=</span> <span class="k">move</span> <span class="n">data</span><span class="p">;</span>          <span class="c1">// ✅ 此时可以 move</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="117-move-的转译策略到-c">11.7 <code class="language-plaintext highlighter-rouge">move</code> 的转译策略（到 C）</h3>

<p><code class="language-plaintext highlighter-rouge">move</code> 是纯编译期概念，在转译到 C 时<strong>完全不产生额外代码</strong>。它只影响编译器的变量活跃分析：</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">Vox</th>
      <th style="text-align: left">C 效果</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">var b = move a;</code></td>
      <td style="text-align: left">编译器不再允许后续对 <code class="language-plaintext highlighter-rouge">a</code> 的访问；生成的 C 代码中 <code class="language-plaintext highlighter-rouge">b</code> 接管了 <code class="language-plaintext highlighter-rouge">a</code> 的所有引用</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">consume(move buf);</code></td>
      <td style="text-align: left">生成的 C 代码中，<code class="language-plaintext highlighter-rouge">buf</code> 变量在函数调用后不再被引用</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">return buf;</code></td>
      <td style="text-align: left">生成的 C 代码等同于普通返回</td>
    </tr>
  </tbody>
</table>

<p>引用检查（借用规则）和所有权跟踪（move）都是纯编译期机制，转译后完全消失，不影响生成的 C 代码。</p>

<h3 id="118-move-与-lambda-捕获">11.8 <code class="language-plaintext highlighter-rouge">move</code> 与 lambda 捕获</h3>

<p><code class="language-plaintext highlighter-rouge">move</code> 在 lambda 捕获中也有对应语义——见第 <a href="#1945-move-捕获-lambda">19.4.5 节</a>。</p>

<hr />

<h2 id="12-指针与内存管理">12. 指针与内存管理</h2>

<h3 id="121-裸指针">12.1 裸指针</h3>
<ul>
  <li><code class="language-plaintext highlighter-rouge">*T</code>：指向不可变数据的指针。</li>
  <li><code class="language-plaintext highlighter-rouge">*mut T</code>：指向可变数据的指针。</li>
  <li>指针运算：<code class="language-plaintext highlighter-rouge">+</code>、<code class="language-plaintext highlighter-rouge">-</code>（步长为指向类型大小），<code class="language-plaintext highlighter-rouge">*</code> 解引用。</li>
  <li>指针可为 <code class="language-plaintext highlighter-rouge">null</code>，用 <code class="language-plaintext highlighter-rouge">null</code> 字面量产生空指针。</li>
</ul>

<h3 id="122-堆分配">12.2 堆分配</h3>
<p>标准库提供分配器对象 <code class="language-plaintext highlighter-rouge">std_heap</code>，底层为 <code class="language-plaintext highlighter-rouge">malloc</code>/<code class="language-plaintext highlighter-rouge">free</code> 映射。</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">alloc</span> <span class="o">=</span> <span class="n">std_heap</span><span class="p">;</span>
<span class="n">var</span> <span class="n">mem</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="n">defer</span> <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">mem</span><span class="p">);</span> <span class="c1">// 作用域出口自动释放</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="123-defer-语句">12.3 <code class="language-plaintext highlighter-rouge">defer</code> 语句</h3>
<p><code class="language-plaintext highlighter-rouge">defer</code> 后跟函数调用或块，在所在作用域退出前按后进先出顺序执行。纯编译期语法糖，零运行开销。</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">process</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">f</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="s">"data.txt"</span><span class="p">);</span>
    <span class="n">defer</span> <span class="nf">close</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>

    <span class="n">var</span> <span class="n">mem</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">256</span><span class="p">);</span>
    <span class="n">defer</span> <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">mem</span><span class="p">);</span>
    <span class="c1">// 无论函数如何退出，两个 defer 都会逆序执行</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="124-内存安全哲学">12.4 内存安全哲学</h3>
<p>Vox 提供<strong>渐进式安全</strong>，程序员可以自由选择安全级别：</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">级别</th>
      <th style="text-align: left">使用方式</th>
      <th style="text-align: center">保护程度</th>
      <th style="text-align: left">适用场景</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left">安全</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;T</code> / <code class="language-plaintext highlighter-rouge">&amp;mut T</code> + <code class="language-plaintext highlighter-rouge">defer</code></td>
      <td style="text-align: center">高（编译器检查）</td>
      <td style="text-align: left">业务逻辑</td>
    </tr>
    <tr>
      <td style="text-align: left">中等</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">&amp;T</code> / <code class="language-plaintext highlighter-rouge">&amp;mut T</code> + 手动管理</td>
      <td style="text-align: center">中（只防悬空引用）</td>
      <td style="text-align: left">性能敏感区</td>
    </tr>
    <tr>
      <td style="text-align: left">自由</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*T</code> / <code class="language-plaintext highlighter-rouge">*mut T</code> + <code class="language-plaintext highlighter-rouge">defer</code></td>
      <td style="text-align: center">低（程序员保证）</td>
      <td style="text-align: left">底层系统编程</td>
    </tr>
    <tr>
      <td style="text-align: left">裸</td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">*T</code> / <code class="language-plaintext highlighter-rouge">*mut T</code></td>
      <td style="text-align: center">无（完全手动）</td>
      <td style="text-align: left">自定义分配器、FFI</td>
    </tr>
  </tbody>
</table>

<p>语言本身<strong>不强制</strong>任何安全级别，程序员通过选择类型来选定保护程度。未来可通过编译选项全局收紧安全检查。</p>

<hr />

<h2 id="13-数组与切片">13. 数组与切片</h2>

<h3 id="131-定长数组-t-n">13.1 定长数组 <code class="language-plaintext highlighter-rouge">[T; N]</code></h3>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">arr</span><span class="p">:</span> <span class="p">[</span><span class="nb">i32</span><span class="p">;</span> <span class="mi">5</span><span class="p">];</span>                     <span class="c1">// 声明，未初始化</span>
<span class="n">var</span> <span class="n">arr</span><span class="p">:</span> <span class="p">[</span><span class="nb">i32</span><span class="p">;</span> <span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">];</span>   <span class="c1">// 声明并初始化</span>
<span class="k">const</span> <span class="n">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">];</span>                 <span class="c1">// 类型推导为 [i32; 3]</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>定长数组是值类型，赋值会复制整个数组。</li>
  <li>索引越界是未定义行为，由程序员自行保证。</li>
  <li>数组字面量必须提供与声明大小匹配的元素数量。</li>
</ul>

<p><strong>访问</strong>：</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">arr</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="n">var</span> <span class="n">x</span> <span class="o">=</span> <span class="n">arr</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="132-切片-t-和-const-t">13.2 切片 <code class="language-plaintext highlighter-rouge">[]T</code> 和 <code class="language-plaintext highlighter-rouge">[]const T</code></h3>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">slice</span><span class="p">:</span> <span class="p">[]</span><span class="nb">i32</span> <span class="o">=</span> <span class="n">arr</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">3</span><span class="p">];</span>          <span class="c1">// 可变切片，可修改元素</span>
<span class="n">var</span> <span class="n">view</span><span class="p">:</span> <span class="p">[]</span><span class="k">const</span> <span class="nb">i32</span> <span class="o">=</span> <span class="n">arr</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">3</span><span class="p">];</span>     <span class="c1">// 不可变切片，只读</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>切片是胖指针，包含数据指针和长度。</li>
  <li>字符串字面量 <code class="language-plaintext highlighter-rouge">"hello"</code> 的类型为 <code class="language-plaintext highlighter-rouge">[]const u8</code>。</li>
  <li><code class="language-plaintext highlighter-rouge">[]T</code> 的元素可修改，<code class="language-plaintext highlighter-rouge">[]const T</code> 的元素只读。</li>
  <li>切片操作 <code class="language-plaintext highlighter-rouge">arr[start..end]</code> 返回一个切片，左闭右开。</li>
</ul>

<h3 id="133-切片引用">13.3 切片引用</h3>

<p>可通过引用来借用切片的访问权，遵循借用规则：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">data</span><span class="p">:</span> <span class="p">[</span><span class="nb">i32</span><span class="p">;</span> <span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">];</span>

<span class="k">const</span> <span class="n">full</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="nb">i32</span><span class="p">]</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">;</span>            <span class="c1">// 对整个数组的不可变引用</span>
<span class="k">const</span> <span class="n">part</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="nb">i32</span><span class="p">]</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="mi">4</span><span class="p">];</span>      <span class="c1">// 对切片子集的不可变引用</span>
<span class="n">var</span> <span class="n">mut_part</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="p">[</span><span class="nb">i32</span><span class="p">]</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">2</span><span class="p">];</span>  <span class="c1">// 可变引用</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<hr />

<h2 id="14-输入输出">14. 输入输出</h2>

<p>Vox 提供两个内置符号用于基本的控制台交互。它们不是关键字，而是编译器自动导入的函数。</p>

<h3 id="141-输出">14.1 输出</h3>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nf">print</span><span class="p">(</span><span class="s">"Hello, "</span><span class="p">);</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"World!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"The answer is "</span><span class="p">,</span> <span class="mi">42</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">print</code> 接受任意数量的参数，按顺序输出到标准输出。</li>
  <li>参数类型必须为 <code class="language-plaintext highlighter-rouge">[]const u8</code>、整型、浮点型或布尔型。</li>
  <li><strong>不自动追加换行符</strong>。换行由程序员通过 <code class="language-plaintext highlighter-rouge">\n</code> 显式控制。</li>
  <li>不提供格式化占位符。需要格式化时，使用字符串拼接或未来标准库中的格式化模块。</li>
</ul>

<h3 id="142-输入">14.2 输入</h3>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">line</span> <span class="o">=</span> <span class="nf">input</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">input()</code> 从标准输入读取一行，返回 <code class="language-plaintext highlighter-rouge">[]const u8</code>。</li>
  <li>返回的字符串不包含末尾的换行符。</li>
  <li>若需要将输入解析为数值，使用 <code class="language-plaintext highlighter-rouge">parse</code> 函数：</li>
</ul>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">n</span> <span class="o">=</span> <span class="nf">parse</span><span class="p">(</span><span class="nf">input</span><span class="p">())</span> <span class="k">as</span> <span class="nb">i32</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="143-实现说明">14.3 实现说明</h3>

<p><code class="language-plaintext highlighter-rouge">print</code> 和 <code class="language-plaintext highlighter-rouge">input</code> 在转译到 C 时，直接映射到 C 标准库的 <code class="language-plaintext highlighter-rouge">printf</code> / <code class="language-plaintext highlighter-rouge">puts</code> 和 <code class="language-plaintext highlighter-rouge">fgets</code>，无额外运行时开销。</p>

<hr />

<h2 id="15-模块系统">15. 模块系统</h2>

<p><strong>文件即模块</strong>：每个 <code class="language-plaintext highlighter-rouge">.vox</code> 文件就是一个模块。</p>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">pub</code> 关键字</strong>：声明公有符号（函数、类型、变量）。</li>
  <li><strong><code class="language-plaintext highlighter-rouge">import</code> 语句</strong>：导入其他模块。</li>
</ul>

<p><strong>示例：</strong></p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="c1">// math.vox</span>
<span class="k">pub</span> <span class="k">const</span> <span class="n">PI</span> <span class="o">=</span> <span class="mf">3.14159</span><span class="p">;</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">surface</span><span class="p">(</span><span class="n">r</span><span class="p">:</span> <span class="nb">f64</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">f64</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">PI</span> <span class="o">*</span> <span class="n">r</span> <span class="o">*</span> <span class="n">r</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// main.vox</span>
<span class="n">import</span> <span class="n">math</span><span class="p">;</span>
<span class="nf">print</span><span class="p">(</span><span class="n">math</span><span class="nf">.surface</span><span class="p">(</span><span class="mf">10.0</span><span class="p">));</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<ul>
  <li>导入模板：<code class="language-plaintext highlighter-rouge">import module_name</code>，之后使用 <code class="language-plaintext highlighter-rouge">module_name.member</code> 访问。</li>
  <li>模块路径对应文件系统：<code class="language-plaintext highlighter-rouge">import math.geometry</code> 查找 <code class="language-plaintext highlighter-rouge">math/geometry.vox</code>。</li>
  <li>模块通过文件路径隐式形成命名空间，无需手动声明。</li>
</ul>

<hr />

<h2 id="16-错误处理">16. 错误处理</h2>

<p>标准库定义 <code class="language-plaintext highlighter-rouge">Result&lt;T, E&gt;</code> 枚举：</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">enum</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="nf">Ok</span><span class="p">(</span><span class="n">T</span><span class="p">),</span>
    <span class="nf">Err</span><span class="p">(</span><span class="n">E</span><span class="p">),</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>推荐函数在可能失败时返回 <code class="language-plaintext highlighter-rouge">Result</code>。</li>
  <li>提供 <code class="language-plaintext highlighter-rouge">try</code> 操作符（语法糖），若表达式结果为 <code class="language-plaintext highlighter-rouge">Err</code> 则立即从当前函数返回该错误：</li>
</ul>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">read_config</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="n">Config</span><span class="p">,</span> <span class="n">Error</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">data</span> <span class="o">=</span> <span class="k">try</span> <span class="nf">read_file</span><span class="p">(</span><span class="s">"config.json"</span><span class="p">);</span>
    <span class="k">return</span> <span class="nf">parse_config</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<hr />

<h2 id="17-与-c-的互操作">17. 与 C 的互操作</h2>

<ul>
  <li><strong>声明外部函数</strong>：
    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">extern</span> <span class="s">"C"</span> <span class="p">{</span>
  <span class="k">fn</span> <span class="nf">printf</span><span class="p">(</span><span class="n">format</span><span class="p">:</span> <span class="o">*</span><span class="k">const</span> <span class="nb">u8</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span><span class="p">;</span>
  <span class="k">fn</span> <span class="nf">malloc</span><span class="p">(</span><span class="n">size</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div>    </div>
  </li>
  <li><strong>结构布局</strong>：默认 C 布局，字段不重排。</li>
  <li><strong>指针映射</strong>：<code class="language-plaintext highlighter-rouge">*T</code> 直接对应 C 指针。<code class="language-plaintext highlighter-rouge">&amp;T</code> 也映射为 C 指针（纯编译期抽象，零开销）。</li>
</ul>

<hr />

<h2 id="18-测试">18. 测试</h2>

<p>使用 <code class="language-plaintext highlighter-rouge">test</code> 块编写单元测试：</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">test</span> <span class="s">"add positive"</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">add</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
    <span class="k">if</span> <span class="n">result</span> <span class="o">!=</span> <span class="mi">5</span> <span class="p">{</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"test failed"</span><span class="p">);</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<ul>
  <li>编译时指定 <code class="language-plaintext highlighter-rouge">--test</code> 标志将运行所有 <code class="language-plaintext highlighter-rouge">test</code> 块。</li>
  <li><code class="language-plaintext highlighter-rouge">test</code> 块能访问模块私有函数。</li>
</ul>

<hr />

<h2 id="19-语法糖与脱糖规则">19. 语法糖与脱糖规则</h2>

<h3 id="191-方法调用">19.1 方法调用</h3>
<p><code class="language-plaintext highlighter-rouge">obj.method(args)</code> 脱糖为 <code class="language-plaintext highlighter-rouge">Type.method(&amp;obj, args)</code>（若 <code class="language-plaintext highlighter-rouge">self: &amp;T</code> 或 <code class="language-plaintext highlighter-rouge">self: *T</code>）或 <code class="language-plaintext highlighter-rouge">Type.method(&amp;mut obj, args)</code>（若 <code class="language-plaintext highlighter-rouge">self: &amp;mut T</code> 或 <code class="language-plaintext highlighter-rouge">self: *mut T</code>）。</p>

<h3 id="192-索引操作">19.2 索引操作</h3>
<p><code class="language-plaintext highlighter-rouge">a[b]</code> 脱糖为 <code class="language-plaintext highlighter-rouge">a.at(b)</code>（若用于读取）或 <code class="language-plaintext highlighter-rouge">a.at_mut(b)</code>（若用于赋值）。因此自定义类型只需实现 <code class="language-plaintext highlighter-rouge">at</code> / <code class="language-plaintext highlighter-rouge">at_mut</code> 方法即可支持下标语法。</p>

<h3 id="193-try-表达式">19.3 <code class="language-plaintext highlighter-rouge">try</code> 表达式</h3>
<p><code class="language-plaintext highlighter-rouge">try expr</code> 脱糖为：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre>match expr {
    Ok(val) =&gt; val,
    Err(e) =&gt; return Err(e),
}
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="194-lambda">19.4 Lambda</h3>

<p>Lambda 是纯编译期语法糖，转译到 C 后有明确的映射规则。现代 C 编译器优化能力极强，闭包结构体和函数指针调用通常可被完全内联优化，最终二进制等价于手写 C。</p>

<h4 id="1941-lambda-语法">19.4.1 Lambda 语法</h4>

<p>Lambda 使用 <code class="language-plaintext highlighter-rouge">fn</code> 关键字定义，可选捕获列表声明对外部变量的捕获方式。</p>

<p>捕获列表 <code class="language-plaintext highlighter-rouge">[...]</code> 位于参数列表之前，可包含<strong>默认捕获关键字</strong>和<strong>变量捕获项</strong>：</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">写法</th>
      <th style="text-align: left">含义</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">[copy]</code></td>
      <td style="text-align: left">默认<strong>值捕获</strong>——lambda 体内所有用到的外部变量以副本形式捕获</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">[ref]</code></td>
      <td style="text-align: left">默认<strong>引用捕获</strong>——lambda 体内所有用到的外部变量以引用形式捕获</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">[move x]</code></td>
      <td style="text-align: left">指定 <code class="language-plaintext highlighter-rouge">x</code> 以所有权转移方式捕获（<code class="language-plaintext highlighter-rouge">x</code> 的所有权移入闭包）</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">[base]</code></td>
      <td style="text-align: left">指定 <code class="language-plaintext highlighter-rouge">base</code> 以值捕获</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">[&amp;config]</code></td>
      <td style="text-align: left">指定 <code class="language-plaintext highlighter-rouge">config</code> 以不可变引用捕获</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">[&amp;mut counter]</code></td>
      <td style="text-align: left">指定 <code class="language-plaintext highlighter-rouge">counter</code> 以可变引用捕获</td>
    </tr>
    <tr>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">[]</code> 或无捕获列表</td>
      <td style="text-align: left">不捕获任何外部变量（纯函数）</td>
    </tr>
  </tbody>
</table>

<p>默认捕获关键字和变量捕获项可混合使用，变量项覆盖默认值：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
</pre></td><td class="rouge-code"><pre><span class="c1">// 无捕获列表 — 纯函数</span>
<span class="n">var</span> <span class="n">f</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span>

<span class="c1">// 指定捕获</span>
<span class="n">var</span> <span class="n">g</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="n">base</span><span class="p">]</span> <span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">base</span><span class="p">;</span> <span class="p">};</span>               <span class="c1">// 值捕获</span>
<span class="n">var</span> <span class="n">h</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="o">&amp;</span><span class="n">config</span><span class="p">]</span> <span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span> <span class="k">return</span> <span class="n">config</span><span class="py">.value</span><span class="p">;</span> <span class="p">};</span>               <span class="c1">// 引用捕获</span>
<span class="n">var</span> <span class="n">i</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">counter</span><span class="p">]</span> <span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="p">};</span>        <span class="c1">// 可变引用捕获</span>

<span class="c1">// 所有权转移捕获</span>
<span class="n">var</span> <span class="n">resource</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="n">var</span> <span class="n">k</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="k">move</span> <span class="n">resource</span><span class="p">]</span> <span class="p">()</span> <span class="k">-&gt;</span> <span class="n">void</span> <span class="p">{</span>                                <span class="c1">// resource 所有权移入闭包</span>
    <span class="n">defer</span> <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">resource</span><span class="p">);</span>
<span class="p">};</span>

<span class="c1">// 默认捕获关键字（编译器推断捕获哪些变量）</span>
<span class="n">var</span> <span class="n">j</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="n">copy</span><span class="p">]</span> <span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">base</span> <span class="o">+</span> <span class="n">counter</span><span class="p">;</span> <span class="p">};</span>      <span class="c1">// 默认全部值捕获</span>
<span class="n">var</span> <span class="n">l</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="k">ref</span><span class="p">]</span> <span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span> <span class="k">return</span> <span class="n">config</span><span class="py">.value</span> <span class="o">+</span> <span class="n">counter</span><span class="p">;</span> <span class="p">};</span>         <span class="c1">// 默认全部引用捕获</span>

<span class="c1">// 混合：默认 + 个别覆盖</span>
<span class="n">var</span> <span class="n">m</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="k">ref</span><span class="p">,</span> <span class="n">timeout</span><span class="p">]</span> <span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>                                   <span class="c1">// 默认引用，但 timeout 值捕获</span>
    <span class="k">return</span> <span class="n">config</span><span class="py">.value</span> <span class="o">+</span> <span class="n">timeout</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">var</span> <span class="n">n</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="n">copy</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">counter</span><span class="p">]</span> <span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>                            <span class="c1">// 默认值拷贝，但 counter 可变引用捕获</span>
    <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span> <span class="o">+</span> <span class="n">config</span><span class="py">.step</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">counter</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">var</span> <span class="n">o</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="n">copy</span><span class="p">,</span> <span class="k">move</span> <span class="n">resource</span><span class="p">]</span> <span class="p">()</span> <span class="k">-&gt;</span> <span class="n">void</span> <span class="p">{</span>                          <span class="c1">// 默认值拷贝，但 resource 所有权转移捕获</span>
    <span class="nf">process</span><span class="p">(</span><span class="n">resource</span><span class="p">);</span>
    <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">resource</span><span class="p">);</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>规则</strong>：</p>
<ul>
  <li>每个捕获项为：默认模式关键字（<code class="language-plaintext highlighter-rouge">copy</code>/<code class="language-plaintext highlighter-rouge">ref</code>）、<code class="language-plaintext highlighter-rouge">name</code>（值捕获）、<code class="language-plaintext highlighter-rouge">&amp;name</code>（不可变引用捕获）、<code class="language-plaintext highlighter-rouge">&amp;mut name</code>（可变引用捕获）或 <code class="language-plaintext highlighter-rouge">move name</code>（所有权转移捕获）。</li>
  <li>捕获列表中最多出现一个默认捕获关键字（<code class="language-plaintext highlighter-rouge">copy</code> 或 <code class="language-plaintext highlighter-rouge">ref</code>），且必须位于列表首位。</li>
  <li>无捕获列表时（即无 <code class="language-plaintext highlighter-rouge">[...]</code> 或 <code class="language-plaintext highlighter-rouge">[]</code>），lambda 体内只能使用参数和局部变量，不能引用外部变量。</li>
  <li>值捕获存储变量在闭包创建时的快照（副本），引用捕获存储指向外部变量的引用，move 捕获将所有权完全转入闭包（原变量失效）。</li>
  <li>默认捕获关键字（<code class="language-plaintext highlighter-rouge">[copy]</code>/<code class="language-plaintext highlighter-rouge">[ref]</code>）是一种<strong>可控的隐式</strong>：捕获的变量列表由编译器根据 lambda 体自动推断，但捕获方式由程序员显式指定。</li>
  <li>引用捕获和 move 捕获的 lambda 必须在其捕获变量的生命周期内使用，编译器在借用检查阶段验证这一点。</li>
</ul>

<h4 id="1942-无捕获-lambda--函数指针">19.4.2 无捕获 lambda → 函数指针</h4>

<p>当 lambda 没有捕获列表（不捕获任何外部变量）时，转译为独立的 C 函数，类型映射为函数指针。</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">f</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span>
<span class="n">var</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">f</span><span class="p">(</span><span class="mi">21</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="kt">int32_t</span> <span class="nf">lambda__1</span><span class="p">(</span><span class="kt">int32_t</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span> <span class="p">}</span>
<span class="kt">int32_t</span> <span class="p">(</span><span class="o">*</span><span class="n">f</span><span class="p">)(</span><span class="kt">int32_t</span><span class="p">)</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">lambda__1</span><span class="p">;</span>
<span class="kt">int32_t</span> <span class="n">result</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="mi">21</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>零运行时开销——调用方式与直接函数调用完全一致。</p>

<h4 id="1943-值捕获-lambda--闭包结构体">19.4.3 值捕获 lambda → 闭包结构体</h4>

<p>当 lambda 按值捕获外部变量时，编译器生成：</p>
<ol>
  <li>一个<strong>闭包结构体</strong>（<code class="language-plaintext highlighter-rouge">struct Closure</code>），捕获变量的副本作为其字段；</li>
  <li>一个<strong>独立的调用函数</strong>，接受 <code class="language-plaintext highlighter-rouge">Closure*</code> 作为首个参数（环境指针）。</li>
</ol>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">base</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="n">var</span> <span class="n">f</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="n">base</span><span class="p">]</span> <span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">base</span><span class="p">;</span> <span class="p">};</span>
<span class="n">var</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">f</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="c1">// 闭包结构体</span>
<span class="k">struct</span> <span class="n">Closure__1</span> <span class="p">{</span> <span class="kt">int32_t</span> <span class="n">base</span><span class="p">;</span> <span class="p">};</span>

<span class="c1">// 调用函数（首参数为环境指针）</span>
<span class="kt">int32_t</span> <span class="nf">lambda__1</span><span class="p">(</span><span class="k">struct</span> <span class="n">Closure__1</span><span class="o">*</span> <span class="n">env</span><span class="p">,</span> <span class="kt">int32_t</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">env</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// Vox 脱糖</span>
<span class="kt">int32_t</span> <span class="n">base</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">Closure__1</span> <span class="n">__env_1</span> <span class="o">=</span> <span class="p">{</span> <span class="p">.</span><span class="n">base</span> <span class="o">=</span> <span class="n">base</span> <span class="p">};</span>
<span class="kt">int32_t</span> <span class="n">result</span> <span class="o">=</span> <span class="n">lambda__1</span><span class="p">(</span><span class="o">&amp;</span><span class="n">__env_1</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="1944-引用捕获-lambda">19.4.4 引用捕获 lambda</h4>

<p>当 lambda 通过引用捕获外部变量时，闭包结构体中存储指向外部变量的引用（<code class="language-plaintext highlighter-rouge">&amp;T</code> 或 <code class="language-plaintext highlighter-rouge">&amp;mut T</code>）而非值副本。</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">var</span> <span class="n">f</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">counter</span><span class="p">]</span> <span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>
    <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">counter</span><span class="p">;</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="k">struct</span> <span class="n">Closure__1</span> <span class="p">{</span> <span class="kt">int32_t</span><span class="o">*</span> <span class="n">counter</span><span class="p">;</span> <span class="p">};</span>  <span class="c1">// &amp;mut T → T*</span>

<span class="kt">int32_t</span> <span class="nf">lambda__1</span><span class="p">(</span><span class="k">struct</span> <span class="n">Closure__1</span><span class="o">*</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span>
    <span class="o">*</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">counter</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">counter</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">return</span> <span class="o">*</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">counter</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>引用捕获的 lambda 必须在其捕获变量的生命周期内使用，编译器在借用检查阶段验证这一点。</p>

<h4 id="1945-move-捕获-lambda">19.4.5 Move 捕获 lambda</h4>

<p>当 lambda 通过 <code class="language-plaintext highlighter-rouge">move name</code> 捕获外部变量时，变量的所有权从原作用域转移至闭包结构体。原变量在 move 后失效，闭包结构体成为该值的新所有者。</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">var</span> <span class="n">resource</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="n">defer</span> <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">resource</span><span class="p">);</span>     <span class="c1">// ❌ 编译错误：resource 被 move 到闭包，不能有 defer</span>

<span class="n">var</span> <span class="n">resource</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="o">=</span> <span class="n">alloc</span><span class="nf">.allocate</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="n">var</span> <span class="n">cleanup</span> <span class="o">=</span> <span class="k">fn</span> <span class="p">[</span><span class="k">move</span> <span class="n">resource</span><span class="p">]</span> <span class="p">()</span> <span class="k">-&gt;</span> <span class="n">void</span> <span class="p">{</span>
    <span class="n">defer</span> <span class="n">alloc</span><span class="nf">.deallocate</span><span class="p">(</span><span class="n">resource</span><span class="p">);</span> <span class="c1">// ✅ defer 在闭包内部，资源由闭包管理</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="k">struct</span> <span class="n">Closure__1</span> <span class="p">{</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">resource</span><span class="p">;</span> <span class="p">};</span>

<span class="kt">void</span> <span class="nf">lambda__1</span><span class="p">(</span><span class="k">struct</span> <span class="n">Closure__1</span><span class="o">*</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// defer 展开：函数退出时释放</span>
    <span class="c1">// ... 函数体逻辑 ...</span>
    <span class="n">free</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">resource</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Vox 脱糖</span>
<span class="kt">uint8_t</span><span class="o">*</span> <span class="n">resource</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="k">struct</span> <span class="n">Closure__1</span> <span class="n">__env_1</span> <span class="o">=</span> <span class="p">{</span> <span class="p">.</span><span class="n">resource</span> <span class="o">=</span> <span class="n">resource</span> <span class="p">};</span>
<span class="c1">// resource 此后不可用</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>规则</strong>：</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">move</code> 捕获的变量，在 lambda 创建后<strong>原变量失效</strong>，不得再使用。</li>
  <li>若原变量有 <code class="language-plaintext highlighter-rouge">defer</code> 绑定的资源释放，编译器检测到变量被 <code class="language-plaintext highlighter-rouge">move</code> 后，自动将 <code class="language-plaintext highlighter-rouge">defer</code> 从原作用域剥离（交由闭包管理）。</li>
  <li>编译器在借用检查阶段验证 move 捕获的合法性（是否在借用期内被 move、是否重复 move 等）。</li>
</ul>

<hr />

<h2 id="20-arena-区域分配">20. Arena 区域分配</h2>

<p>Arena（区域分配器）是 Vox 内置的内存管理方案，提供<strong>零手动的批量分配与释放</strong>。Arena 的核心思想是：在同一作用域内分配的所有对象，在作用域退出时自动一次性释放。</p>

<h3 id="201-设计动机">20.1 设计动机</h3>

<ul>
  <li><strong>Rust 的所有权</strong>太麻烦：生命周期标注、borrow checker、<code class="language-plaintext highlighter-rouge">Rc</code>/<code class="language-plaintext highlighter-rouge">Arc</code>/<code class="language-plaintext highlighter-rouge">Box</code> 的选择焦虑。</li>
  <li><strong>C/C++ 的手动 free/delete</strong> 太烦：容易忘、容易漏、异常路径难以处理。</li>
  <li><strong>Arena 的承诺</strong>：
    <ul>
      <li>不需要写任何 <code class="language-plaintext highlighter-rouge">free</code> / <code class="language-plaintext highlighter-rouge">delete</code> / <code class="language-plaintext highlighter-rouge">defer</code></li>
      <li>释放是 O(1) 的（一次 <code class="language-plaintext highlighter-rouge">free</code> 调用）</li>
      <li>与 Vox 的”显式”哲学一致——<code class="language-plaintext highlighter-rouge">arena</code> 关键字就是显式声明”这段内存我托管”</li>
    </ul>
  </li>
</ul>

<h3 id="202-arena-块主力方案">20.2 Arena 块（主力方案）</h3>

<p>使用 <code class="language-plaintext highlighter-rouge">arena</code> 关键字声明一个 Arena 作用域：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="n">arena</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">a</span> <span class="o">=</span> <span class="n">new</span> <span class="n">MyStruct</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="mi">2</span> <span class="p">};</span>
    <span class="n">var</span> <span class="n">b</span> <span class="o">=</span> <span class="n">new</span> <span class="n">MyStruct</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="mi">4</span> <span class="p">};</span>
    <span class="nf">process</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// a, b 在此自动释放，不需要写任何 defer 或 free</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">arena { ... }</code> 是一个<strong>语句</strong>。</li>
  <li><code class="language-plaintext highlighter-rouge">new T { ... }</code> 在当前的最近外层 Arena 中分配一个 <code class="language-plaintext highlighter-rouge">T</code> 类型的对象，返回指向该对象的指针（<code class="language-plaintext highlighter-rouge">*T</code>）。</li>
  <li>Arena 块退出时，块内所有 <code class="language-plaintext highlighter-rouge">new</code> 分配的内存被一次性释放。</li>
  <li>Arena 块可以嵌套，内层 Arena 在内层退出时释放，不影响外层。</li>
</ul>

<h3 id="203-new-表达式">20.3 <code class="language-plaintext highlighter-rouge">new</code> 表达式</h3>

<p><code class="language-plaintext highlighter-rouge">new</code> 是一个<strong>一元前缀表达式</strong>，在当前最近的外层 Arena 中分配内存：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="c1">// 分配结构体</span>
<span class="n">var</span> <span class="n">p</span> <span class="o">=</span> <span class="n">new</span> <span class="n">Point</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="mf">2.0</span> <span class="p">};</span>

<span class="c1">// 分配数组</span>
<span class="n">var</span> <span class="n">arr</span> <span class="o">=</span> <span class="n">new</span> <span class="p">[</span><span class="nb">i32</span><span class="p">;</span> <span class="mi">10</span><span class="p">];</span>       <span class="c1">// 分配 10 个 i32 的数组，未初始化</span>
<span class="n">var</span> <span class="n">arr</span> <span class="o">=</span> <span class="n">new</span> <span class="p">[</span><span class="nb">i32</span><span class="p">;</span> <span class="mi">10</span><span class="p">]</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span> <span class="c1">// 分配并初始化全部为 0</span>

<span class="c1">// new 返回的是指针类型 *T</span>
<span class="c1">// 用 * 解引用访问</span>
<span class="o">*</span><span class="n">p</span><span class="py">.x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="204-arena-函数函数级-arena">20.4 Arena 函数（函数级 Arena）</h3>

<p>当函数体最外层用 <code class="language-plaintext highlighter-rouge">arena</code> 包裹时，整个函数内所有 <code class="language-plaintext highlighter-rouge">new</code> 分配在函数退出时统一释放：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">process</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">arena</span> <span class="p">{</span>
        <span class="n">var</span> <span class="n">temp</span> <span class="o">=</span> <span class="n">new</span> <span class="n">Temp</span> <span class="p">{</span> <span class="o">...</span> <span class="p">};</span>
        <span class="n">var</span> <span class="n">buf</span> <span class="o">=</span> <span class="n">new</span> <span class="n">Buffer</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
        <span class="k">use</span><span class="p">(</span><span class="n">temp</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="c1">// 函数返回时，temp 和 buf 自动释放</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="205-返回值跨越-arena-边界">20.5 返回值跨越 Arena 边界</h3>

<p>当需要从 Arena 中返回一个对象到外部时，使用 <code class="language-plaintext highlighter-rouge">return</code> 或 <code class="language-plaintext highlighter-rouge">yield</code>：</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">create</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">*</span><span class="n">MyStruct</span> <span class="p">{</span>
    <span class="n">arena</span> <span class="p">{</span>
        <span class="n">var</span> <span class="n">obj</span> <span class="o">=</span> <span class="n">new</span> <span class="n">MyStruct</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="mi">42</span> <span class="p">};</span>
        <span class="k">return</span> <span class="n">obj</span><span class="p">;</span>               <span class="c1">// obj 的所有权转移到调用者</span>
        <span class="c1">// arena 内其他分配仍自动释放</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>编译器规则：</p>
<ul>
  <li>通过 <code class="language-plaintext highlighter-rouge">return</code> 或 <code class="language-plaintext highlighter-rouge">yield</code> 从 Arena 中逃逸的对象，<strong>不被 Arena 释放</strong>。</li>
  <li>Arena 内其他分配（未逃逸的）照常在块退出时释放。</li>
  <li>逃逸的对象<strong>必须由调用者负责释放</strong>（或放入调用者的 Arena）。</li>
</ul>

<h3 id="206-arena-与现有-vox-特性的关系">20.6 Arena 与现有 Vox 特性的关系</h3>

<h4 id="与-defer独立且互补">与 <code class="language-plaintext highlighter-rouge">defer</code>：独立且互补</h4>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">arena</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">f</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="s">"test.txt"</span><span class="p">);</span>     <span class="c1">// 不是 new 分配的，用 defer</span>
    <span class="n">defer</span> <span class="nf">close</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>

    <span class="n">var</span> <span class="n">buf</span> <span class="o">=</span> <span class="n">new</span> <span class="p">[</span><span class="nb">u8</span><span class="p">;</span> <span class="mi">1024</span><span class="p">];</span>     <span class="c1">// arena 管理，不需要 defer</span>
    <span class="nf">read</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="与-mut完全兼容">与 <code class="language-plaintext highlighter-rouge">&amp;</code>/<code class="language-plaintext highlighter-rouge">&amp;mut</code>：完全兼容</h4>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="n">arena</span> <span class="p">{</span>
    <span class="n">var</span> <span class="n">obj</span> <span class="o">=</span> <span class="n">new</span> <span class="n">MyStruct</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="mi">1</span> <span class="p">};</span>
    <span class="n">var</span> <span class="n">r</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">MyStruct</span> <span class="o">=</span> <span class="o">&amp;*</span><span class="n">obj</span><span class="p">;</span>     <span class="c1">// 对 arena 中的对象创建引用</span>
    <span class="c1">// 引用在 arena 结束前有效</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="与裸指针自然对应">与裸指针：自然对应</h4>
<p><code class="language-plaintext highlighter-rouge">new</code> 返回 <code class="language-plaintext highlighter-rouge">*T</code>，直接对应 C 的指针，零开销。</p>

<h3 id="207-arena-的转译策略到-c">20.7 Arena 的转译策略（到 C）</h3>

<p>Arena 在转译到 C 时，使用线性分配器模式：</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="rouge-code"><pre><span class="c1">// Vox:</span>
<span class="c1">// arena {</span>
<span class="c1">//     var a = new MyStruct { x: 1, y: 2 };</span>
<span class="c1">//     var b = new MyStruct { x: 3, y: 4 };</span>
<span class="c1">//     process(a, b);</span>
<span class="c1">// }</span>

<span class="c1">// 生成的 C:</span>
<span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">__arena_begin</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="mi">4096</span><span class="p">);</span>
    <span class="kt">size_t</span> <span class="n">__arena_pos</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">struct</span> <span class="n">MyStruct</span><span class="o">*</span> <span class="n">a</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">MyStruct</span><span class="o">*</span><span class="p">)((</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">__arena_begin</span> <span class="o">+</span> <span class="n">__arena_pos</span><span class="p">);</span>
    <span class="n">__arena_pos</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">MyStruct</span><span class="p">);</span>
    <span class="n">a</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">a</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>

    <span class="k">struct</span> <span class="n">MyStruct</span><span class="o">*</span> <span class="n">b</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">MyStruct</span><span class="o">*</span><span class="p">)((</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">__arena_begin</span> <span class="o">+</span> <span class="n">__arena_pos</span><span class="p">);</span>
    <span class="n">__arena_pos</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">MyStruct</span><span class="p">);</span>
    <span class="n">b</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="n">b</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>

    <span class="n">process</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">);</span>

    <span class="n">free</span><span class="p">(</span><span class="n">__arena_begin</span><span class="p">);</span>  <span class="c1">// O(1) 一次性释放</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>优化</strong>：对于总分配大小较小的 Arena（例如 &lt; 4096 字节），编译器可以自动使用 <code class="language-plaintext highlighter-rouge">alloca</code> 在栈上分配，<strong>零堆分配、零释放</strong>：</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="p">{</span>
    <span class="c1">// 对于小 Arena，使用 alloca 在栈上分配</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">__arena_begin</span> <span class="o">=</span> <span class="n">alloca</span><span class="p">(</span><span class="mi">512</span><span class="p">);</span>
    <span class="kt">size_t</span> <span class="n">__arena_pos</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="c1">// ... 同上 ...</span>
    <span class="c1">// 不需要 free，函数返回时栈帧自动回收</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="208-与-gc-的关系">20.8 与 GC 的关系</h3>

<p>Arena 是 Vox <strong>首选的</strong>内存管理方案，覆盖了 90% 以上的场景：</p>

<table>
  <thead>
    <tr>
      <th>场景</th>
      <th style="text-align: center">Arena 能不能解决</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>临时对象（函数内）</td>
      <td style="text-align: center">✅ Arena 块完美解决</td>
    </tr>
    <tr>
      <td>请求级别的生命周期</td>
      <td style="text-align: center">✅ 函数级 Arena 解决</td>
    </tr>
    <tr>
      <td>对象池</td>
      <td style="text-align: center">✅ Arena 是天然的池</td>
    </tr>
    <tr>
      <td>复杂图结构（链表、树、图）</td>
      <td style="text-align: center">❌ 节点生命周期不同，需配合手动管理</td>
    </tr>
    <tr>
      <td>长期存活的对象</td>
      <td style="text-align: center">✅ 全局 Arena</td>
    </tr>
  </tbody>
</table>

<p>对于 Arena 无法优雅处理的场景（如交叉引用的图结构），未来版本可能引入可选的 GC，但 Arena 始终是 Vox 的内存管理基石。</p>

<h3 id="209-设计决策记录">20.9 设计决策记录</h3>

<ul>
  <li><strong>为何引入 Arena 而非 GC</strong>：Arena 的释放是 O(1) 的，零运行时开销，且与 Vox 的”显式”哲学一致。GC 会引入不可预测的暂停和隐式的释放行为。</li>
  <li><strong>为何用 <code class="language-plaintext highlighter-rouge">new</code> 关键字而非函数调用</strong>：<code class="language-plaintext highlighter-rouge">new</code> 在视觉上明确表示”这是堆分配”，与 C++ 的 <code class="language-plaintext highlighter-rouge">new</code> 语义对齐但更简单（不涉及构造函数）。</li>
  <li><strong>为何 <code class="language-plaintext highlighter-rouge">new</code> 返回指针而非引用</strong>：指针是最低开销的抽象，且与 C 的交互自然。需要安全访问时可以通过 <code class="language-plaintext highlighter-rouge">&amp;*ptr</code> 转换为引用。</li>
  <li><strong>为何不支持 <code class="language-plaintext highlighter-rouge">new</code> 在 Arena 块之外</strong>：这强制了 Arena 的显式性——如果你需要在 Arena 之外分配，说明你没有声明 Arena，编译器会报错。这是设计上的主动约束，防止忘记声明 Arena。</li>
  <li><strong>为何不自动使用栈分配</strong>：编译器会基于总大小自动选择栈（<code class="language-plaintext highlighter-rouge">alloca</code>）或堆（<code class="language-plaintext highlighter-rouge">malloc</code>），程序员不需要关心这一实现细节。</li>
  <li><strong>为何 <code class="language-plaintext highlighter-rouge">arena</code> 是一个语句而非表达式</strong>：Arena 管理的是副作用（内存分配与释放），作为语句更符合直觉，且与 Vox 的求值/语句语境划分一致。</li>
</ul>

<hr />

<h2 id="21-附录">21. 附录</h2>

<h3 id="211-运算符优先级">21.1 运算符优先级</h3>
<p>（从高到低）</p>

<table>
  <thead>
    <tr>
      <th>优先级</th>
      <th>运算符</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1（最高）</td>
      <td><code class="language-plaintext highlighter-rouge">()</code> 调用，<code class="language-plaintext highlighter-rouge">[]</code> 索引，<code class="language-plaintext highlighter-rouge">.</code> 字段访问</td>
    </tr>
    <tr>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">-</code>（一元负），<code class="language-plaintext highlighter-rouge">~</code>（按位取反），<code class="language-plaintext highlighter-rouge">*</code>（解引用），<code class="language-plaintext highlighter-rouge">move</code></td>
    </tr>
    <tr>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">*</code> <code class="language-plaintext highlighter-rouge">/</code> <code class="language-plaintext highlighter-rouge">%</code></td>
    </tr>
    <tr>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">+</code> <code class="language-plaintext highlighter-rouge">-</code></td>
    </tr>
    <tr>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">&lt;&lt;</code> <code class="language-plaintext highlighter-rouge">&gt;&gt;</code></td>
    </tr>
    <tr>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">&amp;</code> <code class="language-plaintext highlighter-rouge">\|</code> <code class="language-plaintext highlighter-rouge">^</code></td>
    </tr>
    <tr>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">&lt;</code> <code class="language-plaintext highlighter-rouge">&lt;=</code> <code class="language-plaintext highlighter-rouge">&gt;</code> <code class="language-plaintext highlighter-rouge">&gt;=</code></td>
    </tr>
    <tr>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">==</code> <code class="language-plaintext highlighter-rouge">!=</code></td>
    </tr>
    <tr>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">not</code>（关键字）</td>
    </tr>
    <tr>
      <td>10</td>
      <td><code class="language-plaintext highlighter-rouge">and</code>（关键字）</td>
    </tr>
    <tr>
      <td>11（最低）</td>
      <td><code class="language-plaintext highlighter-rouge">or</code>（关键字）</td>
    </tr>
  </tbody>
</table>

<p>赋值运算符 <code class="language-plaintext highlighter-rouge">=</code> 优先级低于 <code class="language-plaintext highlighter-rouge">or</code>，右结合。<br />
范围运算符 <code class="language-plaintext highlighter-rouge">..</code> 优先级低于赋值运算符。<br />
一元取引用运算符 <code class="language-plaintext highlighter-rouge">&amp;</code> / <code class="language-plaintext highlighter-rouge">&amp;mut</code> 优先级同解引用（优先级 2）。</p>

<h3 id="212-设计决策记录">21.2 设计决策记录</h3>
<ul>
  <li><strong>为何引入 <code class="language-plaintext highlighter-rouge">move</code> 关键字而不是隐式 move</strong>：保持”显式优于隐式”原则的一致性。<code class="language-plaintext highlighter-rouge">var b = a</code> 如果不含有 <code class="language-plaintext highlighter-rouge">move</code> 关键字，则不应改变 <code class="language-plaintext highlighter-rouge">a</code> 的有效性。隐式 move（Rust 风格）虽然简洁，但使所有权转移变得不可见，与 Vox 的核心设计哲学冲突。</li>
  <li><strong>为何没有 lambda</strong>：lambda 需要闭包捕获机制，会引入隐式堆分配和复杂的生命周期管理，与”显式、无魔法”原则冲突。参数过多时，推荐使用结构体封装。（注：引用和 move 引入后，lambda 的设计已有坚实基础，见第 19.4 节。）</li>
  <li><strong>为何没有函数重载</strong>：函数重载会增加符号查找的复杂性，且容易导致隐式行为。Vox 鼓励使用不同函数名或结构体参数。</li>
  <li><strong>为何文件即模块</strong>：参考 Zig 的设计哲学。文件路径自动成为命名空间，无需手动管理 <code class="language-plaintext highlighter-rouge">namespace</code> 块，一个实体只出现一次。</li>
  <li><strong>为何需要显式 <code class="language-plaintext highlighter-rouge">return</code></strong>：保证求值语境的明确性。每条执行路径的意图都清晰可见。</li>
  <li><strong>为何引入 <code class="language-plaintext highlighter-rouge">global</code> 关键字</strong>：强制程序员在函数内显式声明对模块级变量的访问，避免不小心遮蔽或无意修改全局状态。该关键字是纯编译期指令，零运行时开销。</li>
  <li><strong>为何禁止同作用域遮蔽</strong>：防止旧变量隐式”消失”，保持作用域内名字绑定的单一性和可预测性。</li>
  <li><strong>为何选择转译至 C 作为第一步</strong>：C 的语义与 Vox 高度匹配，且可直接利用成熟的 C 编译器获得原生性能与平台覆盖，是最快的原型验证路径。</li>
  <li><strong>为何不默认初始化变量</strong>：默认初始化为 0 是一个隐式行为，会带来隐性运行时开销。Vox 强制程序员显式初始化所有变量，或在使用前显式赋值。</li>
  <li><strong>为何 I/O 极简</strong>：<code class="language-plaintext highlighter-rouge">print</code> 不自动换行、不提供格式化占位符，所有行为由程序员显式控制。这符合”无魔法”原则。</li>
  <li><strong>为何使用 <code class="language-plaintext highlighter-rouge">&amp;T</code> / <code class="language-plaintext highlighter-rouge">&amp;mut T</code> 而非其他引用语法</strong>：Rust 风格的引用语法是最清晰的选择——<code class="language-plaintext highlighter-rouge">&amp;</code> 符号在视觉上即表示”借取地址”，与解引用 <code class="language-plaintext highlighter-rouge">*</code> 形成对称。<code class="language-plaintext highlighter-rouge">&amp;T</code> 与 <code class="language-plaintext highlighter-rouge">&amp;mut T</code> 的区别在类型层面显式表达，一眼可读。</li>
  <li><strong>为何引入所有权转移（move）</strong>：所有权转移是引用借用规则的逻辑补充。借用管理”临时访问”，move 管理”永久转让”。两者结合形成完整的资源管控体系，使 lambda 高级捕获（move 捕获）等场景的语义安全可表达。</li>
  <li><strong>为何引用必须遵守借用规则（原值冻结）</strong>：<code class="language-plaintext highlighter-rouge">&amp;mut T</code> 是对值的独占写入权——借出期间原值被冻结是引用的核心语义。”引用就真的是引用”，不是换了个名字的别名。参见第 10 节。</li>
  <li><strong>为何引入引用</strong>：引用是现代语言编程的基石。对 Vox 而言，引用是必须而非选择——它为生命周期管理提供了安全可控的基础，也使 lambda 等高级特性的加入成为可能。与裸指针（<code class="language-plaintext highlighter-rouge">*T</code> / <code class="language-plaintext highlighter-rouge">*mut T</code>）并列，让程序员在安全和自由之间自由选择。</li>
  <li><strong>为何引入 lambda</strong>：lambda 已成为现代语言的一等公民，在许多场景中具有不可替代的表达力。引用和 move 机制的引入使得 Vox 可以设计安全且多样的 lambda 捕获方式——捕获关系在类型层面清晰可见，生命周期由借用检查器保障。</li>
  <li>
    <h2 id="为何逻辑运算符选用-andornot-而非-消除单--是位与还是逻辑与的语义混淆python-方案将逻辑运算和位运算在语法层面彻底分离逻辑运算用关键字-and-or-not位运算用符号----程序员无需依赖优先级记忆代码扫读时语义一目了然此设计借鉴自-python也是显式优于隐式原则的自然延伸v050-起生效"><strong>为何逻辑运算符选用 <code class="language-plaintext highlighter-rouge">and</code>/<code class="language-plaintext highlighter-rouge">or</code>/<code class="language-plaintext highlighter-rouge">not</code> 而非 <code class="language-plaintext highlighter-rouge">&amp;&amp;</code>/<code class="language-plaintext highlighter-rouge">||</code>/<code class="language-plaintext highlighter-rouge">!</code></strong>：消除”单 &amp; 是位与还是逻辑与”的语义混淆。Python 方案将逻辑运算和位运算在语法层面彻底分离——逻辑运算用关键字 <code class="language-plaintext highlighter-rouge">and</code> <code class="language-plaintext highlighter-rouge">or</code> <code class="language-plaintext highlighter-rouge">not</code>，位运算用符号 <code class="language-plaintext highlighter-rouge">&amp;</code> <code class="language-plaintext highlighter-rouge">|</code> <code class="language-plaintext highlighter-rouge">^</code> <code class="language-plaintext highlighter-rouge">~</code>——程序员无需依赖优先级记忆，代码扫读时语义一目了然。此设计借鉴自 Python，也是”显式优于隐式”原则的自然延伸。v0.5.0 起生效。</h2>
  </li>
</ul>

<p><em>本手册随语言版本持续更新，当前版本为 v0.7.0。</em></p>]]></content><author><name>summerTBD</name></author><category term="技术" /><category term="编程语言" /><category term="Vox" /><category term="参考手册" /><summary type="html"><![CDATA[Vox 语言参考手册]]></summary></entry><entry><title type="html">倍增算法</title><link href="https://summertbd.github.io/posts/binary_lifiting/" rel="alternate" type="text/html" title="倍增算法" /><published>2026-05-30T00:00:00+08:00</published><updated>2026-05-30T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/binary_lifiting</id><content type="html" xml:base="https://summertbd.github.io/posts/binary_lifiting/"><![CDATA[<h2 id="为什么而诞生">为什么而诞生</h2>

<p>在一个单调数列中我们要去找第一个大于 target 的值（也可以是小于、小于等于、大于等于），我们往往会采用二分查找，它的时间复杂度很优秀，是 $O(\log n)$。但二分法有一个前提：我们必须知道数组的完整范围（即左右边界）。如果不知道数组究竟有多大，甚至数组是”无限”的（或我们只知道起点），二分法就无法直接使用。</p>

<p>此时有两种思路：</p>
<ul>
  <li><strong>线性枚举</strong>：从起点逐个往后找，复杂度 $O(n)$。</li>
  <li><strong>倍增法（指数搜索）</strong>：先通过不断倍增步长快速逼近目标所在区间，再在该区间内二分查找。总复杂度仍为 $O(\log n)$。</li>
</ul>

<p>本文讨论的”倍增”正是这样一种思想：<strong>通过指数级扩大步长来快速定位目标范围</strong>。它在竞赛题中一个经典的应用就是下面这道题——AcWing 109。</p>

<h2 id="什么是倍增算法个人的拙见">什么是倍增算法（个人的拙见）</h2>

<p>现在我们的情况是并不知道整个数组的大小。第一步肯定要找到 target 的大致区间。那怎么找？我们定义一个指数 $p$，从当前位置往后数 $2^p$ 个位置。根据往后数到达的位置与 target 的关系决定 $p$ 是 <code class="language-plaintext highlighter-rouge">p *= 2</code> 还是 <code class="language-plaintext highlighter-rouge">p /= 2</code>。</p>

<p>讲到这里如果你对数字敏感的话，可以看出这其实是一个”奇怪的二分法”。为什么这么说呢？当我们碰到第一个需要 <code class="language-plaintext highlighter-rouge">p /= 2</code> 的时候，你会发现，我们其实在 <code class="language-plaintext highlighter-rouge">cur_pos</code> 和 <code class="language-plaintext highlighter-rouge">cur_pos + (1 &lt;&lt; p)</code> 这个区间做二分查找。时间复杂度也是 $O(\log n)$，不过它其实比真正的二分慢，因为严格说它采用了 $2\log n$ 步，二分则是严格的 $\log n$。</p>

<h2 id="例题">例题</h2>

<p><a href="https://www.acwing.com/problem/content/111/">AcWing 109 - Genius ACM</a></p>

<p>给定一个整数 $M$，对于任意一个整数集合 $S$，定义”校验值”如下：</p>

<p>从集合 $S$ 中取出 $M$ 对数（即 $2 \times M$ 个数，不能重复使用集合中的数，如果 $S$ 中的整数不够 $M$ 对，则取到不能取为止），使得”每对数的差的平方”之和最大，这个最大值就称为集合 $S$ 的”校验值”。</p>

<p>现在给定一个长度为 $N$ 的数列 $A$ 以及一个整数 $T$。我们要把 $A$ 分成若干段，使得每一段的”校验值”都不超过 $T$。求最少需要分成几段。</p>

<p><strong>输入格式</strong></p>

<p>第一行输入整数 $K$，代表有 $K$ 组测试数据。
对于每组测试数据，第一行包含三个整数 $N, M, T$。
第二行包含 $N$ 个整数，表示数列 $A_1, A_2 \dots A_N$。</p>

<p><strong>输出格式</strong></p>

<p>对于每组测试数据，输出其答案，每个答案占一行。</p>

<p><strong>数据范围</strong></p>

\[1 \le K \le 12,\quad
1 \le N, M \le 500000,\quad
0 \le T \le 10^{18},\quad
0 \le A_i \le 2^{20}\]

<p><strong>输入样例：</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>2
5 1 49
8 2 1 7 9
5 1 64
8 2 1 7 9
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>输出样例：</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>2
1
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="思路">思路</h2>

<p>如果我们已知整个数组只需要分成两段（即只有一个断点），那么我们可以直接二分这个断点的位置。但问题在于我们不知道有多少个断点，也无法直接确定每个区间应该从哪里切分——这正是倍增派上用场的地方。</p>

<p>具体来说，我们从左到右处理数组，每次用<strong>倍增法</strong>确定当前段的最长合法右边界：</p>

<ul>
  <li>从左端点 $L$ 出发，尝试扩展长度 $len$（初始为 1）。</li>
  <li>如果 $[L, R + len)$ 的校验值 $\le T$，则接受扩展：$R \gets R + len$，并将 $len$ 翻倍。</li>
  <li>否则将 $len$ 减半，继续尝试更小的步长。</li>
  <li>当 $len$ 变为 0 时，$R$ 即为当前段的最长合法右边界。</li>
</ul>

<p>这样做的时间复杂度为 $O(\log n)$ 次校验，而不是线性枚举的 $O(n)$ 次。</p>

<p>不过想必你一定还有一个疑问：如果按校验值的算法，每次都要给一个区间排序再算校验值，这个复杂度真的过得了吗？</p>

<p>答案是一般过不了。原因在于：对于 $[L, \text{mid})$ 我们要加入 $[\text{mid}, \text{right})$，如果每次都对整个 $[L, \text{right})$ 重新排序，复杂度会很高。但注意到这是一个<strong>插入有序区间</strong>的问题：$[L, \text{mid})$ 已经有序，我们只需要将 $[\text{mid}, \text{right})$ 排序后与它合并即可（归并），这样每次只需 $O(\text{len} \cdot \log \text{len})$ 的排序开销，而非对整个新区间重新排序。</p>

<p>因此我们需要两个辅助数组：</p>
<ul>
  <li>$t$：存放当前 $[L, R)$ 的排序结果；</li>
  <li>$\text{tmp}$：存放合并 $[L, \text{mid})$（来自 $t$）和 $[\text{mid}, \text{right})$（从原数组 $a$ 拷贝并排序）后的结果。</li>
</ul>

<p>如果校验成功，则将 $\text{tmp}$ 写回 $t$；如果失败也没关系，虽然 $[\text{mid}, \text{right})$ 在 $t$ 中排了一次序，但后续的扩展会覆盖它，不会产生副作用。</p>

<h2 id="代码">代码</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
</pre></td><td class="rouge-code"><pre><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="k">typedef</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">ll</span><span class="p">;</span>

<span class="k">const</span> <span class="kt">int</span> <span class="n">N</span> <span class="o">=</span> <span class="mi">500005</span><span class="p">;</span>

<span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="n">m</span><span class="p">;</span>
<span class="n">ll</span> <span class="n">T</span><span class="p">;</span>
<span class="n">ll</span> <span class="n">a</span><span class="p">[</span><span class="n">N</span><span class="p">],</span> <span class="n">t</span><span class="p">[</span><span class="n">N</span><span class="p">],</span> <span class="n">tmp</span><span class="p">[</span><span class="n">N</span><span class="p">];</span>

<span class="n">ll</span> <span class="nf">sq</span><span class="p">(</span><span class="n">ll</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// [l, mid), [mid, r)</span>
<span class="kt">bool</span> <span class="nf">check</span><span class="p">(</span><span class="kt">int</span> <span class="n">l</span><span class="p">,</span> <span class="kt">int</span> <span class="n">mid</span><span class="p">,</span> <span class="kt">int</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">mid</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">r</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">t</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>
    <span class="n">sort</span><span class="p">(</span><span class="n">t</span> <span class="o">+</span> <span class="n">mid</span><span class="p">,</span> <span class="n">t</span> <span class="o">+</span> <span class="n">r</span><span class="p">);</span>

    <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">l</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="n">mid</span><span class="p">,</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="n">mid</span> <span class="o">&amp;&amp;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">t</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="p">{</span>
            <span class="n">tmp</span><span class="p">[</span><span class="n">k</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t</span><span class="p">[</span><span class="n">i</span><span class="o">++</span><span class="p">];</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">tmp</span><span class="p">[</span><span class="n">k</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t</span><span class="p">[</span><span class="n">j</span><span class="o">++</span><span class="p">];</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="n">mid</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">tmp</span><span class="p">[</span><span class="n">k</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t</span><span class="p">[</span><span class="n">i</span><span class="o">++</span><span class="p">];</span>
    <span class="p">}</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">j</span> <span class="o">&lt;</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">tmp</span><span class="p">[</span><span class="n">k</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t</span><span class="p">[</span><span class="n">j</span><span class="o">++</span><span class="p">];</span>
    <span class="p">}</span>

    <span class="n">ll</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">L</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">R</span> <span class="o">=</span> <span class="n">k</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">p</span> <span class="o">&lt;</span> <span class="n">m</span> <span class="o">&amp;&amp;</span> <span class="n">L</span> <span class="o">&lt;</span> <span class="n">R</span><span class="p">;</span> <span class="o">++</span><span class="n">p</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">sum</span> <span class="o">+=</span> <span class="n">sq</span><span class="p">(</span><span class="n">tmp</span><span class="p">[</span><span class="n">R</span><span class="p">]</span> <span class="o">-</span> <span class="n">tmp</span><span class="p">[</span><span class="n">L</span><span class="p">]);</span>
        <span class="n">R</span><span class="o">--</span><span class="p">;</span>
        <span class="n">L</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">sum</span> <span class="o">&lt;=</span> <span class="n">T</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">solve</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">n</span> <span class="o">&gt;&gt;</span> <span class="n">m</span> <span class="o">&gt;&gt;</span> <span class="n">T</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">L</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="kt">int</span> <span class="n">ans</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">L</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">R</span> <span class="o">=</span> <span class="n">L</span><span class="p">;</span>
        <span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">t</span><span class="p">[</span><span class="n">L</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">L</span><span class="p">];</span>
        <span class="k">while</span> <span class="p">(</span><span class="n">len</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">R</span> <span class="o">+</span> <span class="n">len</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">check</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">R</span> <span class="o">+</span> <span class="n">len</span><span class="p">))</span> <span class="p">{</span>
                <span class="n">R</span> <span class="o">+=</span> <span class="n">len</span><span class="p">;</span>
                <span class="n">len</span> <span class="o">&lt;&lt;=</span> <span class="mi">1</span><span class="p">;</span>

                <span class="c1">// 把t给排好序，在check函数中，合并了t的两部分，这部分是</span>
                <span class="c1">// 变成了tmp，但t本身没有被更新，所以这里需要把排好序的t更新回去</span>
                <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">L</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">R</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">t</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">tmp</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="n">L</span><span class="p">];</span>
                <span class="p">}</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">R</span> <span class="o">&gt;=</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
                    <span class="k">break</span><span class="p">;</span>
                <span class="p">}</span>

            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">len</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="n">ans</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">L</span> <span class="o">=</span> <span class="n">R</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">ans</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">ios</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">K</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">K</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">K</span><span class="o">--</span><span class="p">)</span>
        <span class="n">solve</span><span class="p">();</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>]]></content><author><name>summerTBD</name></author><category term="数学" /><category term="算法" /><category term="算法" /><summary type="html"><![CDATA[为什么而诞生]]></summary></entry><entry><title type="html">Cost_of_a_Bracket_Sequence</title><link href="https://summertbd.github.io/posts/Cost_of_a_Bracket_Sequence/" rel="alternate" type="text/html" title="Cost_of_a_Bracket_Sequence" /><published>2026-05-30T00:00:00+08:00</published><updated>2026-05-30T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/Cost_of_a_Bracket_Sequence</id><content type="html" xml:base="https://summertbd.github.io/posts/Cost_of_a_Bracket_Sequence/"><![CDATA[<h2 id="题目链接">题目链接</h2>
<p>https://codeforces.com/problemset/problem/2233/C</p>

<h2 id="问题重述">问题重述</h2>

<p>给定一个长度为 $n$ 的括号序列 $s$（由 <code class="language-plaintext highlighter-rouge">(</code> 和 <code class="language-plaintext highlighter-rouge">)</code> 组成），以及一个整数 $k$。Alice 可以删除至多 $k$ 个括号，她的目标是最小化<strong>剩余序列的最长合法括号子序列（LBS）的长度</strong>。换句话说，Alice 希望无论如何操作，对手都无法在剩下的序列中找到太长的合法配对。</p>

<p>我们的任务是：判断 Alice 是否存在一种删除方案，使得剩余序列的最长合法括号子序列的长度<strong>不超过某个下界</strong>（或者等价地，求出她能做到的最小代价）。</p>

<h2 id="核心观察">核心观察</h2>

<p>合法括号序列有一个基本的结构性质：任何合法的括号配对，左括号一定在对应的右括号之前。因此，如果我们想”破坏”尽可能多的配对，一个自然的想法是：</p>

<ul>
  <li>删除序列中<strong>最左边的一些 <code class="language-plaintext highlighter-rouge">(</code></strong>，因为这些左括号最容易找到与之匹配的右括号；</li>
  <li>删除序列中<strong>最右边的一些 <code class="language-plaintext highlighter-rouge">)</code></strong>，因为这些右括号最容易找到与之匹配的左括号。</li>
</ul>

<p>为什么这样做是最优的？考虑一对已经匹配的括号 $(i, j)$（$i &lt; j$），其中 $s_i = \texttt{(}$，$s_j = \texttt{)}$。由于 $i$ 和 $j$ 在配对意义下是轮换对称的，我们只需关注其中一侧。假设我们删除了某个左括号 $s_i$，对手试图找一个 $i’ \neq i$ 来代替 $s_i$ 与 $s_j$ 配对。如果我们删除的是<strong>最左边的左括号</strong>，那么在它之前没有任何 <code class="language-plaintext highlighter-rouge">(</code> 可以替代它——因此对手必然丢失这个配对，代价<strong>确定性地</strong>减少 1。同理，删除<strong>最右边的右括号</strong>也能保证同样的效果：在它之后没有任何 <code class="language-plaintext highlighter-rouge">)</code> 可以替代它。</p>

<p>反过来，如果我们删除的是中间的某个括号，对手<strong>有可能</strong>在它的左侧找到另一个 <code class="language-plaintext highlighter-rouge">(</code>（或在右侧找到另一个 <code class="language-plaintext highlighter-rouge">)</code>）来顶替，从而维持原有的配对数量。当然，这并不总是成立——在某些序列中删除中间括号确实也会减少代价（例如 <code class="language-plaintext highlighter-rouge">((()))</code> 中删除中间括号依然会破坏配对）。但问题在于：这种操作的收益<strong>依赖于序列的具体结构</strong>，我们无法保证每一次删除都能稳定地减少代价。相比之下，删除最左 <code class="language-plaintext highlighter-rouge">(</code> 或最右 <code class="language-plaintext highlighter-rouge">)</code> 是<strong>无条件</strong>保证代价减 1 的操作——无论序列长什么样，这个结论都成立。因此，最优策略必然选择后者。</p>

<p><strong>结论</strong>：Alice 的最优策略一定是删除序列最左边的 $x$ 个 <code class="language-plaintext highlighter-rouge">(</code> 和最右边的 $y$ 个 <code class="language-plaintext highlighter-rouge">)</code>，其中 $x + y \le k$。</p>

<h2 id="形式化分析">形式化分析</h2>

<p>设 $x$ 为删除的最左 <code class="language-plaintext highlighter-rouge">(</code> 数量，$y$ 为删除的最右 <code class="language-plaintext highlighter-rouge">)</code> 数量，满足 $0 \le x + y \le k$。对每一组 $(x, y)$，我们可以模拟删除后剩余序列的最长合法括号子序列长度：</p>

<ol>
  <li>从左到右扫描 $s$，跳过前 $x$ 个 <code class="language-plaintext highlighter-rouge">(</code>（即不将它们加入剩余序列）；</li>
  <li>从右到左扫描，跳过（标记删除）后 $y$ 个 <code class="language-plaintext highlighter-rouge">)</code>；</li>
  <li>在保留下来的字符中，用经典的栈/贪心方法计算最长合法括号子序列长度：维护一个计数器 <code class="language-plaintext highlighter-rouge">open</code>，遇到 <code class="language-plaintext highlighter-rouge">(</code> 则 <code class="language-plaintext highlighter-rouge">open += 1</code>，遇到 <code class="language-plaintext highlighter-rouge">)</code> 且 <code class="language-plaintext highlighter-rouge">open &gt; 0</code> 则 <code class="language-plaintext highlighter-rouge">match += 1, open -= 1</code>。</li>
</ol>

<p>我们枚举所有合法的 $(x, y)$ 组合，取使得匹配数最小的那一组，即可得到 Alice 的最优代价。</p>

<h2 id="复杂度分析">复杂度分析</h2>

<p>设 $L$ 为 <code class="language-plaintext highlighter-rouge">(</code> 的总数，$R$ 为 <code class="language-plaintext highlighter-rouge">)</code> 的总数。$x$ 的取值范围是 $0 \sim \min(k, L)$，对于每个 $x$，$y$ 取 $\min(k - x, R)$。因此枚举量是 $O(k)$。每次模拟计算匹配数的复杂度是 $O(n)$。总复杂度 $O(kn)$，在题目给定的数据范围内可以通过。</p>

<h2 id="代码">代码</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
</pre></td><td class="rouge-code"><pre><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="k">const</span> <span class="kt">int</span> <span class="n">INF</span> <span class="o">=</span> <span class="mh">0x3f3f3f3f</span><span class="p">;</span>

<span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="n">k</span><span class="p">;</span>
<span class="n">string</span> <span class="n">s</span><span class="p">;</span>

<span class="c1">// 删除最左 x 个 '(' 和最右 y 个 ')' 后，计算剩余序列的最长合法括号子序列长度</span>
<span class="kt">int</span> <span class="nf">calculate_match</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">char</span><span class="o">&gt;</span> <span class="n">kept</span><span class="p">;</span>

    <span class="kt">int</span> <span class="n">left_deleted</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">c</span> <span class="o">:</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">'('</span> <span class="o">&amp;&amp;</span> <span class="n">left_deleted</span> <span class="o">&lt;</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">left_deleted</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">kept</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">right_deleted</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">kept</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">right_deleted</span> <span class="o">&lt;</span> <span class="n">y</span><span class="p">;</span> <span class="o">--</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">kept</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">')'</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">kept</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>       <span class="c1">// 标记删除</span>
            <span class="n">right_deleted</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">open</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">match</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">c</span> <span class="o">:</span> <span class="n">kept</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">'\0'</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">'('</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">open</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">open</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">match</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="n">open</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">match</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// 构造具体的删除方案（1 表示删除，0 表示保留）</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">generate_result</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">res</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="kt">int</span> <span class="n">left_deleted</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">left_deleted</span> <span class="o">&lt;</span> <span class="n">x</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'('</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">res</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="n">left_deleted</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">right_deleted</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">right_deleted</span> <span class="o">&lt;</span> <span class="n">y</span><span class="p">;</span> <span class="o">--</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">')'</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">res</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="n">right_deleted</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">solve</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">n</span> <span class="o">&gt;&gt;</span> <span class="n">k</span> <span class="o">&gt;&gt;</span> <span class="n">s</span><span class="p">;</span>

    <span class="kt">int</span> <span class="n">total_left</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">total_right</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">char</span> <span class="n">c</span> <span class="o">:</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">'('</span><span class="p">)</span> <span class="n">total_left</span><span class="o">++</span><span class="p">;</span>
        <span class="k">else</span>          <span class="n">total_right</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">min_match</span> <span class="o">=</span> <span class="n">INF</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">best_x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">best_y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="kt">int</span> <span class="n">max_x</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">total_left</span><span class="p">);</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;=</span> <span class="n">max_x</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">k</span> <span class="o">-</span> <span class="n">x</span><span class="p">,</span> <span class="n">total_right</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">y</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>

        <span class="kt">int</span> <span class="n">match</span> <span class="o">=</span> <span class="n">calculate_match</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="o">&lt;</span> <span class="n">min_match</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">best_x</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
            <span class="n">best_y</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span>
            <span class="n">min_match</span> <span class="o">=</span> <span class="n">match</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">auto</span> <span class="n">result</span> <span class="o">=</span> <span class="n">generate_result</span><span class="p">(</span><span class="n">best_x</span><span class="p">,</span> <span class="n">best_y</span><span class="p">);</span>
    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">x</span> <span class="o">:</span> <span class="n">result</span><span class="p">)</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">x</span><span class="p">;</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">ios</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">t</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">t</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">t</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">solve</span><span class="p">();</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="小结">小结</h2>

<p>本题的关键在于认识到：为了最大化破坏配对的效果，Alice 应当优先删除两端的括号——最左边的 <code class="language-plaintext highlighter-rouge">(</code> 和最右边的 <code class="language-plaintext highlighter-rouge">)</code>。这个结论可以通过”被删除括号的可替代性”来论证：只有最左的左括号和最右的右括号是<strong>不可替代</strong>的，删除它们才能保证配对数量一定减少。在此基础上，枚举删除数量即可得到最优解。</p>]]></content><author><name>summerTBD</name></author><category term="数学" /><category term="算法" /><category term="算法" /><summary type="html"><![CDATA[题目链接 https://codeforces.com/problemset/problem/2233/C]]></summary></entry><entry><title type="html">《因为它们长得不一样——a·k+b 哈希唯一性的优雅证明》</title><link href="https://summertbd.github.io/posts/elegant-proof-ak-b-hash-uniqueness/" rel="alternate" type="text/html" title="《因为它们长得不一样——a·k+b 哈希唯一性的优雅证明》" /><published>2026-05-29T00:00:00+08:00</published><updated>2026-05-29T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/elegant-proof-ak-b-hash-uniqueness</id><content type="html" xml:base="https://summertbd.github.io/posts/elegant-proof-ak-b-hash-uniqueness/"><![CDATA[<h2 id="问题简述">问题简述</h2>

<p>设 \(k\) 是一个固定的正整数。对于任意整数 \(a\) 和满足 \(0 \le b &lt; k\) 的整数 \(b\)，定义映射</p>

\[f(a,b) = a \cdot k + b,\]

<p>证明 \(f\) 是<strong>单射</strong>（即不同的有序对 \((a,b)\) 对应不同的函数值）。</p>

<hr />

<h2 id="反证法标准做法">反证法（标准做法）</h2>

<p>这是最经典的证明思路。</p>

<p>假设存在两对不同的 \((a_1, b_1)\) 和 \((a_2, b_2)\) 满足：</p>

\[a_1 k + b_1 = a_2 k + b_2,
\quad
0 \le b_1, b_2 &lt; k.\]

<p>移项得：</p>

\[(a_1 - a_2) k = b_2 - b_1.\]

<p>考虑右边 \(b_2 - b_1\) 的绝对值：由于 \(0 \le b_1, b_2 &lt; k\)，必有</p>

\[|b_2 - b_1| &lt; k.\]

<table>
  <tbody>
    <tr>
      <td>左边 \((a_1 - a_2)k\) 要么为 \(0\)，要么绝对值至少为 \(k\)（因为 \(a_1 - a_2\) 是非零整数时，$$</td>
      <td>(a_1 - a_2)k</td>
      <td>\ge k$$）。</td>
    </tr>
  </tbody>
</table>

<p>因此等式两边必须同时为 \(0\)，即 \(a_1 - a_2 = 0\) 且 \(b_2 - b_1 = 0\)。故 \(a_1 = a_2,\; b_1 = b_2\)，与假设矛盾。得证。</p>

<p><strong>结论</strong>：在条件 \(0 \le b &lt; k\) 下（即 \(k\) 严格大于 \(b\) 的上界），映射 \((a,b) \mapsto a \cdot k + b\) 是单射。换言之，\(f\) 将不同的有序对 \((a,b)\) 映射到不同的整数值。</p>

<hr />

<h2 id="另一种思路几何法">另一种思路：几何法</h2>

<p>说实话，我第一次看到这个问题时，并没有想到反证法，而是想到了另一种做法——几何法。这受到了<strong>康托尔配对函数（Cantor pairing function）</strong> 的启发。</p>

<p>康托尔配对函数是一种将自然数对 \((x,y)\) 映射为单个自然数的方法，它通过将平面上的点按某种顺序”走遍”来构造。我们不妨也这样想：</p>

<p>把 \((a,b)\) 看成是一个<strong>矩形网格</strong>中的位置——\(a\) 是行号，\(b\) 是列号，而 \(k\) 是每一行的列数，且 \(0 \le b &lt; k\) 保证了 \(b\) 始终落在有效的列范围内。</p>

<p>在这样一个网格中，将 \((a,b)\) 映射为”从第 0 行第 0 列开始，按行优先数到的序号”：</p>

\[\text{index} = a \cdot k + b.\]

<p>现在来证明这个映射是单射。假设 \((a_1, b_1)\) 和 \((a_2, b_2)\) 映射到同一个序号 \(N\)，即</p>

\[a_1 k + b_1 = N = a_2 k + b_2,
\quad 0 \le b_1, b_2 &lt; k.\]

<p>将 \(N\) 除以 \(k\)，由<strong>带余除法（Euclidean division）</strong> 的定理：对于任意整数 \(N\) 和正整数 \(k\)，存在唯一的整数商 \(q\) 和余数 \(r\)，满足</p>

\[N = q \cdot k + r, \quad 0 \le r &lt; k.\]

<p>由于 \(a_1 k + b_1\) 和 \(a_2 k + b_2\) 都满足这个形式，且 \(0 \le b_1, b_2 &lt; k\)，由商和余数的唯一性即得 \(a_1 = a_2\) 且 \(b_1 = b_2\)。故映射为单射。</p>

<p><strong>注</strong>：这个论证本质上与反证法相同，但几何视角让”行号/列号”与”商/余数”之间的对应关系变得更加直观。</p>

<p>此外还需要注意一个细节：若 \(k = b_{\max}\)，则 \(b = b_{\max}\) 时已不满足 \(0 \le b &lt; k\) 的条件，此时映射不再是单射。例如 \((0, k)\) 和 \((1, 0)\) 就冲突：</p>

\[0 \cdot k + k = k, \quad 1 \cdot k + 0 = k.\]

<p>这与网格中”每一行只有 \(k\) 列（编号 \(0\) 到 \(k-1\)）”的直观一致——\(k\) 已经是越界的列号了。</p>

<hr />

<h2 id="再深究一步从几何到进制">再深究一步：从几何到进制</h2>

<p>当我们用几何法思考时，你是否注意到一个现象：\(b\) 必须有一个最大值 \(b_{\max}\)。遍历到一行的末尾时，再往后就会”进位”到下一行。</p>

<p>这像什么？<strong>像进制数</strong>。</p>

<p>在十进制中，个位满了 10 就向十位进 1。类似地，在这里，\(b\) 满了 \(k\) 就向 \(a\) 进 1。</p>

<p>如果我们把 \((a,b)\) 看成一个 <strong>k 进制数</strong>，其中 \(a\) 是高位，\(b\) 是低位，那么：</p>

\[a \cdot k + b\]

<p>就是它在十进制下的值。</p>

<p>于是，我们有了第三种视角——进制视角。下面给出 <strong>k 进制表示唯一性</strong>的一般证明。</p>

<h3 id="进制表示唯一性的证明">进制表示唯一性的证明</h3>

<p><strong>定理</strong>：设 \(k \ge 2\) 为正整数。任意非负整数 \(N\) 可以唯一地表示为</p>

\[N = a_m k^m + a_{m-1} k^{m-1} + \cdots + a_1 k + a_0,\]

<p>其中 \(m \ge 0\)，\(0 \le a_i &lt; k\) 且 \(a_m \neq 0\)（当 \(N = 0\) 时，取 \(m = 0\)、\(a_0 = 0\)）。</p>

<p><strong>证明（存在性）</strong>：反复使用带余除法。
令 \(N_0 = N\)。对 \(i = 0, 1, 2, \dots\) 依次执行</p>

\[N_i = q_i \cdot k + r_i, \quad 0 \le r_i &lt; k,\]

<p>并设 \(a_i = r_i\)、\(N_{i+1} = q_i\)。由于 \(N_{i+1} &lt; N_i\)（当 \(N_i &gt; 0\) 时），这个过程必在有限步后终止于 \(N_{m+1} = 0\)。此时</p>

\[N = a_m k^m + a_{m-1} k^{m-1} + \cdots + a_0.\]

<p><strong>证明（唯一性）</strong>：假设有两种表示</p>

\[N = \sum_{i=0}^m a_i k^i = \sum_{i=0}^n b_i k^i,\]

<p>其中 \(0 \le a_i, b_i &lt; k\)。不妨设 \(m \le n\)，并对不足的高位补零（即 \(i &gt; m\) 时视 \(a_i = 0\)）。</p>

<p>考虑模 \(k\) 的同余式：</p>

\[\sum_{i=0}^n a_i k^i \equiv \sum_{i=0}^n b_i k^i \pmod{k}.\]

<p>由于 \(k^i \equiv 0 \pmod{k}\) 对 \(i \ge 1\) 成立，上式化为 \(a_0 \equiv b_0 \pmod{k}\)。而 \(0 \le a_0, b_0 &lt; k\)，故 \(a_0 = b_0\)。</p>

<p>两边同时减去 \(a_0 = b_0\) 后除以 \(k\)：</p>

\[\sum_{i=0}^{n-1} a_{i+1} k^{i} = \sum_{i=0}^{n-1} b_{i+1} k^{i}.\]

<p>再次模 \(k\) 得 \(a_1 = b_1\)。以此类推，逐位可得 \(a_i = b_i\) 对 \(i = 0, 1, \dots, n\) 均成立。故表示唯一。∎</p>

<p><strong>回到原问题</strong>：当我们将 \((a,b)\) 视为一个两位的 k 进制数时，上面的唯一性定理直接保证了映射 \(f(a,b) = a \cdot k + b\) 是单射。这正是进制视角的核心力量——它不仅解释了 \(a \cdot k + b\) 为什么是单射，还揭示了其本质：<strong>哈希值的运算（乘 k 再加 b）恰恰就是在做进制数的拼装</strong>。</p>

<hr />

<h2 id="进制数法多项式视角">进制数法（多项式视角）</h2>

<p>将 \((a,b)\) 看作一个 <strong>k 进制数</strong>的两位数字（\(a\) 为高位，\(b\) 为低位），那么</p>

\[a \cdot k + b\]

<p>就是它在十进制下的数值。这里的核心是<strong>进制表示的唯一性</strong>：在 k 进制中，每一位数字的取值范围是 \(0\) 到 \(k-1\)，不同的数码组合必然对应不同的数值。</p>

<p>换一个视角：表达式 \(a \cdot k + b\) 在形式上与一次多项式 \(p(x) = a x + b\) 相同。但需要特别注意的是，这里 \(k\) 是一个<strong>确定的数值</strong>而非未定的变元，因此不能直接套用”多项式恒等定理”（该定理要求等式对所有变元取值成立，而不仅在 \(x = k\) 这一个点成立）。然而，如果限制系数的取值范围，则数值等式可以推出系数相等——这正是反证法所证明的结论。换言之：</p>

<blockquote>
  <p>若 \(0 \le b_1, b_2 &lt; k\) 且 \(a_1 k + b_1 = a_2 k + b_2\)，则 \(a_1 = a_2\) 且 \(b_1 = b_2\)。</p>
</blockquote>

<p>这个结论并不依赖于多项式理论，而是依赖于 \(b\) 的取值范围约束。将表达式类比为多项式，只是为了帮助我们直观理解”系数唯一性”——但这需要以反证法或带余除法作为严格的逻辑基础。</p>

<table>
  <tbody>
    <tr>
      <td><strong>与反证法的联系</strong>：在反证法的推导中，$$</td>
      <td>(a_1-a_2)k</td>
      <td>&lt; k \Rightarrow a_1 = a_2\(这一步骤的实质是：k 进制表示中，高位不同会导致数值差距至少为\)k$$，而低位差不足以弥补这一差距。</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="扩展更高维的情况">扩展：更高维的情况</h2>

<p>以上思路可以自然推广到更多参数。考虑三元组 \((a,b,c)\)，其中 \(0 \le b &lt; k,\; 0 \le c &lt; k\)，映射为</p>

\[g(a,b,c) = a \cdot k^2 + b \cdot k + c.\]

<p>这可以看作是一个 k 进制三位数。下面用<strong>反证法</strong>证明其单射性。</p>

<p>假设存在两个不同的三元组 \((a_1,b_1,c_1)\) 和 \((a_2,b_2,c_2)\)（均满足 \(0 \le b_i, c_i &lt; k\)）使得</p>

\[a_1 k^2 + b_1 k + c_1 = a_2 k^2 + b_2 k + c_2.\]

<p>移项得</p>

\[(a_1 - a_2)k^2 + (b_1 - b_2)k = c_2 - c_1.\]

<p>整理为</p>

\[\big((a_1 - a_2)k + (b_1 - b_2)\big)k = c_2 - c_1.\]

<table>
  <tbody>
    <tr>
      <td>由于 \(0 \le c_1, c_2 &lt; k\)，有 $$</td>
      <td>c_2 - c_1</td>
      <td>&lt; k\(。而左边括号内是整数，记\)m = (a_1 - a_2)k + (b_1 - b_2)\(，则等式为\)m k = c_2 - c_1\(。因此\)</td>
      <td>m</td>
      <td>k &lt; k\(，故\)</td>
      <td>m</td>
      <td>&lt; 1\(，即\)m = 0$$。</td>
    </tr>
  </tbody>
</table>

<p>于是 \(c_2 - c_1 = 0\)，且</p>

\[(a_1 - a_2)k + (b_1 - b_2) = 0.\]

<p>这就回到了我们已经解决的二元情形。由反证法结论得 \(a_1 = a_2\) 且 \(b_1 = b_2\)。结合 \(c_1 = c_2\)，故 \((a_1,b_1,c_1) = (a_2,b_2,c_2)\)，与假设矛盾。得证。</p>

<p>更一般地，对于 \(n\) 元组 \((a_1, a_2, \dots, a_n)\)（满足 \(0 \le a_i &lt; k\)，其中 \(i \ge 2\)，而 \(a_1\) 可为任意整数），映射</p>

\[h(a_1, a_2, \dots, a_n) = a_1 \cdot k^{n-1} + a_2 \cdot k^{n-2} + \cdots + a_n\]

<p>是单射。可以用<strong>数学归纳法</strong>严格证明：当 \(n = 1\) 时结论平凡。假设对 \(n-1\) 元组成立，对 \(n\) 元组的情形，利用等式</p>

\[(a_1 k^{n-1} + \cdots + a_{n-1} k + a_n) = 
\big((a_1 k^{n-2} + \cdots + a_{n-1})\big) \cdot k + a_n\]

<p>将其化归为 \((m, a_n)\) 的二元情形（其中 \(m = a_1 k^{n-2} + \cdots + a_{n-1}\)），再结合归纳假设即可。这从另一个角度印证了 <strong>k 进制表示的唯一性</strong>。</p>

<hr />

<h2 id="总结">总结</h2>

<p>本文从一个简单的哈希映射问题出发，给出了三种不同视角的证明：</p>

<table>
  <thead>
    <tr>
      <th>方法</th>
      <th>核心思想</th>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>反证法</strong></td>
      <td>利用 $$</td>
      <td>b_2-b_1</td>
      <td>&lt; k$$ 迫使系数相等，是最直接的证明</td>
    </tr>
    <tr>
      <td><strong>几何法</strong></td>
      <td>按行优先排列成矩形，通过带余除法的唯一性论证</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td><strong>进制数法</strong></td>
      <td>将 \((a,b)\) 视为 k 进制数，利用进制表示的唯一性</td>
      <td> </td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>殊途同归——它们最终都指向同一个结论：只要 \(k\) 严格大于 \(b\) 的上界，\(a \cdot k + b\) 就是 \((a,b)\) 的一个完美哈希。</p>]]></content><author><name>summerTBD</name></author><category term="数学" /><category term="算法" /><category term="多项式" /><category term="哈希" /><summary type="html"><![CDATA[问题简述]]></summary></entry><entry><title type="html">少年心气</title><link href="https://summertbd.github.io/posts/my_mod/" rel="alternate" type="text/html" title="少年心气" /><published>2026-05-25T00:00:00+08:00</published><updated>2026-05-25T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/my_mod</id><content type="html" xml:base="https://summertbd.github.io/posts/my_mod/"><![CDATA[<p>今天是美好的一天，我花了一个下午，看完了一季动漫，和最新季的最新进度。</p>

<p>为什么我会突然看这部动漫？其实这离我上一次看动漫不远，大概隔了一个月。你可能会说一个月还不远吗？那如果我说，上一次离上上次隔了一年呢？其实，在昨天我已经看了一部分，但是今天我又从头看了一遍。</p>

<p>在昨天，我发现《关于我转生成史莱姆这档事》出了第4季，其实我还没有动力去看，因为它第三季我都没看过。那我为什么突然想看了呢？如果注意博客发布时间，就知道我是发布完博客，才看的。我发布一篇博客后，我心血来潮，我问 AI 你觉得 https://summertbd.github.io/ 这个人的博客怎么样，它回答的八九不离十，但有被骗的经历，我决定问一个刁钻的问题，我的头像的人物是谁，他说是 Shion，我看他的思考过程和他给的答案，我恍惚了，到现在我还是不能释怀。我的确用过这个动漫的头像，但不是 Shion，是利姆露，我现在用的是超时空辉夜姬的彩叶，那一瞬间我想再去看一眼这个我当初说要追到底的动漫。</p>

<p>读者可能不知道，这个时间节点，我才大一，这部动漫是我初中看的，而高中三年，我的少年心气似乎没了。我当初毕业，我是这么跟朋友说的，”初中到高中，我从稚嫩到了成熟，到大学，我一定从成熟到幼稚”。对不起，我没有做到，当我意识到时，我发现我的人生是一个骗局，我说我要重拾少年心气，我没有做到，我说我不要在这么煎熬地向前，我没有做到，我看着别人前进，我无法做到冷静，我还是会打破我的节奏。</p>

<p>当我重新看这部动漫的时候，我想直接看第四季，但是发现我的记忆有点跟不上，我就从第三季开始，虽然还是有点记忆缺失，但我不愿意从第二季开始。我最喜欢的是第一季，但与第二季的拖沓和第一季的不拖泥带水相比，我被恶心了好一阵子，我都忘了我有没有看完第二季，但那不重要了。</p>

<p>最重要的不是它有多好，而是它有多烂。为什么这么说呢？其实这个动漫，我觉得作者没把我当人。他就像一群孩子过家家，它的套路有时候看着很牵强，有时候没有什么逻辑，有时候完全不符合常理。我一边骂作者没把我当人，觉得这个剧情好尬，一边又继续看下去。我当时是怎么看下去的？我也不知道。我就是想继续看下去的。我对逻辑其实很挑的，如果我发现有一处逻辑不严谨，其实我就很容易看不下去，但是我就是看下去了。</p>

<p>当时的我又是怎样的呢？当我照向镜子，我发现我不是我，我不是我眼中的我。</p>

<p>我想，这部作品虽然儿戏，但我想这就是动漫的力量。影视作品不会告诉你，你该怎么做，他只会给你讲一个故事，一个打动你的故事。我有多久是为了看动漫而看动漫，是为了休息而休息，而不是为了更好的工作而休息。我有多久没有见到我自己了，我的身体不是我灵魂的身体，我的灵魂不是我身体的灵魂。</p>

<p>这部动漫，是如此让我尴尬，可这不就是我看下去的理由。</p>

<p>我想我的精神老了，他需要的不是养老，而是一场复活。一场由死到生的复活。</p>]]></content><author><name>summerTBD</name></author><category term="生活" /><category term="博客" /><category term="生活" /><summary type="html"><![CDATA[今天是美好的一天，我花了一个下午，看完了一季动漫，和最新季的最新进度。]]></summary></entry><entry><title type="html">奇数码问题充分性的一个对称性证明（及背后的群作用思想）</title><link href="https://summertbd.github.io/posts/odd_number_puzzle_symmetry/" rel="alternate" type="text/html" title="奇数码问题充分性的一个对称性证明（及背后的群作用思想）" /><published>2026-05-22T00:00:00+08:00</published><updated>2026-05-22T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/odd_number_puzzle_symmetry</id><content type="html" xml:base="https://summertbd.github.io/posts/odd_number_puzzle_symmetry/"><![CDATA[<h2 id="摘要">摘要</h2>

<p>在《算法竞赛进阶指南》中，奇数码问题（奇数阶数码推盘）的可达性条件通常只给出必要性证明——任何合法移动不改变逆序对奇偶性；充分性的证明常被省略或依赖繁杂的分情况构造。本文记录一个利用<strong>棋盘旋转对称性</strong>将上下移动转化为左右移动的证明思路，并指出其背后的群论本质：旋转是状态空间上的一个自同构，而”旋转变换→执行操作→旋转变换回来”的模式正是群作用中的共轭思想。全文给出严格引理与完整构造，展示对称性如何让证明变得更整体、更优雅。</p>

<hr />

<h2 id="1-问题陈述">1. 问题陈述</h2>

<p>设 $n$ 为正奇数。在一个 $n\times n$ 的网格中，放置有数字 $1,2,\dots,n^2-1$ 以及一个空格（记作 $0$）。每一步可以将空格与其上、下、左、右相邻的数字交换位置。给定两个局面（状态）$A$ 和 $B$，问是否可以通过一系列合法移动将 $A$ 变为 $B$。</p>

<p><strong>已知事实（必要性）</strong>：将局面按行优先展开成一个长度为 $n^2$ 的序列（舍去 $0$），其逆序对数量的奇偶性在任何合法移动下保持不变。<br />
因此，若 $A$ 与 $B$ 的逆序对奇偶性不同，则它们一定不可达。</p>

<p><strong>目标（充分性）</strong>：若 $A$ 与 $B$ 的逆序对奇偶性相同，证明它们一定可以相互到达。</p>

<hr />

<h2 id="2-证明概览">2. 证明概览</h2>

<p>传统证明中，需要围绕”如何在不破坏已归位数字的前提下上下移动一个数字”进行复杂的行走构造。本证明的核心是<strong>视角转换</strong>：</p>

<ul>
  <li>将整个棋盘顺时针旋转 $90^\circ$，原问题中的上下移动变成了新视角下的左右移动。</li>
  <li>若我们能证明新视角下的目标一定可达，那么原问题也一定可达。</li>
  <li>通过交替使用“左右移动”引理和旋转技巧，我们可以像搭积木一样逐位将数字归位，最终只剩右下角 $2\times2$ 区域，而该区域的可达性恰好由逆序对奇偶性完全决定。</li>
</ul>

<p>这一想法本质上等同于：旋转是奇数码问题状态空间的一个<strong>自同构</strong>，它保持移动关系与逆序对性质。而“旋转—执行操作—逆旋转”的步骤，正是群作用理论里<strong>共轭</strong>的典型用法。</p>

<hr />

<h2 id="3-记号与基本引理">3. 记号与基本引理</h2>

<p>记状态空间为 $\mathcal{S}$，包含所有将 $0,1,\dots,n^2-1$ 填入 $n\times n$ 网格的排列。定义：</p>

<ul>
  <li><strong>目标状态</strong> $T$：数字按行优先顺序从左到右、从上到下排列，最后一个是 $0$。</li>
  <li><strong>逆序对</strong>：按行优先展开为序列 $s_1,s_2,\dots,s_{n^2}$ 后，满足 $i&lt;j$ 且 $s_i&gt;s_j$ 且 $s_i,s_j\neq 0$ 的数对个数。记 $\operatorname{inv}(A)$ 为状态 $A$ 的逆序对数。</li>
  <li><strong>合法移动</strong>：将空格 $0$ 与上下左右相邻数字交换，记为 $\sim$ 关系。</li>
</ul>

<p><strong>引理 0（必要性）</strong>：若 $A\sim B$，则 $\operatorname{inv}(A)\equiv\operatorname{inv}(B)\pmod{2}$。</p>

<blockquote>
  <p>此即已知结果，不赘证。</p>
</blockquote>

<hr />

<h2 id="4-核心引理">4. 核心引理</h2>

<h3 id="41-左右移动引理">4.1 左右移动引理</h3>

<p><strong>引理 1（自由左右移动）</strong><br />
设当前状态中，已有前 $m$ 个数字（按目标状态 $T$ 的顺序）放在了它们的正确位置，且这些位置构成棋盘左上角的一个矩形区域（即已填满若干整行加上下一行的前若干格）。对于任意数字 $k$ （$k$ 尚未归位），只需利用待处理区域（剩余格子），就可以将该数字左右移动到任意目标列，而不改变任何已归位数字的位置。</p>

<p><strong>证明</strong><br />
将空格移动到数字 $k$ 的同行、相邻列，然后通过与空格交换来实现左右移动。由于已归位区域在左上角且连续，空格在剩余区域内总是有足够的自由度绕到 $k$ 的左侧或右侧，且所有路径不会穿过已完成区域。具体构造可参考标准八数码问题中的“空格绕行”方法，这里不再画格子说明。</p>

<blockquote>
  <p>关键点：该引理只要求“剩余区域连通且不受已完成数字阻挡”，而这对于左上角矩形区域是自然成立的。</p>
</blockquote>

<hr />

<h3 id="42-旋转对称引理">4.2 旋转对称引理</h3>

<p>定义映射 $R:\mathcal{S}\to\mathcal{S}$ 为<strong>将整个棋盘顺时针旋转 $90^\circ$</strong>（格子内容跟随旋转）。也就是说，原位置 $(i,j)$ 的数字移动到新位置 $(j,n+1-i)$。</p>

<p><strong>引理 2（旋转不改变可达性与逆序对）</strong><br />
对于奇数 $n$，旋转 $R$ 满足：</p>

<ol>
  <li>$A$ 可达 $B$ $\iff$ $R(A)$ 可达 $R(B)$；</li>
  <li>$\operatorname{inv}(R(A))\equiv\operatorname{inv}(A)\pmod{2}$。</li>
</ol>

<p><strong>证明</strong><br />
(1) 因为 $R$ 是网格图的一个自同构：它将相邻关系映为相邻关系，且把空格映射到空格的位置。因此，$A$ 中一步移动对应 $R(A)$ 中同样的一步移动，只是方向旋转了 $90^\circ$。递推可得可达性等价。</p>

<p>(2) 将 $n\times n$ 的格子按行优先编号。旋转 $R$ 诱导了格子集合上的一个置换 $\sigma$。当 $n$ 为奇数时，中心一格不动，其余 $n^2-1$ 个格子均落在长度为 $4$ 的循环中，且 $4$-循环的个数为 $\frac{n^2-1}{4}$，由于 $n$ 为奇数，该数为整数且为偶数。每个 $4$-循环是奇置换，偶数个奇置换的乘积为偶置换，故 $\sigma$ 是偶置换。</p>

<p>由于 $A$ 按行优先展开成的序列可通过将目标序列按 $\sigma^{-1}$ 重排得到，而偶置换不改变排列的奇偶性，因此展开序列（含 $0$）的奇偶性在旋转前后不变。去掉 $0$ 后，$0$ 在序列中位置的改变等价于对剩余 $n^2-1$ 个元素施加某个置换，该置换的奇偶性与 $\sigma$ 的奇偶性相关，但恰好与 $0$ 位置变化带来的影响抵消，最终 $\operatorname{inv}(R(A))$ 与 $\operatorname{inv}(A)$ 奇偶性相同。具体推导亦可见经典置换群教材中的标准结论。</p>

<blockquote>
  <p>注：这是整个证明中唯一需要计算置换奇偶性的地方，且仅在处理逆序对不变量时才用到。</p>
</blockquote>

<hr />

<h3 id="43-旋转后的区域形状">4.3 旋转后的区域形状</h3>

<p>使用旋转时，我们关心的是：当我们在“旋转后的视角”下应用引理 1 时，已完成区域的形状是否还能保证剩余区域连通。</p>

<p><strong>引理 3（旋转后的区域适配）</strong><br />
假设在原视角下，已归位的前 $m$ 个数字占据左上角的标准矩形区域（即整行整列的阶梯形）。将当前状态 $C$ 与目标状态 $T$ 同时顺时针旋转 $90^\circ$，得到 $R(C)$ 与 $R(T)$。<br />
在 $R(T)$ 中，前 $m$ 个数字（相同的数字集合）占据的位置仍然是连通的，且它们的补集（剩余自由格子）构成一个连通的、没有“孤岛”的区域，使得引理 1 的前提仍然成立。</p>

<p><strong>证明</strong><br />
原左上角的矩形区域旋转后变成右上角或底部的一个矩形区域，具体形状因矩形是否填满整行而略有不同，但无论如何，已归位数字总是形成一个“角落区域”，剩余格子是一个连通的矩形/ L 形区域。因为 $n$ 为奇数且是整行整列归位，补集始终连通。例如，前 $n$ 个数字填满第一行，旋转后变成最右列，剩余区域仍为 $(n-1)\times n$ 的连通块，空格完全可以在其中活动。</p>

<blockquote>
  <p>这保证了：在新视角下，我们依然可以安全地调用引理 1 来左右移动某个数字，而不会破坏任何已归位数字。</p>
</blockquote>

<hr />

<h2 id="5-充分性证明">5. 充分性证明</h2>

<p><strong>定理（充分性）</strong><br />
若两个状态 $A$ 与 $B$ 满足 $\operatorname{inv}(A)\equiv\operatorname{inv}(B)\pmod{2}$，则 $A$ 可达 $B$。</p>

<p><strong>证明</strong><br />
我们证明任意状态 $A$ 都可以到达某个“标准状态”，而标准状态只有两个（逆序对奇偶性各对应一个），因此奇偶性相同的状态互达。</p>

<p><strong>构造过程</strong>：从状态 $A$ 开始，按行优先顺序依次将数字 $1,2,\dots,n^2-3$ 归位（最后留下右下角 $2\times2$ 区域暂不处理）。</p>

<p>假设前 $k-1$ 个数字已经放在正确位置（即与目标状态 $T$ 中位置一致）。现在要放置数字 $k$。</p>

<ul>
  <li><strong>左右调整</strong>：若数字 $k$ 已经在正确行，直接用引理 1 将其左右移到正确列。</li>
  <li><strong>上下调整</strong>：若数字 $k$ 不在正确行，则进行如下“旋转–移动–逆旋转”操作：
    <ol>
      <li>将当前状态 $C$ 与目标 $T$ 同时顺时针旋转 $90^\circ$，得到 $R(C)$ 与 $R(T)$。</li>
      <li>在 $R(C)$ 中，数字 $k$ 的“上下偏差”变成了左右偏差。由引理 3，此时已完成区域仍然满足引理 1 的条件。</li>
      <li>应用引理 1，在 $R(C)$ 中将数字 $k$ 左右移动到它在 $R(T)$ 中的正确列。</li>
      <li>得到状态 $D’$，再逆时针旋转 $90^\circ$ 回到原视角，得到 $D=R^{-1}(D’)$。</li>
      <li>此时在原棋盘上，数字 $k$ 已被移到正确行，且前 $k-1$ 个数字未被破坏。</li>
    </ol>
  </li>
  <li>再用引理 1 进行左右微调，即可将 $k$ 完全归位。</li>
</ul>

<p>重复此步骤，直到只剩下右下角 $2\times2$ 区域（三个数字加一个空格）。</p>

<p><strong>最后的 $2\times2$ 区域</strong>：<br />
此时棋盘上只剩三个数字和空格未归位，总状态数只有 $4! = 24$ 种，但考虑到空格只能在 $2\times2$ 内移动，实际上可能的状态只有 $4$ 种（空格固定一角时数字的 $3!$ 排列受限于平面移动）。更直接地，可以证明这 $2\times2$ 区域的所有合法移动不改变整个棋盘逆序对的奇偶性，并且恰好有 <strong>两类</strong> 排列状态，分别对应逆序对奇偶性的奇和偶。由于我们在整个过程始终维持逆序对奇偶性不变（由引理 0 和引理 2 保证），最终的 $2\times2$ 状态必属于与初始 $A$ 相同奇偶性的那一类。若与目标状态的逆序对奇偶性相同，最后三个数字的排列必定是目标状态中的顺序；若与目标状态的逆序对奇偶性不同，则是交换了某两个数字的状态（即不可达的另一个标准态）。</p>

<p>因此，任意奇偶性相同的状态都可以用上述过程到达同一个标准状态（例如所有数字归位的目标状态 $T$），从而它们彼此可达。$\square$</p>

<hr />

<h2 id="6-群论视角藏在背后的共轭与群作用">6. 群论视角：藏在背后的共轭与群作用</h2>

<p>回过头看，“旋转–执行操作–逆旋转”这个三步走的做法，在群论中有个名字：<strong>共轭</strong>。</p>

<p>把状态空间 $\mathcal{S}$ 视为集合，合法移动序列构成的群记为 $G$（实际是生成这些移动的变换群，但不必显式写出）。旋转 $R$ 是 $\mathcal{S}$ 上的一个双射，且它保持移动关系，即 $R(g(x)) = g’(R(x))$ 对某个 $g’\in G$ 成立，相当于说 $R$ 正规化 $G$。</p>

<p>当我们想要实现一个垂直移动操作 $f$（在原始问题上很麻烦）时，我们不直接构造 $f$，而是：</p>

<ul>
  <li>先用 $R$ 将状态“平移”到一个新坐标系，</li>
  <li>在该坐标系下执行一个简单的水平移动 $h$（即已有的引理 1），</li>
  <li>再用 $R^{-1}$ 平移回来。</li>
</ul>

<p>所得的操作 $R^{-1}\circ h \circ R$ 就是原始坐标系下的垂直移动。这就是群作用理论中<strong>利用对称性传递操作</strong>的核心技巧。</p>

<p>同时，引理 2 中“旋转不改变逆序对”本质上利用了旋转置换是偶置换这一群论事实，这与“旋转是状态空间的一个对称性”是相容的。整个证明可以看作：利用旋转群 $\langle R\rangle$ 对状态空间的作用，将困难的“上下移动”约化为已经解决的“左右移动”，这正是群作用下轨道分析的一个朴素实例。</p>

<p>对于只自学了 40 多页群论就直觉到“一看见对称就想到群”的笔者来说，这个证明可以说是一次与抽象代数的神交——它在没有显式写出群、轨道、稳定子等术语的情况下，已经运用了共轭与群作用的核心思维。</p>

<hr />

<h2 id="7-注记与传统证明的比较">7. 注记：与传统证明的比较</h2>

<ul>
  <li><strong>传统证明</strong>：通常分别处理“同行移动”和“跨行移动”，后者需要显式构造空格的长距离绕行路径，保证不扰乱已归位数字。证明繁琐但直接。</li>
  <li><strong>本证明</strong>：通过旋转对称性，把行与列的移动统一到同一个引理下，避免了对垂直移动的重复构造，使逻辑更加干净。代价是需要额外证明旋转保持逆序对、以及旋转后区域形状依然适配——但这些性质本身就是对称性带来的自然结果。</li>
  <li><strong>适用性</strong>：该方法紧紧依赖 $n$ 为奇数（保证旋转为偶置换且区域对称），对于偶数阶数码问题则不适用，这与已知结论一致。</li>
</ul>

<hr />

<h2 id="8-结语">8. 结语</h2>

<p>奇数码问题的充分性证明之所以常被略去，是因为复杂的分情形构造容易淹没问题的本质。而当我们抬起视角，利用棋盘自身的旋转对称性，证明就变得有节奏、有结构，甚至透露出群论的影子。这个证明不仅让充分性“显然”，更是一次美妙的思想实验：原来对称性不只是现象，它可以成为证明的工具。</p>

<p>对于竞赛选手，掌握这种“旋转视角”的技巧也可能在其他网格问题中发挥作用；对于数学爱好者，它则是一扇窥见群作用思想的窗——不需要把群论学完，就能先呼吸到它的空气。</p>]]></content><author><name>summerTBD</name></author><category term="数学" /><category term="算法" /><category term="奇数码" /><category term="逆序对" /><category term="对称性" /><category term="群作用" /><category term="组合游戏" /><summary type="html"><![CDATA[摘要]]></summary></entry><entry><title type="html">无向图的最小顶点着色问题</title><link href="https://summertbd.github.io/posts/minimum_vertex_coloring_problem_for_undirected_graphs/" rel="alternate" type="text/html" title="无向图的最小顶点着色问题" /><published>2026-05-21T00:00:00+08:00</published><updated>2026-05-21T00:00:00+08:00</updated><id>https://summertbd.github.io/posts/minimum_vertex_coloring_problem_for_undirected_graphs</id><content type="html" xml:base="https://summertbd.github.io/posts/minimum_vertex_coloring_problem_for_undirected_graphs/"><![CDATA[<h2 id="无向图的最小顶点着色问题概述">无向图的最小顶点着色问题概述</h2>

<p>给你一个无向图，有 \(n\) 个顶点和 \(m\) 条边。现在需要给图中的每个顶点染色，要求<strong>相邻的顶点不能染相同颜色</strong>。请问最少需要多少种颜色才能完成染色？</p>

<p>这就是经典的<strong>图着色问题（Graph Coloring Problem）</strong>，属于 NP-完全问题，当图规模较小时可以用回溯法（DFS + 剪枝）求解。</p>

<h2 id="思路">思路</h2>

<h3 id="核心思想">核心思想</h3>

<p>我们采用<strong>回溯法（DFS）</strong>，逐一为每个顶点选择合法颜色，并利用当前已知的最优解进行剪枝。</p>

<p>由于有 \(n\) 个顶点，最坏情况下每个顶点一种颜色，因此答案一定在 \(1 \sim n\) 之间。我们可以<strong>从小到大猜测</strong>所需颜色数，尝试用不超过该数量的颜色给图染色。如果染色成功，说明当前猜测数可行；否则需要增加颜色数。</p>

<h3 id="算法流程">算法流程</h3>

<p>定义 <code class="language-plaintext highlighter-rouge">dfs(u, cur_max)</code>：当前正在给第 \(u\) 个顶点染色，猜测的最多可用颜色数为 <code class="language-plaintext highlighter-rouge">cur_max</code>。</p>

<ol>
  <li><strong>剪枝</strong>：如果 <code class="language-plaintext highlighter-rouge">cur_max &gt;= ans</code>（当前已找到的最优解），则直接返回，不再继续搜索。</li>
  <li><strong>终止条件</strong>：如果 \(u \geq n\)（所有顶点已染色完成），更新 <code class="language-plaintext highlighter-rouge">ans = cur_max</code> 并返回。</li>
  <li><strong>尝试使用已有颜色</strong>：遍历 <code class="language-plaintext highlighter-rouge">0</code> 到 <code class="language-plaintext highlighter-rouge">cur_max - 1</code> 号颜色，如果颜色 \(i\) 与已染色的邻接顶点不冲突，则将顶点 \(u\) 染成 \(i\)，然后递归 <code class="language-plaintext highlighter-rouge">dfs(u+1, cur_max)</code>，最后回溯 <code class="language-plaintext highlighter-rouge">color[u] = -1</code>。</li>
  <li><strong>尝试使用新颜色</strong>：如果已有颜色都不可用，则将顶点 \(u\) 染成 <code class="language-plaintext highlighter-rouge">cur_max</code>（新颜色），递归 <code class="language-plaintext highlighter-rouge">dfs(u+1, cur_max+1)</code>，然后回溯 <code class="language-plaintext highlighter-rouge">color[u] = -1</code>。</li>
</ol>

<h3 id="关键细节">关键细节</h3>

<ul>
  <li>回溯时将 <code class="language-plaintext highlighter-rouge">color[u]</code> 重置为 <strong>不可能的值</strong>。若颜色编号从 0 开始，则重置为 <code class="language-plaintext highlighter-rouge">-1</code>；若从 1 开始，则重置为 <code class="language-plaintext highlighter-rouge">0</code>。</li>
  <li>算法复杂度为指数级，适用于小规模图（\(n \leq 20\) 左右）。</li>
</ul>

<h2 id="例题蓝桥杯-2017-国赛--分考场">例题：蓝桥杯 2017 国赛 — 分考场</h2>

<h3 id="题目描述">题目描述</h3>

<blockquote>
  <p><strong>问题描述</strong></p>

  <p>\(n\) 个人参加某项特殊考试。为了公平，要求任何两个<strong>认识的人</strong>不能分在同一个考场。求至少需要分几个考场才能满足条件。</p>
</blockquote>

<blockquote>
  <p><strong>输入格式</strong></p>

  <p>第一行一个整数 \(n\)（\(1 \le n \le 100\)），表示参加考试的人数。
第二行一个整数 \(m\)，表示接下来有 \(m\) 行数据。
接下来 \(m\) 行，每行两个整数 \(a, b\)（\(1 \le a, b \le n\)），表示第 \(a\) 个人与第 \(b\) 个人认识。</p>
</blockquote>

<blockquote>
  <p><strong>输出格式</strong></p>

  <p>输出一行一个整数，表示最少分几个考场。</p>
</blockquote>

<blockquote>
  <p><strong>运行限制</strong></p>

  <ul>
    <li>最大运行时间：1s</li>
    <li>最大运行内存：256MB</li>
  </ul>
</blockquote>

<blockquote>
  <p><strong>样例输入</strong></p>

  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre>5
8
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5
</pre></td></tr></tbody></table></code></pre></div>  </div>
</blockquote>

<blockquote>
  <p><strong>样例输出</strong></p>

  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>4
</pre></td></tr></tbody></table></code></pre></div>  </div>
</blockquote>

<h3 id="题目分析">题目分析</h3>

<p>这道题正是图着色问题的经典应用：</p>

<ul>
  <li>将每个学生看作一个<strong>顶点</strong></li>
  <li>将”认识”关系看作<strong>边</strong>（认识的人之间不能在同一考场）</li>
  <li>将每个考场看作一种<strong>颜色</strong></li>
  <li>目标：用最少的考场数（颜色数）完成分配</li>
</ul>

<p>因此，上述回溯算法完全适用于此题。唯一需要注意的是，题目中 \(n\) 的最大值可达 100，如果直接套用暴力回溯会超时。实际竞赛中通常采用<strong>贪心策略</strong>或<strong>更优的剪枝方法</strong>来解决较大规模的数据。</p>

<h3 id="参考代码适用于小规模数据">参考代码（适用于小规模数据）</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
</pre></td><td class="rouge-code"><pre><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;functional&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="n">m</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">n</span> <span class="o">&gt;&gt;</span> <span class="n">m</span><span class="p">;</span>

    <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;&gt;</span> <span class="n">adj</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">false</span><span class="p">));</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">m</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">;</span>
        <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">a</span> <span class="o">&gt;&gt;</span> <span class="n">b</span><span class="p">;</span>
        <span class="n">adj</span><span class="p">[</span><span class="n">a</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">b</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
        <span class="n">adj</span><span class="p">[</span><span class="n">b</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">a</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">color</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">ans</span> <span class="o">=</span> <span class="n">n</span><span class="p">;</span>

    <span class="k">auto</span> <span class="n">is_safe</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">bool</span>
    <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">adj</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">color</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">c</span><span class="p">)</span>
                <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">};</span>

    <span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">dfs</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="kt">int</span> <span class="n">cur_max</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">cur_max</span> <span class="o">&gt;=</span> <span class="n">ans</span><span class="p">)</span>
            <span class="k">return</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">u</span> <span class="o">&gt;=</span> <span class="n">n</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">ans</span> <span class="o">=</span> <span class="n">cur_max</span><span class="p">;</span>
            <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">cur_max</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">is_safe</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">i</span><span class="p">))</span>
            <span class="p">{</span>
                <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
                <span class="n">dfs</span><span class="p">(</span><span class="n">u</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">cur_max</span><span class="p">);</span>
                <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="n">cur_max</span><span class="p">;</span>
        <span class="n">dfs</span><span class="p">(</span><span class="n">u</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">cur_max</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
        <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">};</span>

    <span class="n">dfs</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">ans</span> <span class="o">&lt;&lt;</span> <span class="sc">'\n'</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="代码">代码</h2>

<h3 id="实现一颜色从-0-开始编号">实现一：颜色从 0 开始编号</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
</pre></td><td class="rouge-code"><pre><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="n">m</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">n</span> <span class="o">&gt;&gt;</span> <span class="n">m</span><span class="p">;</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;&gt;</span> <span class="n">adj</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="mi">0</span><span class="p">));</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">m</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">;</span>
        <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">a</span> <span class="o">&gt;&gt;</span> <span class="n">b</span><span class="p">;</span>
        <span class="n">adj</span><span class="p">[</span><span class="n">a</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">b</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
        <span class="n">adj</span><span class="p">[</span><span class="n">b</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">a</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">color</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">ans</span> <span class="o">=</span> <span class="n">n</span><span class="p">;</span>

    <span class="k">auto</span> <span class="n">is_safe</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">bool</span>
    <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">adj</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">color</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">c</span><span class="p">)</span>
                <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">};</span>

    <span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">dfs</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="kt">int</span> <span class="n">cur_max</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">cur_max</span> <span class="o">&gt;=</span> <span class="n">ans</span><span class="p">)</span>
            <span class="k">return</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">u</span> <span class="o">&gt;=</span> <span class="n">n</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">ans</span> <span class="o">=</span> <span class="n">cur_max</span><span class="p">;</span>
            <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">// 尝试使用已有的颜色（0 ~ cur_max-1）</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">cur_max</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">is_safe</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">i</span><span class="p">))</span>
            <span class="p">{</span>
                <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
                <span class="n">dfs</span><span class="p">(</span><span class="n">u</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">cur_max</span><span class="p">);</span>
                <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="c1">// 回溯</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="c1">// 尝试使用新颜色 cur_max</span>
        <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="n">cur_max</span><span class="p">;</span>
        <span class="n">dfs</span><span class="p">(</span><span class="n">u</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">cur_max</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
        <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="c1">// 回溯</span>
    <span class="p">};</span>

    <span class="n">dfs</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">ans</span> <span class="o">&lt;&lt;</span> <span class="sc">'\n'</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="实现二颜色从-1-开始编号">实现二：颜色从 1 开始编号</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
</pre></td><td class="rouge-code"><pre><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;functional&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">ios</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
    <span class="n">cout</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>

    <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="n">m</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">n</span> <span class="o">&gt;&gt;</span> <span class="n">m</span><span class="p">;</span>

    <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;&gt;</span> <span class="n">adj</span><span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span><span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">false</span><span class="p">));</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">color</span><span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">m</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="n">v</span><span class="p">;</span>
        <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">u</span> <span class="o">&gt;&gt;</span> <span class="n">v</span><span class="p">;</span>
        <span class="n">adj</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="n">adj</span><span class="p">[</span><span class="n">v</span><span class="p">][</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">function</span><span class="o">&lt;</span><span class="kt">bool</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">is_safe</span><span class="p">;</span>
    <span class="n">is_safe</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">bool</span>
    <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">adj</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">color</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">c</span><span class="p">)</span>
                <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">};</span>

    <span class="kt">int</span> <span class="n">ans</span> <span class="o">=</span> <span class="n">n</span><span class="p">;</span>

    <span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">dfs</span><span class="p">;</span>
    <span class="n">dfs</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="kt">int</span> <span class="n">cur_max</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">cur_max</span> <span class="o">&gt;=</span> <span class="n">ans</span><span class="p">)</span>
            <span class="k">return</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">u</span> <span class="o">&gt;</span> <span class="n">n</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">ans</span> <span class="o">=</span> <span class="n">cur_max</span><span class="p">;</span>
            <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">// 尝试使用已有的颜色（1 ~ cur_max）</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">cur_max</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">is_safe</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">i</span><span class="p">))</span>
            <span class="p">{</span>
                <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
                <span class="n">dfs</span><span class="p">(</span><span class="n">u</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">cur_max</span><span class="p">);</span>
                <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 回溯（0 表示未染色）</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="c1">// 尝试使用新颜色 cur_max + 1</span>
        <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="n">cur_max</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">dfs</span><span class="p">(</span><span class="n">u</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">cur_max</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
        <span class="n">color</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 回溯</span>
    <span class="p">};</span>

    <span class="n">dfs</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">ans</span> <span class="o">&lt;&lt;</span> <span class="sc">'\n'</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="总结">总结</h2>

<ul>
  <li>该算法本质上是<strong>暴力搜索 + 剪枝</strong>，遍历所有可能的染色方案。</li>
  <li>通过维护当前最优解 <code class="language-plaintext highlighter-rouge">ans</code> 进行剪枝，大幅减少搜索空间。</li>
  <li>适用于小规模图，当 \(n\) 较大时需考虑其他算法（如贪心着色、分支定界等）。</li>
</ul>]]></content><author><name>summerTBD</name></author><category term="算法" /><category term="DFS" /><category term="回溯" /><summary type="html"><![CDATA[无向图的最小顶点着色问题概述]]></summary></entry></feed>