diff --git a/.agents/skills/avoid-ai-writing-zh/CHANGELOG.md b/.agents/skills/avoid-ai-writing-zh/CHANGELOG.md deleted file mode 100644 index 9ef3453..0000000 --- a/.agents/skills/avoid-ai-writing-zh/CHANGELOG.md +++ /dev/null @@ -1,30 +0,0 @@ -# 变更记录 - -所有重要变更都记录在这里。 - ---- - -## [0.3.1] - 2026-04-16 - -### Changed -- 补齐中文 `README.md`、`CHANGELOG.md` 和 `LICENSE` -- 合并重复结构,去掉“补全版”“原版”这类元说明 -- 清理正文中残留的英文说明词,统一为中文 agent 语境 -- 收紧虚构文学相关判断,保留 `literary-fiction` / `web-fiction` 档位 - -### Added -- 新增面向发布的中文配套文档 - ---- - -## [0.3.0] - 2026-04-16 - -### Added -- 中文版 `avoid-ai-writing` 技能雏形 -- 内容模式、中文高危词、中文辅助词、模板句、过渡短语、结构问题、整段重写阈值 -- 虚构文学问题清单 -- `literary-fiction` / `web-fiction` 档位 -- 技术文档例外和自动识别线索 - -### Changed -- 从原技能 `avoid-ai-writing` 3.3.1 迁移为中文版本 diff --git a/.agents/skills/avoid-ai-writing-zh/LICENSE b/.agents/skills/avoid-ai-writing-zh/LICENSE deleted file mode 100644 index 73793ba..0000000 --- a/.agents/skills/avoid-ai-writing-zh/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Kimmy Liu - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.agents/skills/avoid-ai-writing-zh/README.md b/.agents/skills/avoid-ai-writing-zh/README.md deleted file mode 100644 index eb78c98..0000000 --- a/.agents/skills/avoid-ai-writing-zh/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# avoid-ai-writing-zh - -审读并重写中文内容,清理 AI 痕迹、翻译腔和模板腔。这个技能默认面向文学工作空间,尤其关注虚构文学,也适用于评论、摘要、项目说明和创作笔记。 - -## 这份技能做什么 - -- 找出让文字像 AI 生成的写法 -- 在保留原意的前提下重写 -- 在需要时只标记,不重写 -- 处理小说里常见的概念先行、对白书面腔、作者总结句、章尾收口和声线打平 - -## 两种模式 - -### 重写模式 - -默认模式。先审,再改,再做一次回读。 - -### 只标记模式 - -只列问题,不改正文。适合这些情况: - -- 作者想先看问题,再自己定稿 -- 某些写法是有意保留的 -- 你在审阅别人的内容,不能改原文 -- 你只想快速扫一遍 - -当用户说出 `只标记`、`只审不改`、`扫描`、`只看问题`、`detect`、`flag only`、`audit only`、`just flag`、`scan` 这类表达时,进入只标记模式。未指定时,默认重写模式。 - -## 适用范围 - -- 通用中文写作 -- 虚构文学创作 -- 评论、摘要、项目说明、创作笔记 - -如果内容来自 `projects/` 下的项目,先看该项目的语言限制,再用这个技能去清 AI 痕迹。它不替项目改定稿,只负责把文字里的 AI 味压下去。 - -## 虚构文学侧重 - -- 严肃文学更看重声线、留白和场面压力,也更怕解释欲、结论癖和修辞太整齐 -- 网络文学更看重推进、冲突和钩子,也更怕套路化开头、重复钩子和流水线式章节骨架 - -## 输出习惯 - -重写时,先列问题,再给重写版,再做简要说明,最后回读一遍,检查是否还有残留的 AI 痕迹。只标记时,只列问题和判断,不改正文。 diff --git a/.agents/skills/avoid-ai-writing-zh/SKILL.md b/.agents/skills/avoid-ai-writing-zh/SKILL.md deleted file mode 100644 index 84c07fb..0000000 --- a/.agents/skills/avoid-ai-writing-zh/SKILL.md +++ /dev/null @@ -1,551 +0,0 @@ ---- -name: avoid-ai-writing-zh -description: 审读并重写中文内容,清理 AI 痕迹、翻译腔和模板腔。默认面向文学工作空间,支持只标记模式。 -version: 0.3.1 -license: MIT -compatibility: 兼容 agentskills.io SKILL.md 格式的 AI 助手,以及 OpenClaw。无需外部工具或接口。 -metadata: - author: Kimmy Liu - adapted_by: Codex - source_skill: avoid-ai-writing - source_version: "3.3.1" - tags: writing editing voice quality chinese ai-isms - agentskills_spec: "1.0" - openclaw: - emoji: "✍️" ---- - -# 避免 AI 写作:审读与重写 - -你正在审读和重写中文内容,目标是清理让文字像机器生成的 AI 痕迹。 - -这个中文版不是逐句翻译,而是把原技能的判断逻辑改成中文可直接使用的版本。 - -在这个工作空间里,它默认服务于虚构文学创作,所以除了通用中文表达问题,还要额外审理场面、对白、视角、章尾和人物声线。 - -## 模式 - -这份技能只有两种模式: - -**`rewrite`**(默认,重写模式) -标记 AI 痕迹,并直接重写文本,把问题修掉。 - -**`detect`**(只标记模式) -只标记,不重写。适合以下情况: - -- 作者只想看哪些地方被标出来,再自己决定怎么改 -- 某些 AI 痕迹是有意保留的,未必都该删 -- 你在审阅已发布内容、他人文本或不希望被改动的参考材料 -- 你只想快速扫一遍,不想做完整重写 - -当用户说出 `detect`、`flag only`、`audit only`、`just flag`、`scan`、`只标记`、`只审不改`、`仅标记` 之类表达时,进入 `detect` 模式。未指定时,默认 `rewrite`。 - ---- - -在 **rewrite** 模式下,你的任务是: - -1. **先审**:找出每一处 AI 痕迹,并引用具体文本 -2. **再改**:给出清理后的版本,把 AI 痕迹去掉 -3. **做简要说明**:概括你改了什么,以及为什么改 - -在 **detect** 模式下,你的任务是: - -1. **先审**:找出每一处 AI 痕迹,并引用具体文本 -2. **再判断**:说明哪些是明确问题,哪些可能是有意保留、在语境里也成立的写法 - -## 文学工作空间用法 - -- 如果任务来自某个 `projects/` 下的项目,优先考虑项目的语言限制,再用本技能去清 AI 痕迹。 -- 本技能只负责“像不像 AI”,不负责替项目改定稿。项目里已经确认的声线、题材边界和章节任务,优先级高于这里的通用提醒。 -- 在虚构文学里,能不能读懂不是第一标准。更关键的是: - - 这句话是不是像这个视角会这样想 - - 这段对白是不是像这个人会这样说 - - 这段场面是不是先给动作和压力,再给判断 - - 章尾是不是留在物件、动作、半句对白或未完成压力上 - ---- - -## 需要删除或修正的内容 - -### 格式问题 - -- **破折号过多**:把 `—` 和 `--` 尽量改成逗号、句号、括号,或者直接重写。目标是零,硬上限是每 1,000 字最多一个。这个规则也适用于标题。 -- **加粗过量**:大多数词不该加粗。每个大段最多保留一个强调点,或者干脆不用。 -- **标题里放 emoji**:删掉。不要写成 `## 🚀 重点`。如果是社交短文,末尾可极少量使用,但不要夹在句子中间。 -- **列表过密**:把一堆项目符号改成普通段落。只有真正适合列举的内容才用列表,比如步骤、参数、对比。 -- **英文标题式大写**:混排英文标题不要每个词都大写。子标题优先用句式大小写;主标题也不要动不动把每个词首字母都抬起来。 - -### 句式问题 - -- **“不是 X,而是 Y”**:这类句式不要当默认模板。能直接说结论,就直接说结论。 -- **空泛加强词**:删掉 `真正的`、`真的`、`事实上`、`坦白说`、`老实说`、`说清楚一点`、`值得一提`、`不得不说` 这类不提供信息的词。 -- **空泛背书**:删掉 `值得阅读`、`值得关注`、`值得一看`、`值得深入`、`值得留意` 之类模板,改成说明“为什么重要”。 -- **含糊缓和**:删掉 `也许`、`可能会`、`某种程度上`、`在某种意义上`、`可以说`、`大概` 这种只是在拖句子的词。 -- **缺桥句**:段落之间要接得上。两个段落如果互换顺序也不影响阅读,说明桥接不够。 -- **规则的三连**:少用固定的三分法和三连排比。可以用两项、四项,或者直接写成一句话。 - -### 常见替换词 - -这些词在中文里很容易把句子带向 AI 腔。单独出现不一定有问题,但一旦成堆,就该改。 - -#### 第一层 - 看到就优先改 - -| 替换掉 | 改成 | -|---|---| -| 深入探讨 / 深入分析 | 看看、拆开看、直接说 | -| 赋能 | 让、帮助、支持 | -| 协同 | 一起做、配合、合作 | -| 生态(比喻) | 系统、圈子、网络、市场 | -| 闭环 | 完整流程、收口、前后接上 | -| 落地 | 做出来、实施、用起来 | -| 破局 | 找到办法、打开局面 | -| 升维 | 提高、换个层次看 | -| 迭代 | 更新、再改 | -| 复盘 | 回看、重新梳理 | -| 抓手 | 切入口、方法、路径 | -| 颗粒度 | 细到什么程度 | -| 对齐 | 统一、一致 | -| 加持 | 帮助、增强 | -| 加码 | 增加、再加力 | -| 兜底 | 保底、最后处理 | -| 反哺 | 反过来帮助 | -| 下沉 | 往下放、推到一线 | -| 上行 | 往上走、反馈上去 | -| 彰显 | 说明、显示 | -| 呈现 | 写出、展开、显示 | -| 体现 | 说明、表现为 | -| 无缝 | 顺畅、直接、不用额外步骤 | -| 坐落于 | 在、位于 | -| 充满活力 | 活跃、有生气 | -| 蓬勃发展 | 增长、变活跃 | -| 显著 / 重大 / 重要 | 视情况改成具体事实、数字或对比 | - -#### 第二层 - 同段里出现 2 个以上就要警惕 - -| 替换掉 | 改成 | -|---|---| -| 进一步 | 再、更、继续 | -| 持续 / 不断 / 逐步 / 逐渐 | 只有在真有时间变化时才留 | -| 具体来说 / 具体而言 | 直接说具体点 | -| 总体上 / 整体上 | 如果只是转场,删掉 | -| 本质上 / 核心上 / 实际上 | 只有在真的要修正前一句时才用 | -| 某种程度上 / 某种意义上 | 改成明确判断 | -| 从某种角度看 | 直接说明这个角度是什么 | -| 在……背景下 | 先写事件,再补背景,或者直接删掉 | -| 这意味着 | 直接写结果 | -| 从而 / 进而 / 因此 | 改成更直接的因果句 | -| 与此同时 | 改成时间顺序或场景切换 | -| 也就是说 | 如果只是复述,就删 | -| 由此可见 | 只有在推理链真的需要时才留 | -| 值得注意的是 | 直接写值得注意的内容 | -| 显然 / 毋庸置疑 / 不难看出 | 直接证明,不要先喊结论 | -| 其实 / 事实上 / 当然 / 确实 | 只在语气真的需要时保留 | - -#### 第三层 - 只有在密度过高时才标 - -这些词本身不坏,问题在于 AI 太爱堆。要看密度,不看单次出现。 - -| 词语 | 处理方式 | -|---|---| -| 重要 / 关键 / 必要 | 用具体事实替代一部分 | -| 有效 / 高效 | 说清楚怎么有效 | -| 创新 / 创造性 | 说明新在哪里 | -| 动态 / 变化 | 写出变化本身 | -| 多元 / 多样 | 直接列出有哪些不同 | -| 复杂 / 细致 | 说明复杂在哪 | -| 前所未有 / 空前 | 说明它和之前有什么不同 | -| 卓越 / 非凡 / 出色 | 用标准、数字或例子支撑 | -| 世界级 / 顶级 / 最佳 | 除非能比,否则别喊 | - -### 模板短语 - -这些句式很像填空题。只要把空位换掉,整句还是一个味道,就要小心。 - -- `无论你是 X 还是 Y`:假装覆盖所有人,实际谁都没说清楚。 -- `如果你正在……`:只有在确实面向这个人群时才留。 -- `我最近有幸……`:社交文案和评论体里很常见,通常可以直接改成发生了什么。 -- `在……的大背景下`:太像论文开头,能删就删。 -- `当今这个……时代`:空泛,直接写具体情境。 -- `让我们来看看`、`下面我们来拆解`:如果只是过渡,删掉。 - -### 过渡短语 - -下面这些词常常只是为了把话接上,实际上没有推动内容: - -- `再者` -- `此外` -- `另外` -- `同时` -- `更重要的是` -- `与此同时` -- `在今天这个……语境里` -- `在一个……不断变化的时代里` -- `换句话说` -- `也就是说` -- `真正值得看的,是……` -- `最值得注意的是……` -- `更关键的是……` -- `总之` -- `综上` -- `最后` -- `概括来说` -- `一句话概括` -- `归根结底` -- `说到底` -- `说到` -- `值得一提的是` -- `不过` -- `话说回来` - -原则很简单:能用语序、动作顺序、场景切换说清楚,就不要靠过渡词硬连。 - -### 结构问题 - -- **段落长度太整齐**:每段都差不多长,文本会像模板。要故意拉开差异。 -- **开头太像引言**:不要先铺一大段背景再说重点。能先说结论就先说结论。 -- **过度清洗**:把所有不规整的地方都磨平,反而更像 AI。保留一点自然断裂、短句、转折和语气变化。 -- **三段式循环**:如果连续几段都是“背景 -> 行动 -> 感受 -> 总结”,要打散。 -- **可以删一半仍成立**:如果一句话删掉一半意思还在,通常就该删。 - -### 何时该整段重写 - -如果一个文本同时满足下面三条,通常不要再做词语级修补,应该整段重写: - -1. 词表命中已经超过 5 处,且分布在多个类别里。 -2. 结构模式已经触发 3 类以上,比如概念先行、章尾总结、过渡词过密、段落节奏过于均匀。 -3. 朗读起来还是像模板,像说明书,像自动生成稿,而不像人写的东西。 - -这时候,先把核心意思压成一句,再从场面、动作、关系或事实重新搭。 - -### 内容模式 - -下面这些是最重要的内容级判断。它们不只属于通用文案,也同样会污染小说、评论、摘要、项目说明和创作笔记。 - -- **意义膨胀**:把一件普通事写成“转折点”“里程碑”“历史性时刻”。如果事件本身不够大,就别先把它吹大。 -- **无来源背书**:`专家认为`、`研究表明`、`很多人觉得`、`业内人士指出`。没有来源就不要拿权威口气顶上去。 -- **宣传腔**:`充满活力`、`蓬勃发展`、`创新生态`、`未来可期`。如果这段不是广告,就别装广告。 -- **伪分析**:把动作写成 `象征着`、`反映着`、`展示着` 这种连续解释,或者中文里一连串“体现着、象征着、说明着”。这类句子常常有内容,但没有新信息。 -- **套路化挑战**:`尽管面临挑战,仍然蓬勃发展`、`在逆风中保持韧性`。先说具体挑战,再说具体应对,不然就是空句。 -- **空泛范围**:`从……到……`、`从古代文明到现代创业`、`从大爆炸到暗物质`。看着很宽,其实什么都没说。 -- **新颖性膨胀**:`他提出了一个概念`、`她发明了一个说法`、`这是一个没人注意到的问题`。大多数语境里,这类“新”都不新。 -- **情绪标签代替展示**:`我很震惊`、`我很感动`、`一种复杂的情绪涌上心头`、`难以言喻的感觉`。如果是真的情绪,写动作、停顿、视线、呼吸和物件。 -- **假平衡让步**:`虽然 X 很好,但 Y 仍然是问题`。如果两边都空,就只是姿态。要么具体让步,要么直接表态。 -- **反问句开头**:`这意味着什么?`、`为什么这很重要?`、`接下来会怎样?`。如果你已经知道答案,就直接说答案。 -- **括号式缓和**:`(更准确地说)`、`(尤其是)`、`(某种程度上)`。如果括号里的信息重要,就拆成独立句。 -- **编号列表膨胀**:`三个关键点`、`五个理由`、`七件事`。只有真的有这么多并列项时,数字才成立。 -- **推理链泄漏**:`让我们一步步来看`、`先考虑这个,再考虑那个`、`我的思路是`。这种内部推理痕迹不该直接出现在成稿里。 -- **阿谀语气**:`好问题`、`你说得对`、`很有洞察力`。这是聊天界面的习惯,不是写作需要。 -- **回答前复述**:`你问的是……`、`你是在问……`。直接答,不要先重复问题。 -- **置信度词堆叠**:`值得注意的是`、`有趣的是`、`显然`、`当然`、`毋庸置疑`。如果事实本身够强,就别再给它戴情绪标签。 -- **过度结构**:短文里塞太多标题、子标题和列点。结构要服务内容,不要替内容撑门面。 -- **节奏过于均匀**:每句都差不多长,每段都差不多重。机器最爱这种节奏,读者通常也最容易累。 -- **过度抛光**:把所有口气、停顿、碎裂感都磨掉,文字会变平。人写的东西本来就不会完全整齐。 - -#### 中文高危词 - -如果文本里出现下面这些词,先别急着夸,先看它们是不是在偷懒: - -| 替换掉 | 改成 | -|---|---| -| 深入探讨 / 深入分析 / 深挖 | 看看、拆开看、直接写 | -| 领域化大比喻 | 领域、范围、场景、局面、市场 | -| 织锦式比喻 | 直接描述复杂关系 | -| 范式 | 模式、方法、框架 | -| 启程 | 开始、起步 | -| 灯塔式比喻 | 直接重写,不要空喊意义 | -| 证明 / 佐证 / 显示 | 说明、表明、写出 | -| 稳健 / 可靠 / 全面 | 说清楚具体强在哪 | -| 前沿 / 先进 / 最新 | 说清楚到底新在哪里 | -| 借力 / 利用 | 用、借助、调用 | -| 关键 / 重要 / 核心 | 补上具体事实 | -| 强调 / 突出 / 彰显 | 只在有具体内容时用 | -| 细致 / 精细 / 谨慎 | 说清楚具体细在哪 | -| 顺滑 / 无缝 / 流畅 | 说清楚哪里不用额外步骤 | -| 颠覆 / 变革 / 转折点 | 说清楚到底变了什么 | -| 直接打中 / 很有感觉 | 直接说变化,不要空喊 | -| 使得 / 让 / 令 | 先换回人或物,再说动作 | -| 开始 / 继续 / 逐步 / 逐渐 | 只有真有时间变化时才留 | -| 充满活力 / 蓬勃发展 / 热闹 | 说清楚活在哪里、热在哪里 | -| 展示 / 呈现 / 体现 | 写出、说明、表现为 | -| 深挖 / 拆解 | 看看、解释、展开 | -| 忙碌 / 喧闹 / 热闹 | 说清楚忙在哪里 | -| 复杂 / 细致 / 繁复 | 说清楚复杂在哪里 | -| 持续变化说法 | 只在真有变化时留 | -| 长期持续说法 | 说清楚持续了多久 | -| 困难说法 | 说清楚难在哪里 | -| 整体化说法 | 直接写完整范围 | -| 可执行 | 说清楚具体怎么做 | -| 有影响力 | 说清楚具体影响 | -| 收获 | 说清楚得到了什么结论 | -| 专家背书 | 用真实身份或真实来源 | -| 最佳实践 | 说清楚什么做法有效 | -| 本质上 | 能删就删 | -| 协同 | 说清楚实际配合效果 | -| 互动 | 说清楚具体关系 | -| 为了 | 简化成“为”“去”“让” | -| 因为 | 直接写因为 | -| 充当 | 直接写“是”或“做什么” | -| 拥有 / 包含 / 呈现 | 直接写“有”“包括”“写出” | -| 开始 | 直接写“开始”就够 | -| 查明 | 查出、确定、弄清 | -| 努力 | 试、做、想办法 | -| 热衷 | 直接说喜欢或想做 | -| 交响式比喻 | 直接描述协调关系 | -| 拥抱式比喻 | 采用、接受、使用 | - -#### 中文辅助词 - -这些词单独不是坏词,但成簇时很容易把文字推向 AI 腔: - -| 替换掉 | 改成 | -|---|---| -| 驾驭 / 导航 | 处理、应对、工作方式 | -| 培养 / 激发 / 促进 | 说清楚具体动作 | -| 提升 / 升高 / 增强 | 说清楚提升了什么 | -| 释放 / 展开 / 打开 | 说清楚打开了什么 | -| 简化 / 顺手化 | 说清楚省掉了哪一步 | -| 赋能 | 让、支持、帮忙 | -| 加强 / 支撑 / 补强 | 说清楚补强了什么 | -| 带头 / 领跑 | 说清楚谁在做什么 | -| 引发共鸣 | 说清楚为什么会共鸣 | -| 彻底改变 | 说清楚改了哪些地方 | -| 促成 / 促使 | 说清楚谁做了什么 | -| 支撑 / 承托 | 说清楚支撑什么 | -| 细腻 / 细密 | 说清楚细在哪 | -| 多面 / 多元 / 多样 | 直接列出有哪些面 | -| 系统 / 生态 / 网络 | 说清楚到底是什么关系网 | -| 众多 / 大量 / 诸多 | 能给数就给数 | -| 涵盖 / 包括 / 兼顾 | 说清楚具体包括什么 | -| 催化 / 推动 | 说清楚推动了什么 | -| 重构 / 重塑 | 说清楚怎么重了 | -| 补充 / 补强 / 追加 | 说清楚补了什么 | -| 阐明 / 说明 / 解释 | 直接把话说透 | -| 对照 / 对比 / 并置 | 说清楚比的是什么 | -| 变革 / 范式转移 / 结构性变化 | 说清楚具体变化 | -| 基石 / 关键支点 / 核心支撑 | 说清楚它起什么作用 | -| 至关重要 / 绝对重要 | 说清楚为什么重要 | -| 准备就绪 / 已经到位 | 说清楚哪一步完成了 | -| 萌发 / 萌芽 | 说清楚具体长出来什么 | -| 典型 / 典范 / 经典 | 说清楚为什么典型 | -| 总体 / 上位 / 宏观 | 如果只是转场,就删 | -| 底座 / 依托 / 依凭 | 说清楚谁支撑谁 | - -#### 中文模板句 - -- `无论你是 X 还是 Y`:假装覆盖所有人,实际谁都没说清楚。 -- `我最近有幸……`:社交文案里常见,通常可以直接改成发生了什么。 -- `一小步,却是……的一大步`:把结论说满了,改成具体变化。 -- `在……的大背景下`:太像论文开头,能删就删。 -- `当今这个……时代`:空泛,直接写具体情境。 -- `下面我们来看看`、`接下来我们拆一下`:如果只是过渡,删掉。 -- `从……到……`:如果只是拼范围,就改成真正相关的范围。 -- `如果你正在……`:只有确实面向这个人群时才留。 - -放到小说里,下面几类尤其危险: - -- **概念先行**:先讲“这代表什么”“这意味着什么”,再写人物和场面。小说通常要先给人、物、动作和压力,再让意义自己长出来。 -- **作者总结句**:段尾硬收成“这一刻他明白了”“这说明了一切”“从此故事不一样了”。这类句子会把叙事替换成判词。 -- **对白书面腔**:人物开口像在写说明文、陈述意见、交代背景。对白要先服从身份、关系、体力和当下压力,再谈信息密度。 -- **声线打平**:所有人物都说一个样。技能很容易把角色写成同一个“会解释的人”。 -- **情绪标签代替展示**:`复杂的情绪`、`五味杂陈`、`难以言喻的感觉`、`一种说不清的压迫感`。如果情绪是真的,写动作、停顿、视线、手势、呼吸和物件。 -- **章首背景灌输**:每一章都先解释设定、关系和历史,再往下走。小说应该先让场面站起来。 -- **章尾模板化**:不是总收在“他意识到事情不对了”,就是总收在一个空泛钩子上。更好的收口通常是动作、物件、未完成的话、迟到的确认。 -- **意象堆砌但不落地**:句子看着像文学,实际没有人物、动作、空间和时间压力。意象要服务场景,不要替场景站岗。 -- **信息重复复述**:同一章里反复交代同一件事,只是换说法。小说不是提纲扩写。 -- **统一口气的作者声**:旁白、人物、制度、解释都说成一种“稳重而正确”的声音。那通常是 AI 最容易露馅的地方。 - ---- - -## 输出格式 - -### rewrite 模式 - -按以下四部分返回: - -**1. 问题清单** -列出发现的每一处 AI 痕迹,并引用原文中的具体文本。 - -**2. 重写版本** -给出完整重写后的内容。保留原意、原结构和必要事实,只改必须改的地方。 - -**3. 改动摘要** -简要说明你做了哪些主要修改,不用逐词解释。 - -**4. 二次审读** -重新读一遍第 2 部分的重写结果,找出第一次重写后仍然残留的 AI 痕迹,比如重复过渡、空泛加强、词表残留、结构太整齐等。把修正后的文本直接贴出来,并说明这次又改了什么。如果已经干净,就直接说清楚。 - -### detect 模式 - -按以下两部分返回: - -**1. 问题清单** -列出发现的每一处 AI 痕迹,并引用原文中的具体文本。按严重度分组:P0、P1、P2。 - -**2. 判断** -对每一条标记说明它是明确问题,还是在当前语境里也可能成立的写法。并不是所有 AI 相关模式都该删,有些只是要看场景。若文本很干净,也要直接说明。 - ---- - -## 严重度分层 - -不是所有 AI 痕迹都一样。快速扫文时,按严重度优先处理。 - -### P0 - 可信度杀手,立刻修 - -- 回避式免责声明,比如“我目前无法访问实时信息” -- 聊天机器人口头禅,比如“希望这对你有帮助”“如果你愿意,我还可以……” -- 没有来源的空泛归因,比如“专家认为”“研究表明” -- 把平常事件吹成大事的意义膨胀,比如“里程碑”“转折点” - -### P1 - 明显 AI 味,发布前修 - -- 词表里的高风险词 -- 模板短语和空位填充句 -- `让我们`、`下面我们` 之类的假协作开头 -- 同一段里反复换同义词 -- 套路化开场,比如“在……快速发展的今天” -- 加粗过多 -- 破折号太多 - -### P2 - 风格层修整,有空再修 - -- 空泛结尾,比如“未来值得期待”“时间会给出答案” -- 规则的三连 -- 段落长度太整齐 -- 避免系动词,硬写成 `扮演着`、`充当着`、`提供着` 这类绕法 -- 过渡词过密,比如 `此外`、`另外`、`同时` - -做快速审读时,先看 P0 和 P1。完整审读再看三层。 - ---- - -## 自引导豁免 - -当你在写**关于** AI 写作模式的内容时,引用里的示例、代码块里的示例,或者明确标注为“例如”“比如”的演示文本,不要当成问题去改。 - -只标记作者自己的正文,不要改被引用的坏例子。 - ---- - -## 语境档位 - -可以给一个可选的语境提示,用来调节严格程度。如果没有提示,就根据内容自动判断: - -- 短文本 + 话题标签 / 提及,通常是 `linkedin` -- 有代码块、接口、架构说明,通常是 `technical-blog` -- 有称呼和融资、营收、增长语言,通常是 `investor-email` -- 有步骤说明、参数、说明文档结构,通常是 `docs` -- 没有明显信号,就默认 `blog` - -### 档位定义 - -**`linkedin`** -短社交文案。可以更短、更碎,但仍然要具体。 - -**`blog`** -默认模式。长文标准,规则全开。 - -**`technical-blog`** -技术文章、架构说明、接口文档。术语有正常技术意义时,不要因为“像营销词”就误杀。 - -**`investor-email`** -高信任对象。宣传腔是最大风险,必须更紧。 - -**`docs`** -文档、说明书、指南。清晰度优先于文风。 - -**`casual`** -聊天、内部备注、快速回复。只抓最明显的问题。 - -### 小说档位 - -如果内容是虚构文学,优先在下面两个档位里判断,而不是机械套用 `blog`。 - -**`literary-fiction`** -严肃文学、现实主义、实验叙事、慢节奏人物小说。这个档位更看重声线、留白、语气和场面压力,也更容忍句法弹性和局部碎片感。它不容忍的是解释欲、结论癖和修辞整齐到像模板。 - -**`web-fiction`** -网络文学、连载小说、类型小说、强推进叙事。这个档位更看重节奏、冲突、钩子和信息清晰度,也更容忍直接表达、明确目标和高密度情节推进。它不容忍的是模板化开头、重复钩子和一章一个套路骨架。 - -如果没有明确提示,而内容明显是小说: - -- 连载感强、章节钩子重、目标推进快,优先判成 `web-fiction` -- 语气更克制、留白更多、人物声音更重要,优先判成 `literary-fiction` - -### 容忍矩阵 - -未列出的规则,默认都按默认强度处理。 - -| 规则 | linkedin | blog | technical-blog | investor-email | docs | casual | -|---|---|---|---|---|---|---| -| 破折号 | 放宽 | 严格 | 严格 | 严格 | 放宽 | 跳过 | -| 加粗过量 | 放宽 | 严格 | 严格 | 严格 | 放宽 | 跳过 | -| 标题里的 emoji | 放宽 | 严格 | 严格 | 严格 | 跳过 | 跳过 | -| 列表过密 | 跳过 | 严格 | 较宽松 | 严格 | 跳过 | 跳过 | -| 含糊缓和 | 严格 | 严格 | 较宽松 | 严格 | 较宽松 | 跳过 | -| 词表 | 严格 | 严格 | 部分放宽 | 严格 | 放宽 | 只看 P0 | -| 宣传腔 | 放宽 | 严格 | 严格 | 更严格 | 严格 | 跳过 | -| 意义膨胀 | 严格 | 严格 | 严格 | 更严格 | 放宽 | 跳过 | -| 过度避开系动词 | 跳过 | 严格 | 较宽松 | 严格 | 跳过 | 跳过 | -| 段落长度统一 | 跳过 | 严格 | 严格 | 严格 | 放宽 | 跳过 | -| 规则的三连 | 放宽 | 严格 | 较宽松 | 严格 | 跳过 | 跳过 | -| 反问句开头 | 放宽 | 严格 | 严格 | 严格 | 严格 | 跳过 | -| 过渡词 | 跳过 | 严格 | 严格 | 严格 | 放宽 | 跳过 | -| 空泛结尾 | 跳过 | 严格 | 严格 | 更严格 | 跳过 | 跳过 | - -小说不因为是文学文本就豁免这些规则。只是在具体判断时,要按对应档位看声线、节奏、冲突和收口。 - -### 技术文档例外 - -在技术语境里,下面这些词有时有正常含义,不要机械误杀: - -- `robust` -- `comprehensive` -- `seamless` -- `ecosystem` -- `leverage` -- `facilitate` -- `underpin` -- `streamline` - -但下列词依然要盯紧: - -- `delve` -- `tapestry` -- `beacon` -- `embark` -- `testament to` -- `game-changer` -- `harness` - -### 自动识别线索 - -当没有给语境提示时,按这些线索推断: - -| 线索 | 推断语境 | -|---|---| -| 300 字以内,且有话题标签或提及 | `linkedin` | -| 有代码块、接口、架构说明 | `technical-blog` | -| 有称呼,且出现融资、增长、营收等词 | `investor-email` | -| 有步骤、参数、说明文档结构 | `docs` | -| 连载节奏强、章尾钩子明显、目标推进快 | `web-fiction` | -| 留白多、声线重、句法更弹性、人物内里更重要 | `literary-fiction` | -| 没有明显信号 | `blog` | - -如果自动判断不对,就直接说明你采用了哪个档位,以及为什么。 - ---- - -## 主题校准 - -目标是写出像人写的东西。直接、具体、有节奏。 - -五条原则: - -1. **句长要变**:短句和长句混着来。碎句也可以。 -2. **要具体**:把空泛判断换成数字、名字、日期、例子。 -3. **要有声音**:如果语境允许,可以用第一人称,明确偏好和反应。 -4. **别太中性**:如果文章本来就该表态,就直接表态。 -5. **强调要赚到**:不要只说“这很重要”,要让读者看出为什么重要。 - -如果原文已经很好,就只做必要的删改,不要为了“清得更干净”反而把文字修死。替换表只是默认值,不是硬性命令;如果某个词在语境里就是最准确的,保留它。 diff --git a/.claude/skills b/.claude/skills deleted file mode 120000 index 2b7a412..0000000 --- a/.claude/skills +++ /dev/null @@ -1 +0,0 @@ -../.agents/skills \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index e82b1c6..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Publish GitHub Pages - -on: - push: - branches: - - master - -permissions: - contents: read - pages: write - id-token: write - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v2.1.2 - - run: npm ci - - - name: Build - run: npm run build - - - name: Setup Pages - uses: actions/configure-pages@v5 - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: './docs/.vitepress/dist' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b720dba..0000000 --- a/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -_book/ - -node_modules/ - -dist/ - -.vuepress/dist/ -docs/.vitepress/dist/ - -*.log - -docs/.vitepress/cache - -.DS_Store -Thumbs.db diff --git a/docs/public/.nojekyll b/.nojekyll similarity index 100% rename from docs/public/.nojekyll rename to .nojekyll diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 06b60ee..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "recommendations": [ - "davidanson.vscode-markdownlint", - "yzhang.markdown-all-in-one" - ] -} \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 0000000..a8d53f0 --- /dev/null +++ b/404.html @@ -0,0 +1,21 @@ + + + + + + 404 | 编程导引 + + + + + + + + + +
Skip to content

404

PAGE NOT FOUND

But if you don't change your direction, and if you keep looking, you may end up where you are heading.
+ + + + \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 29cc1fa..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,74 +0,0 @@ -# 写作风格指南 - -以下规则用于 AI 在本书中撰写或改写章节内容,保持与现有章节一致的写作风格与结构。 - -本项目包含两个教程: -- **编程导引**(docs/chapters)——JavaScript 编程入门 -- **Agentic AI**(docs/agentic-ai)——AI 协作与 Agent 实践 - -## 语气与叙述 - -- 友好、耐心、口语化、引导式,但不幼稚、不俯视读者,也不把读者当成小孩子。 -- 常用提问引导思考(例如"为什么会这样?""那该怎么办?"),语气保持专业与克制。 -- 先给直观解释,再给更严格的概念或结构化表述。 -- 允许轻量自我指代/引导(如"我们可以看到""我们先看一个例子")。 -- 不用类比来解释概念。直接用清晰的语言把概念说清楚,让一般读者也能理解。 - -## 概念呈现方式 - -- 先直觉,再进入正式概念或定义。 -- 即便解释口语化,也尽量给出标准、严谨的术语和定义,避免误导。 -- 中英术语并列出现,格式常为"中文(English)"或"中文 English"。 -- 不堆砌概念,控制密度与节奏。 -- 不用"短句总结"的固定模板。要点在正文中自然强调即可。 -- 加粗要克制。每个段落最多保留一个强调点,不要每个关键词都加粗。 - -## 章节结构与节奏 - -- 使用清晰的 Markdown 标题层级(# 章节、## 小节、### 子节)。 -- 每个小节围绕一个明确问题或概念展开。 -- 示例与解释交替出现,避免纯概念堆叠。 -- 常见收尾结构: - - "小结"(每条控制在一句话以内) - - "练习"(列表形式,偏可操作,一到四条为宜) - - "延伸阅读"(链接到 reference 目录) - -## 示例与代码 - -- 以 JavaScript 为主,必要时可以用伪代码或其他语言对比,但需明确标注。 -- 代码块短小、可直接运行,示例名称尽量直观。 -- 代码前后给出简短解释,强调"为什么这么写/会发生什么"。 -- 允许用数学公式(LaTeX)辅助说明,但不应喧宾夺主。 - -## 格式与排版 - -- 列表常用无序列表(* 或 -),用于"要点/原因/步骤"。 -- 提醒/注释常用引用块(> 提醒:...)。 -- 强调差异或对比时使用短段落分隔。 - -## 读者假设 - -- 默认读者为零基础或初学者,尽量减少跳步。 -- 遇到"容易误解/容易出错"的点要显式提醒。 -- 不制造焦虑,适当安抚读者,明确展示学习进步与里程碑。 - -## 链接与引用 - -- "延伸阅读"集中链接到 `docs/reference`(或其子目录)下的概念条目。 -- 章节主体尽量少外链;必要外链放在首次出现处,且简短。 - -## 练习风格 - -- 练习题偏"动手写/动手改",一到四条为宜。 -- 明确引导 Learn-by-doing,鼓励多动手、多尝试。 -- 引导发散思维,不局限于教程中给定的例子或解法。 -- 题目表述清晰,避免过度开放。 -- 与本章关键概念直接相关。 - -## Agentic AI 教程特有约定 - -- 核心概念(Prompting 基础、对齐定律、不要焦虑、技能>工具)在多个章节反复出现,但每次侧重不同,不做简单重复。 -- 反模式和最佳实践分散到各章节的相关位置讲,不在开头集中灌输。 -- 技术概念(MCP、Gateway、Memory 等)点到为止,配合一两个例子,不深入协议细节。 -- 动手练习不给具体代码,只描述实现步骤和目标,让读者自己写。 -- 附录(reference/)放技术细节较深的内容(RAG、API 调用),正文只讲原理和概念。 diff --git a/AGENTS_2026.md b/AGENTS_2026.md deleted file mode 100644 index 199df72..0000000 --- a/AGENTS_2026.md +++ /dev/null @@ -1,200 +0,0 @@ ---- -title: agents 2026 ---- - -# 0 - Disclaimer - -1. 以下内容完全是我**个人经验**,充斥着大量**主观呓语**。有不同观点欢迎交流。 -2. 原本这是要拆成 4 篇的内容,我最终决定还是编集在一起发出来,这样内容上也完备一些。文章不包含任何推广内容,也避免过分使用各种营销概念。 -3. 正文部分没有使用任何 AI 工具辅助创作,所有内容都是我手动输入的,图片由 GPT Image 2 生成。 -4. 按照“审阅即过时”原则(Reviewing expires the document),本文内容不承诺任何时效性。 - -# 1 - Utilization is all you need - -2023 年以前,互联网冲浪的一个顶尖技能就是快速利用搜索引擎找到自己需要的信息。我自诩自己还算是擅长这一点,但在 2023 年以后 ChatGPT 代表的大模型出现抹平了这个差距。尽管犄角旮旯里的东西利用搜索引擎还是有价值的,大部分通用知识都可以通过大模型应用来获取了——当然了在这之上其实加强了另外一个技能的要求,即分辨信息真伪的能力。早些年互联网信息泛滥的情况下虽然也充斥着伪造的信息,但相对于大模型凭借幻觉一本正经地现场直编的内容要容易分辨很多。 - -在现在这么一个 AI 的时代应该怎么做才能不落后呢? - -不止一个人这么问过我,或者跟我聊过类似的问题。 - -这些问题包括并不限于: - -- AI 能拿来干什么,怎么与其交互, -- 如何利用它进行二次开发, -- 如何使用 Coding Agent, -- 如何安装和设置龙虾, -- X 是什么,为什么会 Y…… - -数不清的细节。甚至有些问题我都还没了解过。 - -但我通常回复的方式就是,去用就好。因为这也是我长久以来的实践方式。我可能不是第一批拥抱 LLM 的人,但根据身边的统计学来看也是比较少数能够较快接触到新的东西然后上手用的人。 - -我后来直接把这段话置顶在了微博上: - -![Utilization is all you need](https://files.mdnice.com/user/3911/2aba39ea-2ba6-4287-a548-e454c0b3a770.png) - -从 2023 年年初到现在的三年多的时间里,大模型看似进化了非常多,但实际其核心的东西并没有太多根本性的演进。虽然技术层面上花式变化,从 ReAct 到 Chain of Thought,从 RAG 到 tool use、到 Model Context Protocol,从 prompting 到 skills、到 harness engineering。但是纯粹的日常工作这些花里胡哨的技术并不能帮到你太多,反倒是需要落地到具体的应用场景上才有真正的施展空间。 - -当然了,徒增焦虑的事情还是有的,去年的 ~~sipdick~~deepseek、今年的龙虾。我不知道多少人因为这些东西被忽悠,甚至是付费体验,然后发现毫无卵用了。 - -我跟很多朋友一起聊天以后得到了一个还算是共识的结论,我称之为**升级对齐定律**,或者说**升级归零定律**。一旦某个底模升级,或者是出现新的应用场景,那大家立马被拉到同一个起跑线上,之前的很多玩法全部归零。当年我们在 Stable Diffusion 2 时代需要花式训练 LoRA、需要到处搜寻和转换提示词,玩得更花的话需要 inpainting 等更花哨的能力——而现在,我只要一句话就能做到我想要的东西。 - -![“生成一张雷军代言捷豹新款电动跑车的图”](https://files.mdnice.com/user/3911/600b7672-c3c6-4974-b7a4-cc600cb02784.png) - -那是不是我们坐等着到了 AI 到那个地步就行了呢? - -我个人觉得**没问题**,但最好是现在立刻马上就上手使用。大部分的 Chat based 应用是免费的,做一些专业内容的应用一般也都有体验额度。这是当下这个时代的红利。至少日常应用来说,获取信息的效率和文案创作能力能提升很多,在这之上稍微扩展一下就能加速日常工作效率。 - -# 2 - Nondeterminism considered harmful - -一直以来很多人都有一个观点,也就是 AI 替代程序员。 - -目前看来这件事儿好像还在往前推进。很多人似乎已经很轻松地就能通过 Agent 来构建应用、进行创作或者完成其他方面的任务了。 - -但是这个很多人是多大范围呢?从身边的统计学来看,我确实能观察到一大堆的案例。但是脱离了程序员或者软件工程团队这么一个特殊的群体,你会发现真正应用上了这堆东西的并不多。哪怕现在 AI 产品如雨后春笋一般冒出来,现在能够被充分利用发挥其效益的还是少之又少。 -核心问题始终都在于,AI 真正解决的问题并不是“替代程序员”,而是作为一个更加灵活的工作流编排工具。 - -只是恰好程序员手上的工作是最容易标准化和自动化的,也就最先迎接了这一波冲击。编程语言相对于自然语言来说更加形式化,也有非常完善和自动化的工具来让 Agent 驱动来进行工作,加上开源时代有足够丰富的语料来进行训练,自然就能成为最先被应用的领域。 - -当然 Agent 毕竟还是处理自然语言的,于是进一步地被开发出来通过 Long context prompting 来执行任务的能力,进而形成了 Agent Skill 这么一个标准的用法。你只要按照流程构建 Skill 文档,大部分 Agent 都可以按照你设计的流程执行。这一个标准看似只是把某些 prompt 连接在了一起,但其实是个相当强的能力,因为本身 Agent 训练的方式很多时候在传统工作流中需要大量逻辑或者组件来实现的东西,你只需要额外一两句话就可以完成,比如分成多 Agent 并行执行或者从某步骤开始重试。 - -但是这点上也是有另外的问题的,Agent 的底层毕竟还是基于深度学习,概率模型的底子决定了它在做很多事情的时候是不稳定的,既然你可以灵活地加入一些流程,那他当然也可以灵活地跳过甚至抛弃一些流程。因此在关键的工作流中,使用确定性的执行方式(代码)是必不可少的。 - -这其实是给我们呈现了这么两种不同的工作流,一种是传统的、确定性的、有一定处理门槛而同时可以被 Agent 辅助完成的确定性工作流 —— 编程。 - -而另一种就是全新的、**非确定性的**、所有人都可以直接参与的这种 Agentic 工作流。 - -后者是现在大模型时代给所有人的红利,但同时也意味着这是个埋很深的坑。因此对于关键的工作流,或者关键的工作流程,很多时候还是不能直接替代。就像目前的大部分 Agent 工具一样,还是得靠 TypeScript 或者 Rust 等来糊,Markdown 还没能完全替代编程语言。 - ---- - -当然非确定性并不一定完全是坏事儿,当你需要用它去做一些创造性工作或者去探索一些东西的时候,能够摆脱你自己的固有视角限制,发现一些新的观点和方向。当然这就又回到了我在前面的内容里说的点,这些东西是否有效、是否属实或者是否能够获取到大量受众,就需要看个人所掌握的工具和能力水平了。 - -回到文章最开头的那个现象。AI 能否替代程序员呢?我长期持有的观点始终是这个样子的:[这个行业最终会被淘汰](https://mp.weixin.qq.com/s/0rHwuygVmiPMPAoRdE1gZQ),但是绝对不是现在这些人吹嘘的“替代”。 - -随着 Agent 能力的增强其所综合应用编程工具和 Agentic workflow 的能力也会越来越强,编程绝对不再是程序员独有的能力。Agent 能做的事情应该是去自动化任何可以自动化的工作,而编程只是它可以利用的一个手段。所以拥有编程能力,至少借助 Agent 拥有编程能力的人会越来越多,这个角色会越来越不显眼,但是作为实现确定性执行引擎的基础,这部分工作是无法直接替代的。 - -我从三月份开始一直在尝试使用 Agent 写长篇小说,现在已经在番茄更新了三部,总共超过 100 万字的内容了,虽然本身流量一般但对我来说仍然很有成就感——去年我保持了 100 天连续更新,才总共产出了 10 万字,这种效率已经远胜我了。很多工作流在我看来只要稍作总结就能完全自动化。目前很多人还在糊各种花里胡哨的软件,其实是还沉浸在有了锤子看什么都是钉子的状态。当然对许多人来说这也是件好事儿——总得先用起来,知道它能做什么,再接下来才能更好地利用它。 - -# 3 - Agents made simple - -大部分动态编程语言都会提供一个 REPL,这个概念来自于 LISP,即 Read-Eval-Print Loop。 -有一个很形象地表示这到底是在干什么的伪代码: - -``` -(loop - (print - (eval - (read)))) -``` - -任何一个 Agent 其工作流也大概如此: - -- **用户输入**:接收用户需求、上下文、约束条件 -- **思考决策**:LLM 自主判断:直接回答 / 需要调用工具 -- **工具执行**:调用对应工具,拿到外部返回结果(数据、接口、检索内容等) -- **结果校验** - - 任务已满足需求 → 进入收尾 - - 信息不足 / 未完成 / 结果异常 → 回到步骤 2,**多轮迭代** -- **轮次限制熔断**:设置最大迭代轮数,避免死循环 - - 超限仍未完成:梳理当前进度、缺失信息、核心差距(Gap),如实告知用户 -- **最终输出**:整合全部信息,整理成自然语言结果回复用户 - -前文我类比了 Markdown(或者说 Agentic 工作流)与编程语言,可以这么说,Agent 就是重新发明了一个非确定性的 REPL(哪怕所谓 Ralph-Loop,本质也不过如此)。 - -在重新发明 REPL 之外,Agent 还重新发明了 **Scheduler** —— 这东西在操作系统、云平台已经被发明过两次了。我们前面的内容说过,Agent 无非就是个非确定的工作流执行器,如何根据用户定义的任务来调度、执行和管理这些工作流,就成了诸如 OpenClaw、Hermes 之类的工具要做的另外一件事情。 - -除此之外还有什么呢?没了。 - ---- - -任何在之上加入的东西都是花活——至少目前来看帮助不大。 - -一个例子比如 Multi Agent,稍微有点经验的人就知道按照工作流拆分多 Agent 这种事情有多蠢——如果相互之间都有上下文依赖,这种拆分方式只是徒然让流程拉长而且中间因为沟通不畅造成更多不稳定的问题——毕竟信息传递就会有概率丢失,更何况 Agent 本身就是非确定性的。反正现在的 Context 已经足够大到 1M 了,不是异常复杂的工作流几乎也用不了多大的。毕竟 Bill Gates 当年就说过,_640k ought to be enough for anybody_ (不是 - -![640k ought to be enough for anybody](https://files.mdnice.com/user/3911/b9fbb26b-e440-40a1-a522-e472f8c0b4a1.png) - -还有一个例子就是 Memory——简单来说,一个规划合理的项目结构完全不需要这东西,而简单的任务又根本不会使用多大的 context,所以这种花哨的东西在目前来看除了让 OpenClaw 等之类的 bot 记得你叫什么之外,没什么实质性的用处。我自己构建的 Agent suite 就完全抛弃了任何 memory 相关的内容,因为长期来看这东西除了一直增长最后把 context 搞成一团糟,完全不如让他自行使用 rg 直接在 workspace 里实时检索获取最新的一手信息。 - -可以看看某国产 Agent 的记忆有多糊: - -> 用户名为\*\*,现居上海,B2B SaaS 产品经理,正在为所负责产品规划「\*\*」新功能,目标用户为产品/设计/远程团队。已完成 5 次用户访谈,识别核心需求:简化操作、实时协作、集成链接到其他对象。团队配置 1 前端+1 后端工程师,全职 3 个月,计划 Q2 发布 MVP。竞品对标\*\*和\*\*,定位需比\*\*简单、比\*\*功能更丰富,需兼容现有实时同步架构,移动端支持不重要。同时以笔名\*\*创作\*\*小说三部曲,当前编辑第 71-79 章,作品围绕\*\*、\*\*两位主角以 POV 视角叙事,呈现\*\*家族兴衰。采用 SOUL.md 角色文档和记忆更新系统维持剧情连续性。此外处理财务文档工作,需产出多格式 Office 输出(PDF/Excel/PPT/Word)…… - -这种把用户当成俊福来对待的记忆系统真的完全没必要,除了污染上下文没有任何好处。 - ---- - -所以我觉得 Agent 这件事情上还是会回到所谓的**升级对齐定律**:当大模型基座的能力足够强,context 足够大,token 足够便宜的时候,Harness Engineering 也就成了摆设。所谓**一力降十会**,花活再多也不如一个量大管饱的 REPL 让大模型自己干稳妥。 - -# 4 - Prompt Engineering is dead, long live prompting - -让我们再把时间线拨回三年前。 - -当年我们还在用各种方式分享 Prompt 模板、Stable Diffusion 关键词、最后搞出来 Chain of Thought,然后 Model native reasoning 出现了,一切都不用再麻烦了。 - -为了接入工作流,最开始我们是用 prompt 来给示例要求输出结构化结果、然后是允许 prompt 一组 schema 让大模型输出结构化结果、然后是 tool use / tool call,然后是 model context protocol 来链接 Chat based 大模型软件和工具,最后完全进展到只通过 Markdown 和少量脚本来实现的 Agent Skill。 - -为了 Feed 更多的领域知识,最早我们做的是直接塞进 prompt,因为内容量太大于是有了 RAG,再之后是提供工具或者 MCP 让大模型主动检索,最后变成直接扔一个目录和 Playwright (Chrome) MCP 让 Agent 自行去获取信息。 - -这个系列里我几乎每一篇都在强调升级对齐定律,这里就不再特别说了。而我今天想说的是,到底哪些变了,哪些没变。 - -变了的是上下文——这里并不只是那个 8k-1M 的 token 容量,而是模型以及所有能够获取到的输入。 - -- 早期的模型能力不足,我们需要 Prompt Engineering 来编排模型能够理解的输入,得到我们预期的输出。而现在 reasoning 能力都已经是 model native 了,我们需要的操作只是把我们想要的东西告诉目标模型就 OK 了。 -- 早期的模型 context 不足,我们需要考虑提供合适的准确的信息就要精细控制上下文中填充的内容,需要 embedding 和 RAG,需要精细设计 tools 的输入输出来优化 token 占用。现在 128k 是入门基础,256k 是大部分主流模型能够做到的,少部分 SOTA 模型已经能够提供 1M 的 context。 -- 早期模型的参数量比较小,后期逐渐提升了训练规模,并且加入了很多针对性的优化,并且扩展了工具调用了 agent 能力,这种情况下的模型完全能够自动化地去完成相应任务。 -- 早期的应用只有一个单纯的 Chat UI,API 也只能支持 message 输入,tool use 能力增强以后逐渐出现了功能更加丰富的客户端以及 TUI 的 Agent,除了文本、文件上传和图片以外,这些工具可以直接访问你系统信息、读取文件系统、执行命令和访问网络。 -- 涉及到在线的大模型服务,这方面的能力也都在提升——可以检索网络信息、可以与某些开放的工具交互,然后 会根据你的历史消息记住一些东西——当然这件事情不一定是好事儿,就像我前面提过的,记忆系统现在相当不成熟,哪天你在 Gemini 问过一次脚气怎么治,接下来的聊天里他可能时不时地冒出来关心你的脚气问题。 - -这是变了的东西——大模型或者 Agent 的知识储备越来越丰富、获取信息的渠道越来越多、拿到的结果也越来越准确、能够一次性容纳更多的内容来获取更加全面的上下文。 - -没变的是什么呢?Prompting。 - -不是单纯的“Prompt”,而是一套 **Prompting 规则**。长期以来我们看 Prompt Engineering 好像是在如何提供更多的信息给到大模型让他来按照我们的意图生成内容,但其实这个思路一定程度上是反过来的。我们提供的各种信息,更多的是限制大模型不让他过度发散,而是使用我们预期的方式来完成任务——因为现有规模参数量的情况下,我们所能提供到的信息甚至不如它自己编造的丰富,因此让它们按照我们预期的方式来工作的办法就是提供**准确**、**清晰**而且**具体**的规则,让他们按照规则来做事情。 - -你会发现从 Prompt Engineering 开始我们其实就是在这样做,一直到现在。无论是 AGENTS.md 规范,还是 Agent Skills,还是其他什么花样的操作,其实都是在遵循同样和类似的原则。 - -说到这儿再插一个题外话,前面我说过记忆系统不靠谱,另外一个不靠谱的东西就是各种抽象技能——前面那一章 Nondeterminism considered harmful 之外我本来还想写一个 Abstraction considered harmful 来着,但是现在看这部分内容在本章里提一句就行了。如果一个 Skill 写得很抽象,其结果就是起不到任何作用,或者是与预期效果造成严重偏离 —— 以当前的情况来看,你等待 Agent 通过 SKILL 来进化还不如等待底座模型升级。 - -举个例子,我那个编写小说的 SKILL 并没有什么花哨的东西,就是具体如何构建大纲、角色、和编写小说的指导,也没有任何记忆系统或者所谓的什么抽象思考能力,但是每一篇小说在我这边都有一个严格的标准结构: - -``` - README.md - STYLEGUIDE.md - 00-intake/ - 01-brief/ - 02-research/ - 03-foundation/ - souls/ - 04-outline/ - 05-rules/ - 06-analysis-research/ - 07-writing/ - samples/ - chapters/ - 08-operations/ - logs/ - feedback/ - retrospectives/ - change-log/ - 09-assets/ - 10-publication/ - 11-quality/ - 99-archive/ - samples-YYYYMMDD/ - import-YYYYMMDD/ -``` - -有些人说这就是所谓的 Harness Engineering —— 其实归根结底还是 Prompting,某些东西不用过多的文本解释,因为 Agent 能够识别和理解项目结构,而只要你项目管理能力就能很好利用这点。 - -# 5 - Summary - -说这么多,我觉得重点就这么几条吧: - -- 抓住一切可以利用的机会和手段开始用起来各种 AI 工具和 Agent。 -- 不要焦虑,也不要过度追捧营销概念。 -- 尝试构建自己的工具、工作流和 SKILL,多了解和积累相关的经验。 -- 认清本质,接受技术升级带来的工作和生活方式改进。 - -以上。 - diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 120000 index 47dc3e3..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -AGENTS.md \ No newline at end of file diff --git a/docs/public/CNAME b/CNAME similarity index 100% rename from docs/public/CNAME rename to CNAME diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 649fb00..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,2 +0,0 @@ -# 贡献指南 - diff --git a/GLOSSARY.html b/GLOSSARY.html new file mode 100644 index 0000000..ab74560 --- /dev/null +++ b/GLOSSARY.html @@ -0,0 +1,24 @@ + + + + + + Glossary | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page
+ + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 2f244ac..0000000 --- a/LICENSE +++ /dev/null @@ -1,395 +0,0 @@ -Attribution 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - d. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - g. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - i. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's - License You apply must not prevent recipients of the Adapted - Material from complying with this Public License. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public -licenses. Notwithstanding, Creative Commons may elect to apply one of -its public licenses to material it publishes and in those instances -will be considered the “Licensor.” The text of the Creative Commons -public licenses is dedicated to the public domain under the CC0 Public -Domain Dedication. Except for the limited purpose of indicating that -material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the -public licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/README.md b/README.md deleted file mode 100644 index 100daf4..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Introduction to Programming - -A guide for anyone to be ready to programming in anything. diff --git a/SUMMARY.html b/SUMMARY.html new file mode 100644 index 0000000..e97db98 --- /dev/null +++ b/SUMMARY.html @@ -0,0 +1,24 @@ + + + + + + Table of contents | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page
+ + + + \ No newline at end of file diff --git a/assets/GLOSSARY.md.9584a1ea.js b/assets/GLOSSARY.md.9584a1ea.js new file mode 100644 index 0000000..ee42feb --- /dev/null +++ b/assets/GLOSSARY.md.9584a1ea.js @@ -0,0 +1 @@ +import{_ as a,c as s,o as t,z as e,a as o}from"./chunks/framework.41379913.js";const S=JSON.parse('{"title":"Glossary","description":"","frontmatter":{},"headers":[],"relativePath":"GLOSSARY.md"}'),r={name:"GLOSSARY.md"},c=e("h1",{id:"glossary",tabindex:"-1"},[o("Glossary "),e("a",{class:"header-anchor",href:"#glossary","aria-label":'Permalink to "Glossary"'},"​")],-1),n=[c];function l(d,i,_,p,h,m){return t(),s("div",null,n)}const G=a(r,[["render",l]]);export{S as __pageData,G as default}; diff --git a/assets/GLOSSARY.md.9584a1ea.lean.js b/assets/GLOSSARY.md.9584a1ea.lean.js new file mode 100644 index 0000000..ee42feb --- /dev/null +++ b/assets/GLOSSARY.md.9584a1ea.lean.js @@ -0,0 +1 @@ +import{_ as a,c as s,o as t,z as e,a as o}from"./chunks/framework.41379913.js";const S=JSON.parse('{"title":"Glossary","description":"","frontmatter":{},"headers":[],"relativePath":"GLOSSARY.md"}'),r={name:"GLOSSARY.md"},c=e("h1",{id:"glossary",tabindex:"-1"},[o("Glossary "),e("a",{class:"header-anchor",href:"#glossary","aria-label":'Permalink to "Glossary"'},"​")],-1),n=[c];function l(d,i,_,p,h,m){return t(),s("div",null,n)}const G=a(r,[["render",l]]);export{S as __pageData,G as default}; diff --git a/assets/SUMMARY.md.6e51fa23.js b/assets/SUMMARY.md.6e51fa23.js new file mode 100644 index 0000000..4f269a0 --- /dev/null +++ b/assets/SUMMARY.md.6e51fa23.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,O as l}from"./chunks/framework.41379913.js";const u=JSON.parse('{"title":"Table of contents","description":"","frontmatter":{},"headers":[],"relativePath":"SUMMARY.md"}'),c={name:"SUMMARY.md"},h=l('

Table of contents

',2),o=[h];function r(i,n,_,s,f,m){return a(),t("div",null,o)}const p=e(c,[["render",r]]);export{u as __pageData,p as default}; diff --git a/assets/SUMMARY.md.6e51fa23.lean.js b/assets/SUMMARY.md.6e51fa23.lean.js new file mode 100644 index 0000000..76746df --- /dev/null +++ b/assets/SUMMARY.md.6e51fa23.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,O as l}from"./chunks/framework.41379913.js";const u=JSON.parse('{"title":"Table of contents","description":"","frontmatter":{},"headers":[],"relativePath":"SUMMARY.md"}'),c={name:"SUMMARY.md"},h=l("",2),o=[h];function r(i,n,_,s,f,m){return a(),t("div",null,o)}const p=e(c,[["render",r]]);export{u as __pageData,p as default}; diff --git a/assets/app.ef6b4c6d.js b/assets/app.ef6b4c6d.js new file mode 100644 index 0000000..eed4404 --- /dev/null +++ b/assets/app.ef6b4c6d.js @@ -0,0 +1 @@ +import{d as p,K as s,a2 as i,u,p as c,k as l,a3 as d,a4 as f,a5 as m,a6 as h,a7 as A,a8 as g,a9 as P,aa as v,ab as y,ac as C,ad as w,ae as _,af as b,ag as E}from"./chunks/framework.41379913.js";import{t as R}from"./chunks/theme.0fe641a0.js";function r(e){if(e.extends){const a=r(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const n=r(R),D=p({name:"VitePressApp",setup(){const{site:e}=u();return c(()=>{l(()=>{document.documentElement.lang=e.value.lang,document.documentElement.dir=e.value.dir})}),d(),f(),m(),n.setup&&n.setup(),()=>h(n.Layout)}});async function O(){const e=T(),a=S();a.provide(A,e);const t=g(e.route);return a.provide(P,t),a.component("Content",v),a.component("ClientOnly",y),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),n.enhanceApp&&await n.enhanceApp({app:a,router:e,siteData:C}),{app:a,router:e,data:t}}function S(){return w(D)}function T(){let e=s,a;return _(t=>{let o=b(t);return e&&(a=o),(e||a===o)&&(o=o.replace(/\.js$/,".lean.js")),s&&(e=!1),E(()=>import(o),[])},n.NotFound)}s&&O().then(({app:e,router:a,data:t})=>{a.go().then(()=>{i(a.route,t.site),e.mount("#app")})});export{O as createApp}; diff --git a/assets/ch01_environment.md.ad374fcf.js b/assets/ch01_environment.md.ad374fcf.js new file mode 100644 index 0000000..ce47ec3 --- /dev/null +++ b/assets/ch01_environment.md.ad374fcf.js @@ -0,0 +1,12 @@ +import{_ as s,c as a,o as n,O as l}from"./chunks/framework.41379913.js";const h=JSON.parse('{"title":"环境","description":"简单介绍下JavaScript编程语言,以及对应的环境准备工作。","frontmatter":{"description":"简单介绍下JavaScript编程语言,以及对应的环境准备工作。"},"headers":[],"relativePath":"ch01_environment.md"}'),o={name:"ch01_environment.md"},p=l(`

环境

JavaScript

JavaScript是目前使用最广泛的编程语言之一。作为互联网和移动计算时代的基础支撑语言,从基本的生产力工具,到日常的泛娱乐平台,它活跃在每一次你浏览网站、使用App的过程中,提供基本的运算和流程保障。

JavaScript的诞生和发展也颇具传奇色彩,Brendan Eich在几天的时间内设计并实现了这门编程语言,影响了后面几十年的软件和技术的发展。如今几乎每一个设备都接入了JavaScript,深入到了我们生活的方方面面。作为一门工业级的编程语言,JavaScript由欧洲计算机制造商协会(ECMA)制定标准化的行为和定义,来规范这个语言的使用和发展方向。TC39技术委员会也一直在更新JavaScript的功能,让这个有二十多年历史的旧技术不断焕发新的活力。

JavaScript的环境触手可及。在电脑端你可以随时打开一个现代的浏览器,页面上右键菜单中选择“Inspect”或者“查看/检查元素”,选择“控制台(Console)”标签。如果你是在移动端或者浏览器不支持,也可以通过一些工具比如移动端Console来体验JavaScript控制台。

编写代码

当需要写大量代码组合使用的时候,你可以通过本地编辑Web页面的方式来编写JavaScript代码。JavaScript是Web端的基本编程语言,可以稳定地运行在大多数现代浏览器中。

通过文件编辑器创建下面的代码,命名为index.html

html
<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+    <meta charset="UTF-8">
+    <title>Introduction to Programming</title>
+</head>
+<body>
+    <script type="text/javascript">
+        console.log("hello world");
+    </script>
+</body>
+</html>

<script ...></script>之间,就是你编写JavaScript代码的地方。编写完成后,用浏览器打开(或者刷新)这个文件,就能看到对应的效果和执行内容。

比如上面的代码,就是在控制台打印“hello world”。

本书推荐使用Visual Studio Code作为基本的代码编辑工具。你可以在官网进行下载和安装。官网也有详细的使用教程。

MDN 中对浏览器开发者工具基本的工具软件有更为完整的介绍,可以参考。如果你想了解更多关于Web设计和开发的知识,MDN也有完备的文档供参考。

一切准备就绪的话,我们就开始吧。

`,14),t=[p];function e(r,c,D,F,i,y){return n(),a("div",null,t)}const _=s(o,[["render",e]]);export{h as __pageData,_ as default}; diff --git a/assets/ch01_environment.md.ad374fcf.lean.js b/assets/ch01_environment.md.ad374fcf.lean.js new file mode 100644 index 0000000..bef705f --- /dev/null +++ b/assets/ch01_environment.md.ad374fcf.lean.js @@ -0,0 +1 @@ +import{_ as s,c as a,o as n,O as l}from"./chunks/framework.41379913.js";const h=JSON.parse('{"title":"环境","description":"简单介绍下JavaScript编程语言,以及对应的环境准备工作。","frontmatter":{"description":"简单介绍下JavaScript编程语言,以及对应的环境准备工作。"},"headers":[],"relativePath":"ch01_environment.md"}'),o={name:"ch01_environment.md"},p=l("",14),t=[p];function e(r,c,D,F,i,y){return n(),a("div",null,t)}const _=s(o,[["render",e]]);export{h as __pageData,_ as default}; diff --git a/assets/ch02_computation.md.d79955df.js b/assets/ch02_computation.md.d79955df.js new file mode 100644 index 0000000..f63fc5a --- /dev/null +++ b/assets/ch02_computation.md.d79955df.js @@ -0,0 +1,13 @@ +import{_ as r,c as h,z as e,a as l,E as s,B as a,O as c,G as d,o as f}from"./chunks/framework.41379913.js";const Ls=JSON.parse('{"title":"计算","description":"计算机可以完成哪些基本运算?如何把运算进行组合?如何进行函数封装?如何实现递归?","frontmatter":{"description":"计算机可以完成哪些基本运算?如何把运算进行组合?如何进行函数封装?如何实现递归?"},"headers":[],"relativePath":"ch02_computation.md"}'),y={name:"ch02_computation.md"},x=c('

计算

编程语言是人与计算机打交道的一个重要工具。

计算机(Computer)是干嘛的?当然是计算(Computing)啦。

在浏览器的控制台输入以下代码:

javascript
1 + 1

是不是有看到浏览器在回应你:2。这里基本上是能够当成一个计算器用的。

没错,这就是计算嘛。

然而,有什么用呢?

计算

简(pian)单(mian)地讲,一切问题都能抽出来一套数学模型,同样能够抽出来的数学模型都可以转化成计算问题,然后让计算机来解决。

所以计算可以看成是整个程序设计的基础。同样像我们之前提到的,计算机,本身也就是拿来做计算的。

那么我们都可以做哪些计算呢?

',12),g={class:"katex"},A={class:"katex-mathml"},C=c('',1),T=c(`
javascript
function pow5(num) {
+    return num * num * num * num * num;
+}

比如pow5的意思就是求一个数的5次方。我们定义了一个函数,怎么用呢?

然后尝试下面的代码:

javascript
pow5(1)

嗯,没错,输出了我们期望的结果(1)。

组合

什么是组合呢?

想想我们学习函数和多项式的时候,

`,8),D={class:"katex-display"},F={class:"katex"},b={class:"katex-mathml"},k=c('',1),S={class:"katex-display"},v={class:"katex"},P={class:"katex-mathml"},V=c('',1),I={class:"katex"},z={class:"katex-mathml"},E=c('',1),N={class:"katex"},R={class:"katex-mathml"},w=c('',1),j={class:"katex"},q={class:"katex-mathml"},B=c('',1),$={class:"katex"},J={class:"katex-mathml"},O=c('',1),G={class:"katex-display"},H={class:"katex"},K={class:"katex-mathml"},L=c('',1),M=e("p",null,"即",-1),Q={class:"katex-display"},U={class:"katex"},W={class:"katex-mathml"},X=c('',1),Y=e("p",null,"好吧我编不下去了。总之展开来以后很丑的。",-1),Z={class:"katex"},ss={class:"katex-mathml"},as=c('',1),ls={class:"katex"},ns={class:"katex-mathml"},ts=c('',1),es=c(`

但是在JavaScript里面我们并不用担心这个问题。

比如我们有两个函数

javascript
function f(x) {
+    return x*x + 2*x + 1;
+}
+
+function g(x) {
+    return (x+3) * (x-5);
+}
`,3),ps={class:"katex"},cs={class:"katex-mathml"},os=c('',1),_s=c(`
javascript
function h(x) {
+    return f(g(x));
+}

或者你也可以不用专门去定义,在用到的时候直接用就可以:

javascript
f(g(5))
+// or
+h(5)

组合的意义

比如我们可以考虑一个更复杂的函数,

`,5),us={class:"katex-display"},ms={class:"katex"},ds={class:"katex-mathml"},is=c('',1),rs={class:"katex"},hs={class:"katex-mathml"},fs=c('',1),ys={class:"katex"},xs={class:"katex-mathml"},gs=c('',1),As={class:"katex-display"},Cs={class:"katex"},Ts={class:"katex-mathml"},Ds=c('',1),Fs={class:"katex"},bs={class:"katex-mathml"},ks=c('',1),Ss={class:"katex"},vs={class:"katex-mathml"},Ps=c('',1),Vs={class:"katex"},Is={class:"katex-mathml"},zs=c('',1),Es={class:"katex"},Ns={class:"katex-mathml"},Rs=c('',1),ws=e("code",null,"pow5",-1),js=c('

令人欣慰的是,组合帮我们保证了这个问题不会有这些重复。

当你计算pow5(3+1)的时候,JavaScript并不会去展开成(3+1)*(3+1)...*(3+1)这种形式,而是直接变成了pow5(4),最后的结果是1024。

也就是说,函数的每个自变量(编程语言里面我们会把他们叫做参数(argument))会先被求值,得到他们的值以后才会带入计算。

*请记住我们现在讨论的这个问题,后面会展开并且深入地探讨。

练习

',6);function qs(Bs,$s,Js,Os,Gs,Hs){const n=d("mo"),t=d("mi"),o=d("mrow"),_=d("annotation"),u=d("semantics"),m=d("math"),p=d("mn"),i=d("msup");return f(),h("div",null,[x,e("p",null,[l("当然普通的"),e("span",g,[e("span",A,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(n,null,{default:a(()=>[l("+")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(n,null,{default:a(()=>[l("×")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(n,null,{default:a(()=>[l("∗")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("÷")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,{mathvariant:"normal"},{default:a(()=>[l("/")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("+ - \\times(*) \\div(/)")]),_:1})]),_:1})]),_:1})]),C]),l("不用说,你还可以把一些复杂的计算规则定义成函数:")]),T,e("p",null,[e("span",D,[e("span",F,[e("span",b,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(i,null,{default:a(()=>[s(t,null,{default:a(()=>[l("x")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("f(x) = x^2 + 2x + 1 ")]),_:1})]),_:1})]),_:1})]),k])])]),e("p",null,[e("span",S,[e("span",v,[e("span",P,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("g(x) = (x+3)(x-5) ")]),_:1})]),_:1})]),_:1})]),V])])]),e("p",null,[l("然后我们要定义一个"),e("span",I,[e("span",z,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("h")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("h(x) = f(g(x))")]),_:1})]),_:1})]),_:1})]),E]),l(",那么定义出来的"),e("span",N,[e("span",R,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("h")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("h(x)")]),_:1})]),_:1})]),_:1})]),w]),l("长什么样呢?")]),e("p",null,[l("我们首先要把"),e("span",j,[e("span",q,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("g(x)")]),_:1})]),_:1})]),_:1})]),B]),l("代入"),e("span",$,[e("span",J,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("f(x)")]),_:1})]),_:1})]),_:1})]),O]),l(",")]),e("p",null,[e("span",G,[e("span",H,[e("span",K,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(i,null,{default:a(()=>[s(n,null,{default:a(()=>[l(")")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("f(g(x)) = g(x)^2 + 2g(x) + 1 ")]),_:1})]),_:1})]),_:1})]),L])])]),M,e("p",null,[e("span",Q,[e("span",U,[e("span",W,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(n,null,{default:a(()=>[l("[")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(i,null,{default:a(()=>[s(n,null,{default:a(()=>[l("]")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1}),s(n,null,{default:a(()=>[l("∗")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("f(g(x)) = [(x+3)(x-5)]^2 + 2 * (x+3)(x-5) + 1 ")]),_:1})]),_:1})]),_:1})]),X])])]),Y,e("p",null,[l("而且如果以后遇到"),e("span",Z,[e("span",ss,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("σ")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("z")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("\\sigma(x,y,z)")]),_:1})]),_:1})]),_:1})]),as]),l("跟"),e("span",ls,[e("span",ns,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("θ")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("a")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("b")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("c")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("\\theta(a,b,c)")]),_:1})]),_:1})]),_:1})]),ts]),l("组合的,或者是更多参数的函数组合,结果更是可想而知。")]),es,e("p",null,[l("那么我们的"),e("span",ps,[e("span",cs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("h")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("h(x)")]),_:1})]),_:1})]),_:1})]),os]),l("可以很简单的写成这个样子:")]),_s,e("p",null,[e("span",us,[e("span",ms,[e("span",ds,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("σ")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(i,null,{default:a(()=>[s(t,null,{default:a(()=>[l("x")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("∗")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("×")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("\\sigma(x,y) = x^2 + x*y + (x+y)\\times5 ")]),_:1})]),_:1})]),_:1})]),is])])]),e("p",null,[l("当 "),e("span",rs,[e("span",hs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("x, y")]),_:1})]),_:1})]),_:1})]),fs]),l(" 分别为 "),e("span",ys,[e("span",xs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("4x+1,5x-3")]),_:1})]),_:1})]),_:1})]),gs]),l("的时候。你可以试着自己去展开一下这个算式:")]),e("p",null,[e("span",As,[e("span",Cs,[e("span",Ts,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("σ")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(i,null,{default:a(()=>[s(n,null,{default:a(()=>[l(")")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("×")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("\\sigma(4x+1, 5x-3) = (4x+1)^2 + (4x+1)(5x-3)+(4x+1+5x-3)\\times 5 ")]),_:1})]),_:1})]),_:1})]),Ds])])]),e("p",null,[l("总之,无论他有多长,都会在算式里面充斥着"),e("span",Fs,[e("span",bs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("4x+1")]),_:1})]),_:1})]),_:1})]),ks]),l("和"),e("span",Ss,[e("span",vs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("5x-3")]),_:1})]),_:1})]),_:1})]),Ps]),l(",而且,计算机并没有那么聪明,所以只好按照特定的顺序一次次的计算。")]),e("p",null,[l("于是"),e("span",Vs,[e("span",Is,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("4x+1")]),_:1})]),_:1})]),_:1})]),zs]),l("被重复计算了三次,"),e("span",Es,[e("span",Ns,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("5x-3")]),_:1})]),_:1})]),_:1})]),Rs]),l("被重复计算了两次。对于"),ws,l("那种情况,更是会被求值5次。")]),js])}const Ms=r(y,[["render",qs]]);export{Ls as __pageData,Ms as default}; diff --git a/assets/ch02_computation.md.d79955df.lean.js b/assets/ch02_computation.md.d79955df.lean.js new file mode 100644 index 0000000..0487f19 --- /dev/null +++ b/assets/ch02_computation.md.d79955df.lean.js @@ -0,0 +1 @@ +import{_ as r,c as h,z as e,a as l,E as s,B as a,O as c,G as d,o as f}from"./chunks/framework.41379913.js";const Ls=JSON.parse('{"title":"计算","description":"计算机可以完成哪些基本运算?如何把运算进行组合?如何进行函数封装?如何实现递归?","frontmatter":{"description":"计算机可以完成哪些基本运算?如何把运算进行组合?如何进行函数封装?如何实现递归?"},"headers":[],"relativePath":"ch02_computation.md"}'),y={name:"ch02_computation.md"},x=c("",12),g={class:"katex"},A={class:"katex-mathml"},C=c("",1),T=c("",8),D={class:"katex-display"},F={class:"katex"},b={class:"katex-mathml"},k=c("",1),S={class:"katex-display"},v={class:"katex"},P={class:"katex-mathml"},V=c("",1),I={class:"katex"},z={class:"katex-mathml"},E=c("",1),N={class:"katex"},R={class:"katex-mathml"},w=c("",1),j={class:"katex"},q={class:"katex-mathml"},B=c("",1),$={class:"katex"},J={class:"katex-mathml"},O=c("",1),G={class:"katex-display"},H={class:"katex"},K={class:"katex-mathml"},L=c("",1),M=e("p",null,"即",-1),Q={class:"katex-display"},U={class:"katex"},W={class:"katex-mathml"},X=c("",1),Y=e("p",null,"好吧我编不下去了。总之展开来以后很丑的。",-1),Z={class:"katex"},ss={class:"katex-mathml"},as=c("",1),ls={class:"katex"},ns={class:"katex-mathml"},ts=c("",1),es=c("",3),ps={class:"katex"},cs={class:"katex-mathml"},os=c("",1),_s=c("",5),us={class:"katex-display"},ms={class:"katex"},ds={class:"katex-mathml"},is=c("",1),rs={class:"katex"},hs={class:"katex-mathml"},fs=c("",1),ys={class:"katex"},xs={class:"katex-mathml"},gs=c("",1),As={class:"katex-display"},Cs={class:"katex"},Ts={class:"katex-mathml"},Ds=c("",1),Fs={class:"katex"},bs={class:"katex-mathml"},ks=c("",1),Ss={class:"katex"},vs={class:"katex-mathml"},Ps=c("",1),Vs={class:"katex"},Is={class:"katex-mathml"},zs=c("",1),Es={class:"katex"},Ns={class:"katex-mathml"},Rs=c("",1),ws=e("code",null,"pow5",-1),js=c("",6);function qs(Bs,$s,Js,Os,Gs,Hs){const n=d("mo"),t=d("mi"),o=d("mrow"),_=d("annotation"),u=d("semantics"),m=d("math"),p=d("mn"),i=d("msup");return f(),h("div",null,[x,e("p",null,[l("当然普通的"),e("span",g,[e("span",A,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(n,null,{default:a(()=>[l("+")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(n,null,{default:a(()=>[l("×")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(n,null,{default:a(()=>[l("∗")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("÷")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,{mathvariant:"normal"},{default:a(()=>[l("/")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("+ - \\times(*) \\div(/)")]),_:1})]),_:1})]),_:1})]),C]),l("不用说,你还可以把一些复杂的计算规则定义成函数:")]),T,e("p",null,[e("span",D,[e("span",F,[e("span",b,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(i,null,{default:a(()=>[s(t,null,{default:a(()=>[l("x")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("f(x) = x^2 + 2x + 1 ")]),_:1})]),_:1})]),_:1})]),k])])]),e("p",null,[e("span",S,[e("span",v,[e("span",P,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("g(x) = (x+3)(x-5) ")]),_:1})]),_:1})]),_:1})]),V])])]),e("p",null,[l("然后我们要定义一个"),e("span",I,[e("span",z,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("h")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("h(x) = f(g(x))")]),_:1})]),_:1})]),_:1})]),E]),l(",那么定义出来的"),e("span",N,[e("span",R,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("h")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("h(x)")]),_:1})]),_:1})]),_:1})]),w]),l("长什么样呢?")]),e("p",null,[l("我们首先要把"),e("span",j,[e("span",q,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("g(x)")]),_:1})]),_:1})]),_:1})]),B]),l("代入"),e("span",$,[e("span",J,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("f(x)")]),_:1})]),_:1})]),_:1})]),O]),l(",")]),e("p",null,[e("span",G,[e("span",H,[e("span",K,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(i,null,{default:a(()=>[s(n,null,{default:a(()=>[l(")")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("f(g(x)) = g(x)^2 + 2g(x) + 1 ")]),_:1})]),_:1})]),_:1})]),L])])]),M,e("p",null,[e("span",Q,[e("span",U,[e("span",W,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("f")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("g")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(n,null,{default:a(()=>[l("[")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(i,null,{default:a(()=>[s(n,null,{default:a(()=>[l("]")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1}),s(n,null,{default:a(()=>[l("∗")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("f(g(x)) = [(x+3)(x-5)]^2 + 2 * (x+3)(x-5) + 1 ")]),_:1})]),_:1})]),_:1})]),X])])]),Y,e("p",null,[l("而且如果以后遇到"),e("span",Z,[e("span",ss,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("σ")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("z")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("\\sigma(x,y,z)")]),_:1})]),_:1})]),_:1})]),as]),l("跟"),e("span",ls,[e("span",ns,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("θ")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("a")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("b")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("c")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("\\theta(a,b,c)")]),_:1})]),_:1})]),_:1})]),ts]),l("组合的,或者是更多参数的函数组合,结果更是可想而知。")]),es,e("p",null,[l("那么我们的"),e("span",ps,[e("span",cs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("h")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("h(x)")]),_:1})]),_:1})]),_:1})]),os]),l("可以很简单的写成这个样子:")]),_s,e("p",null,[e("span",us,[e("span",ms,[e("span",ds,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("σ")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(i,null,{default:a(()=>[s(t,null,{default:a(()=>[l("x")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("∗")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("×")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("\\sigma(x,y) = x^2 + x*y + (x+y)\\times5 ")]),_:1})]),_:1})]),_:1})]),is])])]),e("p",null,[l("当 "),e("span",rs,[e("span",hs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(t,null,{default:a(()=>[l("y")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("x, y")]),_:1})]),_:1})]),_:1})]),fs]),l(" 分别为 "),e("span",ys,[e("span",xs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("4x+1,5x-3")]),_:1})]),_:1})]),_:1})]),gs]),l("的时候。你可以试着自己去展开一下这个算式:")]),e("p",null,[e("span",As,[e("span",Cs,[e("span",Ts,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(t,null,{default:a(()=>[l("σ")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(n,{separator:"true"},{default:a(()=>[l(",")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("=")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(i,null,{default:a(()=>[s(n,null,{default:a(()=>[l(")")]),_:1}),s(p,null,{default:a(()=>[l("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(n,null,{default:a(()=>[l("(")]),_:1}),s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1}),s(n,null,{default:a(()=>[l(")")]),_:1}),s(n,null,{default:a(()=>[l("×")]),_:1}),s(p,null,{default:a(()=>[l("5")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("\\sigma(4x+1, 5x-3) = (4x+1)^2 + (4x+1)(5x-3)+(4x+1+5x-3)\\times 5 ")]),_:1})]),_:1})]),_:1})]),Ds])])]),e("p",null,[l("总之,无论他有多长,都会在算式里面充斥着"),e("span",Fs,[e("span",bs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("4x+1")]),_:1})]),_:1})]),_:1})]),ks]),l("和"),e("span",Ss,[e("span",vs,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("5x-3")]),_:1})]),_:1})]),_:1})]),Ps]),l(",而且,计算机并没有那么聪明,所以只好按照特定的顺序一次次的计算。")]),e("p",null,[l("于是"),e("span",Vs,[e("span",Is,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("4")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("+")]),_:1}),s(p,null,{default:a(()=>[l("1")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("4x+1")]),_:1})]),_:1})]),_:1})]),zs]),l("被重复计算了三次,"),e("span",Es,[e("span",Ns,[s(m,null,{default:a(()=>[s(u,null,{default:a(()=>[s(o,null,{default:a(()=>[s(p,null,{default:a(()=>[l("5")]),_:1}),s(t,null,{default:a(()=>[l("x")]),_:1}),s(n,null,{default:a(()=>[l("−")]),_:1}),s(p,null,{default:a(()=>[l("3")]),_:1})]),_:1}),s(_,{encoding:"application/x-tex"},{default:a(()=>[l("5x-3")]),_:1})]),_:1})]),_:1})]),Rs]),l("被重复计算了两次。对于"),ws,l("那种情况,更是会被求值5次。")]),js])}const Ms=r(y,[["render",qs]]);export{Ls as __pageData,Ms as default}; diff --git a/assets/ch03_procedure.md.2caba3b4.js b/assets/ch03_procedure.md.2caba3b4.js new file mode 100644 index 0000000..f0e4163 --- /dev/null +++ b/assets/ch03_procedure.md.2caba3b4.js @@ -0,0 +1,60 @@ +import{_ as u,c as _,z as l,E as s,B as a,a as n,O as e,G as c,o as m}from"./chunks/framework.41379913.js";const us=JSON.parse('{"title":"过程","description":"程序的基本控制结构有哪几种?递归和循环的区别是什么?短路求值的作用是什么?","frontmatter":{"description":"程序的基本控制结构有哪几种?递归和循环的区别是什么?短路求值的作用是什么?"},"headers":[],"relativePath":"ch03_procedure.md"}'),f={name:"ch03_procedure.md"},h=l("h1",{id:"过程",tabindex:"-1"},[n("过程 "),l("a",{class:"header-anchor",href:"#过程","aria-label":'Permalink to "过程"'},"​")],-1),x=l("p",null,"什么是过程呢?",-1),g=l("p",null,"凭经验我们就能知道,实际的问题并不是像理想中那么简单,不是一个表达式就能描述的清楚的,而且也并不一定能通过组合来直接解决。",-1),b=l("p",null,"甚至是简单的数学问题,也不能用他们来解决。比如:",-1),v={class:"katex-display"},z={class:"katex"},T={class:"katex-mathml"},w=e('',1),k=l("p",null,"求绝对值,或者",-1),E={class:"katex-display"},S={class:"katex"},P={class:"katex-mathml"},V=e('',1),j=e(`

求积。

其中一个需要我们根据参数的值,来选择不同的计算方式,返回不同的结果;另外一个则是通过对特定的表达式做一个累乘(没错其实行为跟乘方是一样的,所以我把它叫 pow)。

我们先看累乘。

累乘

如果我们已知指数的话,要写起来是很简单的:

javascript
function pow1(x) { return x; }
+function pow2(x) { return pow1(x) * x; }
+function pow3(x) { return pow2(x) * x; }
+function pow4(x) { return pow3(x) * x; }
+function pow5(x) { return pow4(x) * x; }

这样写其实是符合pow的定义的。

然后其实我们可以把它变换一下:

javascript
// 伪代码,无法正确运行
+function pow(x, 1) { return x; }
+function pow(x, 2) { return pow(x, 1) * x; }
+function pow(x, 3) { return pow(x, 2) * x; }
+function pow(x, 4) { return pow(x, 3) * x; }
+function pow(x, 5) { return pow(x, 4) * x; }

所以我们得到了pow函数的另一个形式:

`,10),I={class:"katex-display"},N={class:"katex"},q={class:"katex-mathml"},R=e('',1),B=l("p",null,"没错我们又归结到了选择结构上。后面我们会拿这个来讨论。现在只是简单提一提。",-1),$=l("p",null,"哦对了,现在pow的这个形式叫做递归定义,数学上叫递推关系式(好像又叫归纳定义?)。",-1),J={class:"katex"},O={class:"katex-mathml"},G=e('',1),H=e(`

根据前面的分析,我们可以知道,如果能够拿到前面一个乘积,然后和第y个相乘,,就可以得到我们要的结果。

于是我们假设那第y-1个乘积是prod,最终的结果就是prod*x。再往前推prod,这个关系是基本上不变的。

那么,我们只要有一个东西来保存前面的结果,再跟后面的相乘之后更新保存的结果就可以了。

这个能保存数据的东西我们把它叫做变量(Variable)。

比如最开始prod = 1,然后prod = prod * x,会把prodx相乘的结果计算出来,再保存进prod里面。

所以我们可以简单地把这个计算看成

javascript
let prod=1
+prod=prod*x;
+prod=prod*x;
+prod=prod*x;
+...
+prod=prod*x; // 执行y次

那么怎么让他能够只执行y次呢?

循环

JavaScript提供了一种东西叫做循环语句。

大概是长这个样子的

javascript
let prod=1;
+for(let i = 1; i <= y; i = i+1) {
+    prod = prod*x;
+}

这个叫循环语句,意思就是,在满足指定条件的情况下,会一直去执行语句体(花括号之间)的内容。

上面那段代码的意思是,让prod初值为1,让i从1开始,每次增加1,如果i小于等于y的话,执行prod = prod*x,否则跳出循环。

确实很复杂,特别是比起上面那个递归定义来。

我们对它进行一个简单的改写。

`,16),K={class:"katex"},L={class:"katex-mathml"},M=e('',1),Q={class:"katex"},U={class:"katex-mathml"},W=e('',1),X={class:"katex"},Y={class:"katex-mathml"},Z=e('',1),ss={class:"katex"},as={class:"katex-mathml"},ns=e('',1),ls={class:"katex"},ps={class:"katex-mathml"},os=e('',1),ts=l("code",null,"i <= y-1",-1),es=l("code",null,"i < y",-1),cs=e(`

另外,但凡类似x=x+y这种的,都可以写成x+=y,特别地,当运算符为+-,同时y为1的时候,可以直接写成x++或者x--

于是一个典型的循环就出来了:

javascript
let prod=1;
+for(let i = 0; i < y; i++) {
+    prod *= x;
+}

那么我们的pow函数也有了:

javascript
function pow(x, y) {
+    let prod=1;
+    for(let i = 0; i < y; i++) {
+        prod *= x;
+    }
+    return prod;
+}

选择

好的,回头来看我们的abs函数。

我们要根据x的符号来决定时返回x还是他的相反数。

JavaScript让我们能通过选择语句if...else...做这件事。

javascript
function abs(x) {
+    if(x >= 0) {
+        return x;
+    } else {
+        return -x;
+    }
+}

当然if/else还有一种简单的形式,可以让我们写起来更省力。

javascript
function abs(x) {
+    return (x >= 0) ? x : -x;
+}

?:运算符共有三部分,问好前面是判断条件,问号后面由冒号分成两部分,如果判断条件为真,那么返回冒号与问号之间的,否则返回冒号之后的。

递归

好的,这样子我们就能完善我们前面写的递归定义的pow了。

javascript
function pow(x, y) {
+    return (y == 1) ? x : pow(x, y-1) * x;
+}

不能比循环的明白更多。

尾递归

尾递归这个概念可以简单做个了解。主要是因为如果有了尾递归,编译器就可以更好的优化程序的执行过程。而且更重要的一点是,尾递归跟循环几乎是一一对应的关系。

我们先简单的把这个实现写出来吧。

javascript
function pow(x, y, prod = 1) {
+    return (y == 0) ? prod : pow(x, y-1, prod*x);
+}

怎么转换到循环的呢?

首先我们提取出变量来。

javascript
function pow(x, y, prod = 1) {
+    if (y == 0) {
+        return prod;
+    }
+    y = y - 1;
+    prod*=x;
+    return prow(x, y, prod);
+}

正式写成循环

javascript
function pow(x, y, prod = 1) {
+    for(;y != 0;) { // or while(y != 0)
+        y = y - 1;
+        prod*=x;
+    }
+    return prod;
+}

改写成递增的变量

javascript
function pow(x, y) {
+    let prod = 1;
+    for(let i = 0; i < y; i++) {
+        prod *= x;
+    }
+    return prod;
+}

短路求值

试想一下我们的递归版pow代码

javascript
function pow(x, y) {
+    return (y == 1) ? x : pow(x, y-1) * x;
+}

如果这里你把函数自身的调用写成了pow(x, y) * x,结果会是怎么样的呢?

如果这样写,y作为一个计数器并没有递减,那么每回调用都会跟当前调用一样传递相同的参数进去。

于是会一在重复递归地调用pow(x, y),直到不能再调用导致栈溢出。

然而一定是这样吗?你可以调用一下pow(4, 1)试试,会正常输出4

为什么会有这种不同呢?

上一讲,我们提到了组合的概念,说,通过组合会强制求值每一个传递给函数的参数,但是当我们把这个东西忘?:操作符上套的时候,并没有效果。

这种特性叫做短路求值(short-circuit evaluation),对于?:来说,只有当条件为真的时候才去求值真分支,为假的时候才去求值假分支。

所以如果写错的那个pow,当y == 1的时候,还是能够正常执行的。

过程

一个再复杂的流程,最终都会由这样的三部分组成:

根据我们前面提到的组合的思想,再复杂的过程都能通过这些来组合解决。

练习

`,45);function rs(ys,Fs,is,Ds,ds,Cs){const p=c("mi"),o=c("mo"),t=c("mrow"),D=c("mtd"),r=c("mn"),d=c("mtr"),C=c("mtable"),y=c("annotation"),F=c("semantics"),i=c("math"),A=c("msubsup");return m(),_("div",null,[h,x,g,b,l("p",null,[l("span",v,[l("span",z,[l("span",T,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,{mathvariant:"normal"},{default:a(()=>[n("∣")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(p,{mathvariant:"normal"},{default:a(()=>[n("∣")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(p,null,{default:a(()=>[n("a")]),_:1}),s(p,null,{default:a(()=>[n("b")]),_:1}),s(p,null,{default:a(()=>[n("s")]),_:1}),s(o,null,{default:a(()=>[n("(")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,null,{default:a(()=>[n(")")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(t,null,{default:a(()=>[s(o,{fence:"true"},{default:a(()=>[n("{")]),_:1}),s(C,null,{default:a(()=>[s(d,null,{default:a(()=>[s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1})]),_:1})]),_:1}),s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,null,{default:a(()=>[n("≥")]),_:1}),s(r,null,{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),_:1}),s(d,null,{default:a(()=>[s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(o,null,{default:a(()=>[n("−")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1})]),_:1})]),_:1}),s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,null,{default:a(()=>[n("<")]),_:1}),s(r,null,{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("|x| = abs(x) = \\begin{cases} x, &x\\ge0 \\\\ -x, &x\\lt0 \\end{cases} ")]),_:1})]),_:1})]),_:1})]),w])])]),k,l("p",null,[l("span",E,[l("span",S,[l("span",P,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("p")]),_:1}),s(p,null,{default:a(()=>[n("o")]),_:1}),s(p,null,{default:a(()=>[n("w")]),_:1}),s(o,null,{default:a(()=>[n("(")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n(")")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(A,null,{default:a(()=>[s(o,null,{default:a(()=>[n("∏")]),_:1}),s(t,null,{default:a(()=>[s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1})]),_:1})]),_:1}),s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1})]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("pow(x,y) = \\prod_{1}^{y}{x} ")]),_:1})]),_:1})]),_:1})]),V])])]),j,l("p",null,[l("span",I,[l("span",N,[l("span",q,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("p")]),_:1}),s(p,null,{default:a(()=>[n("o")]),_:1}),s(p,null,{default:a(()=>[n("w")]),_:1}),s(o,null,{default:a(()=>[n("(")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n(")")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(t,null,{default:a(()=>[s(o,{fence:"true"},{default:a(()=>[n("{")]),_:1}),s(C,null,{default:a(()=>[s(d,null,{default:a(()=>[s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1})]),_:1})]),_:1}),s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1})]),_:1})]),_:1}),s(d,null,{default:a(()=>[s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("p")]),_:1}),s(p,null,{default:a(()=>[n("o")]),_:1}),s(p,null,{default:a(()=>[n("w")]),_:1}),s(o,null,{default:a(()=>[n("(")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n("−")]),_:1}),s(r,null,{default:a(()=>[n("1")]),_:1}),s(o,null,{default:a(()=>[n(")")]),_:1}),s(o,null,{default:a(()=>[n("×")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1})]),_:1})]),_:1}),s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n(">")]),_:1}),s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("pow(x, y) = \\begin{cases} x, &y=1 \\\\ pow(x, y -1)\\times x, &y>1 \\end{cases} ")]),_:1})]),_:1})]),_:1})]),R])])]),B,$,l("p",null,[n("好的,我们回到"),l("span",J,[l("span",O,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(o,null,{default:a(()=>[n("∏")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("\\prod")]),_:1})]),_:1})]),_:1})]),G]),n("运算符上来。")]),H,l("p",null,[n("一般来说,程序员是从 "),l("span",K,[l("span",L,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(r,null,{default:a(()=>[n("0")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),M]),n(" 开始计数的。于是从 "),l("span",Q,[l("span",U,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("1")]),_:1})]),_:1})]),_:1})]),W]),n(" 到 "),l("span",X,[l("span",Y,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("y")]),_:1})]),_:1})]),_:1})]),Z]),n(",就变成了从 "),l("span",ss,[l("span",as,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(r,null,{default:a(()=>[n("0")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),ns]),n(" 到 "),l("span",ls,[l("span",ps,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n("−")]),_:1}),s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("y-1")]),_:1})]),_:1})]),_:1})]),os]),n(",进一步地"),ts,n("又可以进一步地写成"),es,n("(反正都是整数,证明我就不给了),这样条件就简化掉了一个符号。")]),cs])}const _s=u(f,[["render",rs]]);export{us as __pageData,_s as default}; diff --git a/assets/ch03_procedure.md.2caba3b4.lean.js b/assets/ch03_procedure.md.2caba3b4.lean.js new file mode 100644 index 0000000..66e1591 --- /dev/null +++ b/assets/ch03_procedure.md.2caba3b4.lean.js @@ -0,0 +1 @@ +import{_ as u,c as _,z as l,E as s,B as a,a as n,O as e,G as c,o as m}from"./chunks/framework.41379913.js";const us=JSON.parse('{"title":"过程","description":"程序的基本控制结构有哪几种?递归和循环的区别是什么?短路求值的作用是什么?","frontmatter":{"description":"程序的基本控制结构有哪几种?递归和循环的区别是什么?短路求值的作用是什么?"},"headers":[],"relativePath":"ch03_procedure.md"}'),f={name:"ch03_procedure.md"},h=l("h1",{id:"过程",tabindex:"-1"},[n("过程 "),l("a",{class:"header-anchor",href:"#过程","aria-label":'Permalink to "过程"'},"​")],-1),x=l("p",null,"什么是过程呢?",-1),g=l("p",null,"凭经验我们就能知道,实际的问题并不是像理想中那么简单,不是一个表达式就能描述的清楚的,而且也并不一定能通过组合来直接解决。",-1),b=l("p",null,"甚至是简单的数学问题,也不能用他们来解决。比如:",-1),v={class:"katex-display"},z={class:"katex"},T={class:"katex-mathml"},w=e("",1),k=l("p",null,"求绝对值,或者",-1),E={class:"katex-display"},S={class:"katex"},P={class:"katex-mathml"},V=e("",1),j=e("",10),I={class:"katex-display"},N={class:"katex"},q={class:"katex-mathml"},R=e("",1),B=l("p",null,"没错我们又归结到了选择结构上。后面我们会拿这个来讨论。现在只是简单提一提。",-1),$=l("p",null,"哦对了,现在pow的这个形式叫做递归定义,数学上叫递推关系式(好像又叫归纳定义?)。",-1),J={class:"katex"},O={class:"katex-mathml"},G=e("",1),H=e("",16),K={class:"katex"},L={class:"katex-mathml"},M=e("",1),Q={class:"katex"},U={class:"katex-mathml"},W=e("",1),X={class:"katex"},Y={class:"katex-mathml"},Z=e("",1),ss={class:"katex"},as={class:"katex-mathml"},ns=e("",1),ls={class:"katex"},ps={class:"katex-mathml"},os=e("",1),ts=l("code",null,"i <= y-1",-1),es=l("code",null,"i < y",-1),cs=e("",45);function rs(ys,Fs,is,Ds,ds,Cs){const p=c("mi"),o=c("mo"),t=c("mrow"),D=c("mtd"),r=c("mn"),d=c("mtr"),C=c("mtable"),y=c("annotation"),F=c("semantics"),i=c("math"),A=c("msubsup");return m(),_("div",null,[h,x,g,b,l("p",null,[l("span",v,[l("span",z,[l("span",T,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,{mathvariant:"normal"},{default:a(()=>[n("∣")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(p,{mathvariant:"normal"},{default:a(()=>[n("∣")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(p,null,{default:a(()=>[n("a")]),_:1}),s(p,null,{default:a(()=>[n("b")]),_:1}),s(p,null,{default:a(()=>[n("s")]),_:1}),s(o,null,{default:a(()=>[n("(")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,null,{default:a(()=>[n(")")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(t,null,{default:a(()=>[s(o,{fence:"true"},{default:a(()=>[n("{")]),_:1}),s(C,null,{default:a(()=>[s(d,null,{default:a(()=>[s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1})]),_:1})]),_:1}),s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,null,{default:a(()=>[n("≥")]),_:1}),s(r,null,{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),_:1}),s(d,null,{default:a(()=>[s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(o,null,{default:a(()=>[n("−")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1})]),_:1})]),_:1}),s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,null,{default:a(()=>[n("<")]),_:1}),s(r,null,{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("|x| = abs(x) = \\begin{cases} x, &x\\ge0 \\\\ -x, &x\\lt0 \\end{cases} ")]),_:1})]),_:1})]),_:1})]),w])])]),k,l("p",null,[l("span",E,[l("span",S,[l("span",P,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("p")]),_:1}),s(p,null,{default:a(()=>[n("o")]),_:1}),s(p,null,{default:a(()=>[n("w")]),_:1}),s(o,null,{default:a(()=>[n("(")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n(")")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(A,null,{default:a(()=>[s(o,null,{default:a(()=>[n("∏")]),_:1}),s(t,null,{default:a(()=>[s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1})]),_:1})]),_:1}),s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1})]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("pow(x,y) = \\prod_{1}^{y}{x} ")]),_:1})]),_:1})]),_:1})]),V])])]),j,l("p",null,[l("span",I,[l("span",N,[l("span",q,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("p")]),_:1}),s(p,null,{default:a(()=>[n("o")]),_:1}),s(p,null,{default:a(()=>[n("w")]),_:1}),s(o,null,{default:a(()=>[n("(")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n(")")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(t,null,{default:a(()=>[s(o,{fence:"true"},{default:a(()=>[n("{")]),_:1}),s(C,null,{default:a(()=>[s(d,null,{default:a(()=>[s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1})]),_:1})]),_:1}),s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n("=")]),_:1}),s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1})]),_:1})]),_:1}),s(d,null,{default:a(()=>[s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("p")]),_:1}),s(p,null,{default:a(()=>[n("o")]),_:1}),s(p,null,{default:a(()=>[n("w")]),_:1}),s(o,null,{default:a(()=>[n("(")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n("−")]),_:1}),s(r,null,{default:a(()=>[n("1")]),_:1}),s(o,null,{default:a(()=>[n(")")]),_:1}),s(o,null,{default:a(()=>[n("×")]),_:1}),s(p,null,{default:a(()=>[n("x")]),_:1}),s(o,{separator:"true"},{default:a(()=>[n(",")]),_:1})]),_:1})]),_:1}),s(D,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n(">")]),_:1}),s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("pow(x, y) = \\begin{cases} x, &y=1 \\\\ pow(x, y -1)\\times x, &y>1 \\end{cases} ")]),_:1})]),_:1})]),_:1})]),R])])]),B,$,l("p",null,[n("好的,我们回到"),l("span",J,[l("span",O,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(o,null,{default:a(()=>[n("∏")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("\\prod")]),_:1})]),_:1})]),_:1})]),G]),n("运算符上来。")]),H,l("p",null,[n("一般来说,程序员是从 "),l("span",K,[l("span",L,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(r,null,{default:a(()=>[n("0")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),M]),n(" 开始计数的。于是从 "),l("span",Q,[l("span",U,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("1")]),_:1})]),_:1})]),_:1})]),W]),n(" 到 "),l("span",X,[l("span",Y,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("y")]),_:1})]),_:1})]),_:1})]),Z]),n(",就变成了从 "),l("span",ss,[l("span",as,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(r,null,{default:a(()=>[n("0")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),ns]),n(" 到 "),l("span",ls,[l("span",ps,[s(i,null,{default:a(()=>[s(F,null,{default:a(()=>[s(t,null,{default:a(()=>[s(p,null,{default:a(()=>[n("y")]),_:1}),s(o,null,{default:a(()=>[n("−")]),_:1}),s(r,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(y,{encoding:"application/x-tex"},{default:a(()=>[n("y-1")]),_:1})]),_:1})]),_:1})]),os]),n(",进一步地"),ts,n("又可以进一步地写成"),es,n("(反正都是整数,证明我就不给了),这样条件就简化掉了一个符号。")]),cs])}const _s=u(f,[["render",rs]]);export{us as __pageData,_s as default}; diff --git a/assets/ch04_encoding.md.6d49b0e8.js b/assets/ch04_encoding.md.6d49b0e8.js new file mode 100644 index 0000000..82f572e --- /dev/null +++ b/assets/ch04_encoding.md.6d49b0e8.js @@ -0,0 +1,4 @@ +import{_ as i,c as h,z as p,a as n,E as a,B as s,O as o,G as r,o as f}from"./chunks/framework.41379913.js";const W=JSON.parse('{"title":"编码","description":"","frontmatter":{},"headers":[],"relativePath":"ch04_encoding.md"}'),C={name:"ch04_encoding.md"},y=p("h1",{id:"编码",tabindex:"-1"},[n("编码 "),p("a",{class:"header-anchor",href:"#编码","aria-label":'Permalink to "编码"'},"​")],-1),A=p("p",null,"这里的编码(Encoding)当然不是指简单的编写代码。",-1),x=p("p",null,"为什么会有这个概念呢。",-1),g=p("p",null,"想想古老的印度人是怎么表示数字的吧:用十个不同的符号和位序组合来表示一个确切的数字,演化成今天我们的用阿拉伯数字(Arabic Numerials)表示的十进制系统。",-1),T=p("p",null,[n("没错就是你身边到处都存在的"),p("strong",null,"数字"),n("。这套系统只需要简单的十个符号和并不怎么复杂的规则就能从0表示到任意大的确切数字。")],-1),b=p("p",null,"我们在第二讲中提到,代码中的表示的“整数”,跟数学意义上的整数,是有所差距的。这个差距其宗之一就在于表示数字的能力上。",-1),D={class:"katex"},F={class:"katex-mathml"},v=o('',1),S=o('
javascript
pow(2, 70);

嗯,结果是什么鬼(1.1805916207174113e+21,特别是后面这段)!

也就是说,我们能直观表示的整数也就21位(十万亿亿级),再之后就只能用科学计数法表示了。

实际你会发现,其实还没到这个数量级的时候,数字就已经无法精确表示了。

为什么会这样呢?

二进制

跟阿拉伯数字系统一样,计算机内部也是利用位序来表示数值,但犹豫数字电路只有通和断两种状态,所以,相当于每位上只能有两种状态,作为传统,我们将用0和1来表示这两种状态。

',7),I={class:"katex"},k={class:"katex-mathml"},P=o('',1),z=o('

可是我们之前使用的过程告诉我们,其实并不是这样的。为什么呢?

定长

既然计算机表示数字就是靠0和1,那么如果我要表示多个数字,怎么来区分呢?

比如,你知道下面究竟是1,2,3,4还是13, 14?

javascript
11011100

于是其实我们可以思考一下,出了数值,还有什么可以利用的。

长度。(以及其实你也可以用霍夫曼编码。)

当我规定用于表示数据的长度必须是一定(比如4位)的时候,表示数据就特别地清晰了:

javascript
0001 0010 0011 0100

就可以表示1,2,3,4这四个数。而上面那个我们理解不了的序列,也可以解释成13,14这两个数。

当你去规定一种解释二进制序列的方法的时候,其实你就是在定义一种编码

二进制序列中,每个0或者1,叫一个bit(位),特定长度(一般是8)的基本解释单位,叫做byte(字节),这个特定长度,据说是依据当前系统中表示基本的字符编码的长度来确定。

换句话说,编码不仅仅是能用来表示数字的。

ASCII

现在提到字节大家都会反应是8位,就是因为这个东西。

全名叫American Standard Code for Information Interchange(美国标准信息交换码)。

这套代码从上世纪60年代一致用到现在,足以说明有多么经典。然而我们先不说他是什么,简单的从我们自己的角度出发先设计一套东西。

字符集

要知道美国人民的生活很是单调的,每天就面临大小写加起来四十多个字母,十个阿拉伯数字,以及十多个常用的标点符号。对比起来,深深地为自己的母语感到自豪。

',19),V={class:"katex"},q={class:"katex-mathml"},E=o('',1),U=o(`

比如我们可以定义,0000000 ~ 0001001 表示数字字符0-9, 0100001 ~ 0111010 表示大写字母字符的A-Z,1000001 ~ 1011010表示小写字母字符的a-z,于是我们就能对把对应的字母变成相应的编码了。

但是老是这样看着满屏的0和1,总是会感觉眼花。既然都是表示数据,并不是说相同的东西,计算机看到长什么样子,我们就要看一样的。只要是能够表示相同的数据,完全可以换一种我们更习惯的方式来表示。

十六进制

比如我们可以使用十六进制(Hexadecimal)代替二进制(Binary),也就是说,同一个位上面共有16个状态,习惯上分别用0-9加上a-f来表示,同时,为了与十进制表示进行区分,一般会加上0x前缀或者h后缀。对于刚刚的字符编码来说,就可以用0x00 ~ 0x09 表示 数字字符,0x21 ~ 0x3A 表示大写字母A-Z,0x40 ~ 0x5A表示小写字母a-z。

为什么是十六进制而不是其他的十二进制或者十八、二十四进制呢?

我们可以简单的做一个对比:

Bin 0001 0010 0011 .... 1010 1011 1100 1101 1110 1111
+Hex    1    2    3         A    B    C    D    E    F
+Dec    1    2    3        10   11   12   13   14   15

十六进制的一位刚好一一对应二进制的每四个位。

于是本来用二进制表示起来很冗长的东西,用十六进制可以比较轻松的表述出来。而且相互之间的转换也很容易。

相反生活中常用的十进制跟二进制对应的就不那么协调,所以一般也比较少用,只要知道对应的位上的值就好。

也就是说,只要能够数

`,11),N={class:"katex-display"},j={class:"katex"},B={class:"katex-mathml"},J=o('',1),R=o(`

嗯,我觉得如果你玩过2048的话一定不会对前面的那些数字感到陌生。

ASCII

嗯,ASCII跟我们想的一样,选择了8位作为实际的长度,同时加入了一些必须要用到的特殊字符(比如制表符tab 0x09,回车记号carriage return 0x0D,换行符line feed 0x0A..),以及一些控制字符(比如空字符NUL 0x00,文件结束符EOF等)。于是整体的设计相对于我们的想法有一些偏移:

好的我们来试试吧:

javascript
'\\x41'

我们可以直接用引号'括起来单个字符,在程序中直接拿来使用。同时也可以反过来看:

javascript
let a_char = 'a';
+a_char.charCodeAt(0).toString(16)

CJK字符集

CJK(有时候也会带上V),是China,Japan,Korea(和Vietnam)的统称,在这些地区,因为同遗传自古汉语,字符的数量可是非常之多,8位的ASCII根本就没办法保存这么多东西。

于是随着各种语言诞生了各种编码,比如GB2312 简体中文,Big-5 繁体中文,Shift-JIS 日文等。这些编码并不是相互兼容的,于是一般对于特定的内容,需要专门指出其编码。同样地,当你尝试去一种编码来解释另外一种编码的文本时,就会出现乱码的情况。注意这个时候并不是你电脑中毒了。

Unicode

所以就有人想,不如把所有的编码都统一了吧,于是就出现了Unicode。

现行版本的Unicode加入了世界上能够找到的绝大多数语言的符号(嗯,包括emoji),然后将这些符号分类整理,统一编码。于是,在理想情况下,大家都使用Unicode字符集的话,就不会出现乱码的错误了。

Unicode包含的字符量足够的多,于是就要设计比ASCII更充分大的表示方式才能够容纳所有的符号。最初选择的是每个字符使用16位的二进制串来表示,这样的话一个中文字符虽然会超出8位范围之外,还是能够表示得出来,这种方法被称为UTF-16。

然而不久就发现,符号的数量(就算仅仅是中文字符)远远多于65536个,于是又进一步地扩充到了32位,可以表示到上亿个字符了,目前为止还要很久才会资源枯竭,这种方法类似地,被称为UTF-32。

然而UTF-32有个很致命的缺点。

对于西方人来说,并没有CJKV地区的人的需求,1个字节就能足够表示他们的符号,而UTF-32硬生生地为了统一,要求字母字符也要用4个字节来表示,这样就让空间整整大了4倍,而且另外3倍的空间完全是浪费掉的,没有任何作用。

但是,为了兼容,没办法。

UTF-8

于是为了解决这个浪费空间的问题,UTF-8出现了。这是来自Ken Thompson的设计,他给整个IT界贡献了不少东西,Unix、C语言、UTF-8。

简单来说就是,在保持与ASCII兼容的情况下,通过添加一些标记来支持更长的编码。

具体的做法当然维基百科介绍的比我更清楚,这里提起来只是要说这么一个思路:变长编码(Variable-Width Encoding)。因为比较重要的就是在设计的过程中考虑变量和不变量,以及设置各种标志和不同的数据区块解释方式,这样就可以把简单的二进制序列变成我们想要的任意数据。

当然,这里的思想我们会在第6章详细地展开。

数系的扩充

你有没有尝试计算JavaScript里面1/3的结果?

当我们需要小数的时候又该如何表示呢?

这个问题留作练习。

作为参考答案,你可以搜索IEEE754。

`,29);function K(w,$,O,Z,G,H){const l=r("mn"),c=r("mrow"),_=r("msup"),m=r("annotation"),d=r("semantics"),u=r("math"),t=r("mo"),e=r("mi");return f(),h("div",null,[y,A,x,g,T,b,p("p",null,[n("比如,我们来计算"),p("span",D,[p("span",F,[a(u,null,{default:s(()=>[a(d,null,{default:s(()=>[a(c,null,{default:s(()=>[a(_,null,{default:s(()=>[a(l,null,{default:s(()=>[n("2")]),_:1}),a(c,null,{default:s(()=>[a(l,null,{default:s(()=>[n("7")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1})]),_:1})]),_:1})]),_:1}),a(m,{encoding:"application/x-tex"},{default:s(()=>[n("2^{70}")]),_:1})]),_:1})]),_:1})]),v]),n(":")]),S,p("p",null,[n("然后我们就会看到计算机努力的表示数字: "),p("span",I,[p("span",k,[a(u,null,{default:s(()=>[a(d,null,{default:s(()=>[a(c,null,{default:s(()=>[a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1})]),_:1}),a(m,{encoding:"application/x-tex"},{default:s(()=>[n("0,1,10,11,100,101,110,111,1000,1001,1010")]),_:1})]),_:1})]),_:1})]),P]),n(" 等你都觉得脑袋大了,计算机才数到10,不过其实关于这个问题并不需要你操心太多,反正跟阿拉伯数字一样,只要足够长,任意大的数都可以表示。")]),z,p("p",null,[n("当然,美国人民日常使用的字符量少,在信息化的时代,其实是有优势的,比如,把他们常用的字符加起来也就100个左右,而"),p("span",V,[p("span",q,[a(u,null,{default:s(()=>[a(d,null,{default:s(()=>[a(c,null,{default:s(()=>[a(_,null,{default:s(()=>[a(l,null,{default:s(()=>[n("2")]),_:1}),a(c,null,{default:s(()=>[a(l,null,{default:s(()=>[n("6")]),_:1})]),_:1})]),_:1}),a(t,null,{default:s(()=>[n("<")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,null,{default:s(()=>[n("<")]),_:1}),a(_,null,{default:s(()=>[a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("7")]),_:1})]),_:1})]),_:1}),a(m,{encoding:"application/x-tex"},{default:s(()=>[n("2^{6} \\lt 100 \\lt 2^7")]),_:1})]),_:1})]),_:1})]),E]),n(",所以我们只要有长度为7位的二进制串长度就能把美国人民用的那些字符给完全表示出来。")]),U,p("p",null,[p("span",N,[p("span",j,[p("span",B,[a(u,null,{default:s(()=>[a(d,null,{default:s(()=>[a(c,null,{default:s(()=>[a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("3")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("7")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(l,null,{default:s(()=>[n("5")]),_:1}),a(l,null,{default:s(()=>[n("5")]),_:1}),a(l,null,{default:s(()=>[n("3")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("3")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("7")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(l,null,{default:s(()=>[n("7")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1}),a(l,null,{default:s(()=>[n("3")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1})]),_:1}),a(m,{encoding:"application/x-tex"},{default:s(()=>[n("1,2,4,8,16,...,1024,2048,...,32768,65536, 131072,...,2147483648 ")]),_:1})]),_:1})]),_:1})]),J])])]),R])}const M=i(C,[["render",K]]);export{W as __pageData,M as default}; diff --git a/assets/ch04_encoding.md.6d49b0e8.lean.js b/assets/ch04_encoding.md.6d49b0e8.lean.js new file mode 100644 index 0000000..098b560 --- /dev/null +++ b/assets/ch04_encoding.md.6d49b0e8.lean.js @@ -0,0 +1 @@ +import{_ as i,c as h,z as p,a as n,E as a,B as s,O as o,G as r,o as f}from"./chunks/framework.41379913.js";const W=JSON.parse('{"title":"编码","description":"","frontmatter":{},"headers":[],"relativePath":"ch04_encoding.md"}'),C={name:"ch04_encoding.md"},y=p("h1",{id:"编码",tabindex:"-1"},[n("编码 "),p("a",{class:"header-anchor",href:"#编码","aria-label":'Permalink to "编码"'},"​")],-1),A=p("p",null,"这里的编码(Encoding)当然不是指简单的编写代码。",-1),x=p("p",null,"为什么会有这个概念呢。",-1),g=p("p",null,"想想古老的印度人是怎么表示数字的吧:用十个不同的符号和位序组合来表示一个确切的数字,演化成今天我们的用阿拉伯数字(Arabic Numerials)表示的十进制系统。",-1),T=p("p",null,[n("没错就是你身边到处都存在的"),p("strong",null,"数字"),n("。这套系统只需要简单的十个符号和并不怎么复杂的规则就能从0表示到任意大的确切数字。")],-1),b=p("p",null,"我们在第二讲中提到,代码中的表示的“整数”,跟数学意义上的整数,是有所差距的。这个差距其宗之一就在于表示数字的能力上。",-1),D={class:"katex"},F={class:"katex-mathml"},v=o("",1),S=o("",7),I={class:"katex"},k={class:"katex-mathml"},P=o("",1),z=o("",19),V={class:"katex"},q={class:"katex-mathml"},E=o("",1),U=o("",11),N={class:"katex-display"},j={class:"katex"},B={class:"katex-mathml"},J=o("",1),R=o("",29);function K(w,$,O,Z,G,H){const l=r("mn"),c=r("mrow"),_=r("msup"),m=r("annotation"),d=r("semantics"),u=r("math"),t=r("mo"),e=r("mi");return f(),h("div",null,[y,A,x,g,T,b,p("p",null,[n("比如,我们来计算"),p("span",D,[p("span",F,[a(u,null,{default:s(()=>[a(d,null,{default:s(()=>[a(c,null,{default:s(()=>[a(_,null,{default:s(()=>[a(l,null,{default:s(()=>[n("2")]),_:1}),a(c,null,{default:s(()=>[a(l,null,{default:s(()=>[n("7")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1})]),_:1})]),_:1})]),_:1}),a(m,{encoding:"application/x-tex"},{default:s(()=>[n("2^{70}")]),_:1})]),_:1})]),_:1})]),v]),n(":")]),S,p("p",null,[n("然后我们就会看到计算机努力的表示数字: "),p("span",I,[p("span",k,[a(u,null,{default:s(()=>[a(d,null,{default:s(()=>[a(c,null,{default:s(()=>[a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1})]),_:1}),a(m,{encoding:"application/x-tex"},{default:s(()=>[n("0,1,10,11,100,101,110,111,1000,1001,1010")]),_:1})]),_:1})]),_:1})]),P]),n(" 等你都觉得脑袋大了,计算机才数到10,不过其实关于这个问题并不需要你操心太多,反正跟阿拉伯数字一样,只要足够长,任意大的数都可以表示。")]),z,p("p",null,[n("当然,美国人民日常使用的字符量少,在信息化的时代,其实是有优势的,比如,把他们常用的字符加起来也就100个左右,而"),p("span",V,[p("span",q,[a(u,null,{default:s(()=>[a(d,null,{default:s(()=>[a(c,null,{default:s(()=>[a(_,null,{default:s(()=>[a(l,null,{default:s(()=>[n("2")]),_:1}),a(c,null,{default:s(()=>[a(l,null,{default:s(()=>[n("6")]),_:1})]),_:1})]),_:1}),a(t,null,{default:s(()=>[n("<")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(t,null,{default:s(()=>[n("<")]),_:1}),a(_,null,{default:s(()=>[a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("7")]),_:1})]),_:1})]),_:1}),a(m,{encoding:"application/x-tex"},{default:s(()=>[n("2^{6} \\lt 100 \\lt 2^7")]),_:1})]),_:1})]),_:1})]),E]),n(",所以我们只要有长度为7位的二进制串长度就能把美国人民用的那些字符给完全表示出来。")]),U,p("p",null,[p("span",N,[p("span",j,[p("span",B,[a(u,null,{default:s(()=>[a(d,null,{default:s(()=>[a(c,null,{default:s(()=>[a(l,null,{default:s(()=>[n("1")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("3")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("7")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(l,null,{default:s(()=>[n("5")]),_:1}),a(l,null,{default:s(()=>[n("5")]),_:1}),a(l,null,{default:s(()=>[n("3")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("3")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("0")]),_:1}),a(l,null,{default:s(()=>[n("7")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(e,{mathvariant:"normal"},{default:s(()=>[n(".")]),_:1}),a(t,{separator:"true"},{default:s(()=>[n(",")]),_:1}),a(l,null,{default:s(()=>[n("2")]),_:1}),a(l,null,{default:s(()=>[n("1")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(l,null,{default:s(()=>[n("7")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1}),a(l,null,{default:s(()=>[n("3")]),_:1}),a(l,null,{default:s(()=>[n("6")]),_:1}),a(l,null,{default:s(()=>[n("4")]),_:1}),a(l,null,{default:s(()=>[n("8")]),_:1})]),_:1}),a(m,{encoding:"application/x-tex"},{default:s(()=>[n("1,2,4,8,16,...,1024,2048,...,32768,65536, 131072,...,2147483648 ")]),_:1})]),_:1})]),_:1})]),J])])]),R])}const M=i(C,[["render",K]]);export{W as __pageData,M as default}; diff --git a/assets/ch05_sequence.md.e9066e5e.js b/assets/ch05_sequence.md.e9066e5e.js new file mode 100644 index 0000000..e865431 --- /dev/null +++ b/assets/ch05_sequence.md.e9066e5e.js @@ -0,0 +1,21 @@ +import{_ as y}from"./chunks/cons-cells.6ed9c323.js";import{_ as u,c as d,z as t,a as n,E as s,B as a,O as e,G as _,o as m}from"./chunks/framework.41379913.js";const Q=JSON.parse('{"title":"序列","description":"","frontmatter":{},"headers":[],"relativePath":"ch05_sequence.md"}'),D={name:"ch05_sequence.md"},F=t("h1",{id:"序列",tabindex:"-1"},[n("序列 "),t("a",{class:"header-anchor",href:"#序列","aria-label":'Permalink to "序列"'},"​")],-1),h=t("p",null,"你应该记得,我们在编码那一张提过如何表示多个数字的,没错,定长编码可以帮我们解决这个问题,帮我们界定数字之间的边界。但是,当我们要表示的数据不只是一组的时候,又该怎么来界定边界?",-1),C={class:"katex"},A={class:"katex-mathml"},f=e('',1),g={class:"katex"},T={class:"katex-mathml"},x=e('',1),b=e('
cpp
0x01030507 0x090B0002 0x0406080A

好的,接下来请完成以下任务:

嗯,是不是感觉自己懵了,如果不知道第一组有几个的话你是不可能准确找到这个数字的。

于是我们得到了一个简单的界定序列边界的方法,就是要知道各序列对应的长度。

',5),k={class:"katex"},S={class:"katex-mathml"},v=e('',1),P={class:"katex"},E={class:"katex-mathml"},V=e('',1),q={class:"katex"},I={class:"katex-mathml"},L=e('',1),N=e('

长度

我们可以看到,界定一个序列,必须要知道的是两点,第一个是他的起始元素,第二个就是这个序列的长度。

一般来说,当这个序列的长度固定不变,并且每个元素是挨在一起的时候,我们常把它叫做array。

关于array,我非常不喜欢“数组”这个翻译,因为这里面并不一定是数,也有可能是符,所以我会尽可能地使用array来表示它。

在JavaScript中,定义一个array如下:

javascript
let nums = [1, 2, 3, 4, 5]

对于array来讲,初始元素是方括号中的取值为0的时候的值。在方括号中的值我们称之为index(索引,奇葩翻译一般叫做下标)。这些索引以数字的形式出现,作为序号来表示数据在序列中的位置。

',7),j={class:"katex"},R={class:"katex-mathml"},B=e('',1),$=e(`

不过对于遍历序列来说还支持另外一种写法。

javascript
for(let item of nums) {
+    console.log(item);
+}

不需要你去管什么索引了,这样子写就像是在说

javascript
对于 nums 里面的 item
+    输出 item 的值

当然,在Python中看起来更自然:

python
for item in nums:
+    print(item)

JavaScript中的字符串与array类似,自己维护了长度。但在早期的一些编程语言(如C语言)中,是没有做这种记录的,取而代之的是使用一个特定的标志来表示,这里是最后一个值为空。这种字符串也有另外一个名字,叫空结尾串(Null-terminated String)。

这种特殊标记就可以让我们界定一个序列的边界的另外一种方式。

我们可以来简单模拟一下这个行为:

javascript
let content = ['h', 'e', 'l', 'l', 'o', null];
+
+// 求长度:
+
+function length(nts) {
+    let length = 0;
+    for (let item of nts) {
+        if (item != null) {
+            length ++;
+        } else {
+            break;
+        }
+    }
+    return length;
+}
+
+length(content) // 5

而实际存放这个串的空间大小:

javascript
content.length // 6

可变长序列

试想你在一个笔记本上记录内容,每一行算是一个条目,当一页记录满以后,并不能连续地往下面记了,就需要翻到下一页纸。

连续不连续并不重要,重要的是我们能从这一页翻到下一页。

对应到我们的序列表示上,只要当前元素能够记录下一个元素的位置,是不是就表示我们的序列就能从第一个元素全部找出来了呢?

就像这张图片描述的:

list

这种结构被称为链接列表(也叫链表,Linked List)在上世纪五十年代就已经设计用于实际的程序。链表这种结构非常的灵活,可以很轻松的在任意位置添加或者删除数据。因为并不需要内存结构上的连续,所有的位序关系都是靠前一个元素和后一个元素之间的链接关系来确定,而添加或者删除数据只要改变一下这种链接关系就可以了。

但是对于链表来说一个很大的问题就是访问数据。比如我们要访问链表的第n个元素的时候,对于array和串这种结构来说,因为内存空间上是连续的,直接做一次加法就能找到,但是对于链表来说每次都要从第一个元素开始找起。而且,这种形式的链表又称作单链表(Singly Linked List),是没有办法从尾部往头部走的。

所以,在单链表的基础上,又构造了双链表(Double Linked List),每一个元素除了纪录自己对应的下一个元素之外,还要保存自己对应的上一个元素。这种时候如果做一些相邻元素的访问,就比单链表高效了一些。

比如,当我访问列表中第7个元素,然后再去访问第6个的时候,只要从第7个元素向前查找一次就可以了。

也许以后你可能还会听说到循环链表(Circular Linked List),或者是其他的形式,这些都离不开最初链表的思想:把数据和位置做关联。

线性结构

我们简单的做一个总结。

对于array,string和list我们都称为序列,原因其实是显而易见的。

首先,他们满足这样一点:所有的都是从头到尾连续地组织在一起,从任何地方出发都能找到下一个元素或者是到序列尾部。就像是在头和尾之间有一条线把他们串起来一样,而且在逻辑表示上,我们也是把他们放成一串。

另外,对于每一个元素,他们在序列中的位置是固定的,换句话说,序列中的元素是有前后顺序的。当我任意交换两个元素的位置,得到的新序列就跟原来的序列是不同的。

第三点就是,当我们从第一个元素开始找下去的时候,总是能找到最后一个元素(对于没有最后一个元素的情况,我们会在以后进行探讨)。

总结下来就是,序列是线性的(Linear)、有序的(Ordered,类比“排序过的(Sorted)”)和有穷的(Finite)。

生成器

很多时候我们的序列是通过某种规则产生的,而这种规则

练习:最长公共子序列(Longest Common Subsequence)

通常我们会对两个序列的内容进行对比,比较这两个序列有什么地方不同。这就是典型的LCS问题。LCS的应用很多,大部分版本控制系统的核心逻辑中就有它的存在。

请参阅Wikipedia对LCS问题的分析和讨论,尝试实现求LCS的算法,并试分析该算法要求迭代器至少要属于哪个category。

',36);function w(J,O,z,G,W,H){const l=_("mn"),p=_("mo"),o=_("mrow"),c=_("annotation"),r=_("semantics"),i=_("math");return m(),d("div",null,[F,h,t("p",null,[n("比如,我们有两组数,分别是"),t("span",C,[t("span",A,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("1")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("3")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("5")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("7")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("9")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("1")]),_:1}),s(l,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("1,3,5,7,9,11")]),_:1})]),_:1})]),_:1})]),f]),n("和"),t("span",g,[t("span",T,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("0")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("2")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("4")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("6")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("8")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("1")]),_:1}),s(l,null,{default:a(()=>[n("0")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("0,2,4,6,8,10")]),_:1})]),_:1})]),_:1})]),x]),n(",假设以一个字节(十六进制)表示的话,就是下面这个样子的:")]),b,t("p",null,[n("比如我们知道这两个序列是连续放在一起的,第一个序列长度是"),t("span",k,[t("span",S,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("6")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("6")]),_:1})]),_:1})]),_:1})]),v]),n(",同时也知道要找的是第二个序列的第三个数,那么这个数就应该是第"),t("span",P,[t("span",E,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("6")]),_:1}),s(p,null,{default:a(()=>[n("+")]),_:1}),s(l,null,{default:a(()=>[n("3")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("6+3")]),_:1})]),_:1})]),_:1})]),V]),n("个数字。")]),t("p",null,[n("当然,这个数字要从"),t("span",q,[t("span",I,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("1")]),_:1})]),_:1})]),_:1})]),L]),n("数过去才行,否则从任意其他的位置数,都不能得到我们想要的结果。")]),N,t("p",null,[n("有个重点是,这里的索引是从"),t("span",j,[t("span",R,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("0")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),B]),n("开始,最大是序列长度-1,这其实也就是为什么我们在第三章讲过程的时候提到循环的写法的一个原因。")]),$])}const U=u(D,[["render",w]]);export{Q as __pageData,U as default}; diff --git a/assets/ch05_sequence.md.e9066e5e.lean.js b/assets/ch05_sequence.md.e9066e5e.lean.js new file mode 100644 index 0000000..8254032 --- /dev/null +++ b/assets/ch05_sequence.md.e9066e5e.lean.js @@ -0,0 +1 @@ +import{_ as y}from"./chunks/cons-cells.6ed9c323.js";import{_ as u,c as d,z as t,a as n,E as s,B as a,O as e,G as _,o as m}from"./chunks/framework.41379913.js";const Q=JSON.parse('{"title":"序列","description":"","frontmatter":{},"headers":[],"relativePath":"ch05_sequence.md"}'),D={name:"ch05_sequence.md"},F=t("h1",{id:"序列",tabindex:"-1"},[n("序列 "),t("a",{class:"header-anchor",href:"#序列","aria-label":'Permalink to "序列"'},"​")],-1),h=t("p",null,"你应该记得,我们在编码那一张提过如何表示多个数字的,没错,定长编码可以帮我们解决这个问题,帮我们界定数字之间的边界。但是,当我们要表示的数据不只是一组的时候,又该怎么来界定边界?",-1),C={class:"katex"},A={class:"katex-mathml"},f=e("",1),g={class:"katex"},T={class:"katex-mathml"},x=e("",1),b=e("",5),k={class:"katex"},S={class:"katex-mathml"},v=e("",1),P={class:"katex"},E={class:"katex-mathml"},V=e("",1),q={class:"katex"},I={class:"katex-mathml"},L=e("",1),N=e("",7),j={class:"katex"},R={class:"katex-mathml"},B=e("",1),$=e("",36);function w(J,O,z,G,W,H){const l=_("mn"),p=_("mo"),o=_("mrow"),c=_("annotation"),r=_("semantics"),i=_("math");return m(),d("div",null,[F,h,t("p",null,[n("比如,我们有两组数,分别是"),t("span",C,[t("span",A,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("1")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("3")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("5")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("7")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("9")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("1")]),_:1}),s(l,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("1,3,5,7,9,11")]),_:1})]),_:1})]),_:1})]),f]),n("和"),t("span",g,[t("span",T,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("0")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("2")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("4")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("6")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("8")]),_:1}),s(p,{separator:"true"},{default:a(()=>[n(",")]),_:1}),s(l,null,{default:a(()=>[n("1")]),_:1}),s(l,null,{default:a(()=>[n("0")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("0,2,4,6,8,10")]),_:1})]),_:1})]),_:1})]),x]),n(",假设以一个字节(十六进制)表示的话,就是下面这个样子的:")]),b,t("p",null,[n("比如我们知道这两个序列是连续放在一起的,第一个序列长度是"),t("span",k,[t("span",S,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("6")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("6")]),_:1})]),_:1})]),_:1})]),v]),n(",同时也知道要找的是第二个序列的第三个数,那么这个数就应该是第"),t("span",P,[t("span",E,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("6")]),_:1}),s(p,null,{default:a(()=>[n("+")]),_:1}),s(l,null,{default:a(()=>[n("3")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("6+3")]),_:1})]),_:1})]),_:1})]),V]),n("个数字。")]),t("p",null,[n("当然,这个数字要从"),t("span",q,[t("span",I,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("1")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("1")]),_:1})]),_:1})]),_:1})]),L]),n("数过去才行,否则从任意其他的位置数,都不能得到我们想要的结果。")]),N,t("p",null,[n("有个重点是,这里的索引是从"),t("span",j,[t("span",R,[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(o,null,{default:a(()=>[s(l,null,{default:a(()=>[n("0")]),_:1})]),_:1}),s(c,{encoding:"application/x-tex"},{default:a(()=>[n("0")]),_:1})]),_:1})]),_:1})]),B]),n("开始,最大是序列长度-1,这其实也就是为什么我们在第三章讲过程的时候提到循环的写法的一个原因。")]),$])}const U=u(D,[["render",w]]);export{Q as __pageData,U as default}; diff --git a/assets/ch06_data.md.8876a824.js b/assets/ch06_data.md.8876a824.js new file mode 100644 index 0000000..3871341 --- /dev/null +++ b/assets/ch06_data.md.8876a824.js @@ -0,0 +1,112 @@ +import{_ as D}from"./chunks/cons-cells.6ed9c323.js";import{_ as C,c as i,z as t,a as n,E as s,B as a,O as e,G as p,o as A}from"./chunks/framework.41379913.js";const T=JSON.parse('{"title":"数据","description":"","frontmatter":{},"headers":[],"relativePath":"ch06_data.md"}'),d={name:"ch06_data.md"},u=e(`

数据

很多时候我们要处理的东西并不只是一个简单的数据,或者是一个由多个数据组成的简单序列。比如,当我们要考虑一间房子,关注的是户型、面积和位置等,考虑一辆汽车,关注的是性价比、能耗和易用性等,考虑一个学生,关注的是他的学号、姓名、各科成绩等。

很多时候我们也需要考虑一系列的符号,比如房屋的户型、汽车的品牌,这些在一定程度上固定的内容,并不需要专门去用字符串处理。

这种时候,序列、引用和各种标量(Scalar)类型都不能很好的满足我们的需要。

我们来做一个简单的示范吧。

结构

比如,我们要表示一辆车,其中包括他的品牌、价格和最高时速。在当前的情况下,最合适的方式就是采用长度为3的array,约定好第0位表示品牌,第1位表示价格,第2位表示最高时速。于是我们可以得到以下的设计:

javascript
let car = [1, 500000, 180]

于是我们就能够很轻松地(并丑陋地)表示汽车了。

这种写法虽然算是比较方便,但是还是有很多问题的。

比如,每次我们都需要多写很多个函数,比如所有的元素都必须是同一个类型的。

所以大多数编程语言都提供结构(Structure)或者记录(Record)类型,来简化我们实现这个的过程。

比如,在JavaScript中我们可以很简单的把Car表示如下:

javascript
let car = {
+    brand: 'Nissan',
+    price: 500000, // USD
+    speed: 180 // MPH
+}

这是一个结构(struct),其中包含了三个域(field,也叫字段)。比起还要反复地写各种函数来,要简单和方便很多。 而且,各个字段的类型可以不同。

这样子我们就能通过Car来表示一辆汽车了:

javascript
car.brand

很简单吧。

那么,结构的本质到底是什么呢?

回到本章开始我们的讨论。房子、汽车、学生,这些概念性的实体(Entity)都有一些自己本身的属性(Property),以及与其他实体之间的关系(Relation)。 我们可以给这些实体的属性和关系贴上一个个标签,把他们与所属的实体之间关联起来。这样就组成了我们现在看到的结构。

当然,他还是数据,但是这些数据通过我们贴上不同的标签以及把他们关联组合,就变成了更加具有实际意义的内容。换句话说,我们给一块二进制内存区赋予了语义(Semantics)。

来让我们看一下其他的许多结构吧。

复数,

cpp
let complex = {
+    real: 1,
+    image: 1
+};

分数,

cpp
let frac = {
+    numerator: 1,
+    denominator: 2
+};

甚至是,用数据代替表达式,

cpp
let arith = {
+    optr: '+'
+    leftOperand: 1,
+    rightOperand: 2
+};

以及,更多复杂的结构。

递归数据类型

看到这里也许你开始担忧了。什么是递归数据结构(Recursive Data Type)?

这里我们要拿前面提到的一个例子来说事儿了。

list

看这张图,去掉这个list的第一个节点,结果得到的还是一个list。同样地,在这个节点上面再加一个节点,得到的也依然还是一个list。

也就是说,这个list的每一个节点大概对应到下面的结构:

cpp
struct ListNode{
+    int value;
+    ListNode* next;
+};

我们试着用这个结构建立一个列表。

cpp
ListNode a{1, nullptr};
+ListNode b{2, &a};
+ListNode c{3, &b};

c出发,我们就能够依次遍历完ba,而且对于每一个节点,处理的方式基本一致。

cpp
void printList(ListNode* list) {
+    if(list == nullptr) return;
+    std::cout << list->value;
+    printList(list->next);
+}

没错,list就是递归的。每一个list节点都包含一个list的引用。这样我们就能够利用递归来对其进行操作了。

比如我们想要构造一个新的list,也可以把他做成递归的。

cpp
struct List{
+    int value;
+    shared_ptr<List> next;
+}
+
+shared_ptr<List> make_cell(int value, shared_ptr<List> next) {
+    auto cell = make_shared<List>();
+    cell->value = value;
+    cell->next = next;
+    return cell;
+}
+
+template<class ...Args>
+shared_ptr<List> make_list(int value, Args... args) {
+    return make_cell(value, make_list(args...));
+}
+
+template<>
+shared_ptr<List> make_list(int value) {
+    return make_cell(value, nullptr);
+}

构造器(Constructor)

看我们的make_cell函数,其实就是创建了一个shared_ptr,然后再利用传入的参数来对cell中的数据进行初始化。当然这意思其实已经很明确了,make_cell作为创建一个新的cell结构的函数而存在。

但是这样未免太罗嗦,每次我创建一个struct的时候都要再去考虑怎么设计一个用来专门创建实例的函数。JavaScript提供了一种叫做构造器的函数,可以简化这个过程。加入了构造器的List定义如下:

cpp
struct {
+    int value;
+    shared_ptr<List> next;
+
+    List(int _value, shared_ptr<List> _next):
+        value(_value), next(_next) {}
+}

这里的构造器是说,我接受两个参数(_next, _value),然后用这两个参数去初始化JavaScript结构(struct)中的字段。

然后我们可以把整个make_cell函数改成

cpp
make_shared<List>(value, next);

构造器能够让我们以一种很直观的方式来创建和使用数据。 比如:

cpp
struct User {
+    std::string name;
+    int age;
+    double height;
+
+    User(std::string _name, int _age, double _height):
+        name(_name), age(_age), height(_height) {}
+};
+
+auto user = User{"Kimmy", 18, 178.0};

没错,这样看上去跟前面定义的Car类型没有什么区别。但是当结合上默认值参数以及需要在数据初始化的时候做一些其他操作的时候,构造器给提供了很直接的方案。后面我们会进一步地看到这种方案的优点。(当然你也可以去看一看什么是RAII。)

递归结构上的操作

好的我们回到刚刚的List上来。

我们已经可以通过make_list来创建一个整数的列表了,那么,作为一个递归的结构,我们当然也可以通过递归来对齐定义一些操作。

比如把List里面的每一个元素做一个转换。

cpp
shared_ptr<List> 
+transform(shared_ptr<List> list, std::function<int(int)> transformer) {
+    if(list == nullptr) return nullptr;
+    return make_shared<List>(transformer(list->value), transform(list->next, tranformer))
+}
+
+
+// => {-1, -2, -3, -4, -5}
+transform(make_list(1,2,3,4,5), [](int i) -> int { return -i; });

作为数据的过程(Procedure as data)

当然,看到上面那段代码你会是满头雾水。std::function<int(int)>是什么,[](){}放在一起又是什么。

回过头去想想,我们是不是可以定义一些过程,然后对这个过程进行调用,去做一些复杂的计算?同时,当我们需要进一步利用这些过程的时候,可以把它们直接当成数据拿来用的。于是我们可以约束他们的类型,然后根据这些约束再进一步地做抽象。

比如第三章中的pow,类型就是std::function<int(int, int)>,接收两个整数,返回他们的乘方,结果也是整数。

而有些时候我们可能并不只是需要拿已经就有的过程来简单地做抽象,所以更有必要提供一种方式让我们随时都能够创建一个过程。于是JavaScript给我们提供了Lambda表达式(Lambda Expression,在其他语言里面也可能叫做匿名函数/过程,Anonymous Function/Procedure),比如

cpp
auto negate=[](int i) -> int{ return -i; };

我们就地创建了一个lambda表达式,然后把它赋值给了negate。这个lambda表达式所表示的就是一个匿名过程,它会接收一个整数,然后返回这个数的相反数。

于是当我们再去回过头看transform的时候,就不会觉得太突兀了。

cpp
shared_ptr<List> 
+transform(shared_ptr<List> list, std::function<int(int)> transformer) {
+    if(list == nullptr) return nullptr;
+    return make_shared<List>(transformer(list->value), transform(list->next, tranformer))
+}

transform接收一个List和一个int(int)类型的transformer,然后在这个List上递归地对每一个元素执行transformer,得到一个新的结果,然后组成一个新的List。

好的我们来看另外一种递归数据类型,树。更确切地说,是二叉树(Binary Tree)。

C++
struct Tree {
+    int value;
+    shared_ptr<Tree> left;
+    shared_ptr<Tree> right;
+
+    Tree(int _v, shared_ptr<Tree> _l, shared_ptr<Tree> _r): 
+        value(_v), left(_l), right(_r) {}
+};

也就是说,Tree是这种结构,他的左侧是Tree,右侧也是Tree。而且Tree的每个节点都会带有一个value。

当然你还是会怀疑说这种无聊的结构拿来有什么用。那我们看下面的内容:

*
+           /   \\
+          +     4
+        /   \\ 
+       1     -
+           /   \\
+          2     3
`,74),h={class:"katex"},g={class:"katex-mathml"},m=e('',1),_=e(`

遍历(Traverse)

对于前面那个树形结构,我们可以试着从最顶端的节点写出他的value,然后再分别访问他的左右子树,按照这样的方式把所有的内容写出来,就能得到下面的结果:

* + 1 - 2 3 4

这种表示方式又叫做该二叉树的先序遍历,又叫前序遍历。

然后我们可以把属于同一个树的部分用括号包起来,就得到了:

scheme
(* (+ 1 (- 2 3)) 4)

没错这就是Lisp。

同样地,我们可以选择先一次访问一棵树的左右子树,然后再写出器对应的value,这样一层层递进下去。我们能够得到如下的结果:

1 2 3 - + 4 *

如果你有了解过的话,就能看得出来这跟forth或者PostScript很像,当然他也叫上面那个二叉树的后序遍历。

当然还有一种叫做中序遍历的过程,得到的结果是:

1 + 2 - 3 * 4

跟最初的算术表达式的区别就在于没有了括号。当然,本来表示的意思也丧失了。

对于前序遍历和后序遍历的结果,并不会改变整个表达式的语义,只是重新定义了这个表达式的解析方式。

练习

请尝试用代码实现先序、中序和后序遍历。

深度优先(Depth-First)和广度优先(Breath-First)

前面我们提到的遍历,是对整个树进行访问的过程。如果我们只需要从根节点出发,然后去寻找某个指定元素的话,可以有这样两种选择。

第一种是遇到有分支,我们就选择一个方向继续往前走,直到走到不能再继续,然后再一步步返回,同时在返回的过程中尝试其他的分支。

这种首先走到最底端然后再慢慢回来的过程叫做深度优先查找(Depth-First Search)。

比如前文中的那个表达式树,整个过程就是

cpp
* + 1 <后退> - 2 <后退> 3 <后退> <后退> <后退> 4

也就是说,我们需要访问整个树才能查找到4。

另外一种方式是,遇到分支,我们先把对应的分支的每一个第一层先访问一遍,再一步步走到下一层。

这样的过程叫做广度优先查找(Breath-First Search)。比如

cpp
* <下一层> + 4 <下一层> 1 - <下一层> 2 3

这个时候我们如果要查找4的话,只要在第一层就可以了。

栈(stack)与队列(queue)

我们仔细思考一下怎么实现DFS和BFS。

对于DFS,我们需要考虑如果找不到如何返回,所以我们可以先试着模拟一下DFS查找4的流程。

cpp
path            description
+==========      ==========
+*               //step 1
+* +             //step 2
+* + 1           //step 3 and 1 has no childs
+* +             //get back to +
+* + -           //step 4
+* + - 2         //step 5 and no childs
+* + -           //get back to -
+* + - 3         //step 6 and no childs
+* + -           //get back to - and no more childs to visit
+* +             //get back to + and no more childs to visit
+*               //get back to *
+* 4             //step 7 found!

可以看到,每次当遇到无法继续的节点,都会有一步回退操作,其实就是把path中加入进去内容的再取出来,这种一般最后进入但是最先退出(Last In First Out,LIFO)的结构我们常常称作是栈(stack)。栈通常会用来辅助实现一些拥有LIFO特性的过程,比如过程调用,也有一个对应的调用栈(call stack)。

比如当我们调用递归函数pow(2, 5),对应的调用栈展开如下:

cpp
pow(2, 5)                 => 2 * 2 * 2 * 2 * 2
+    pow(2, 4)             => 2 * 2 * 2 * 2
+        pow(2, 3)         => 2 * 2 * 2
+            pow(2, 2)     => 2 * 2
+                pow(2, 1) => 2

这也就是在第三章我提到“栈溢出”中的“栈”的含义。当一个调用栈太大而超出了当前系统规定的范围以后,就会造成这种问题。

我们接下来看BFS,模拟一下它查找3的流程:

cpp
path            description
+==========      ========== 
+*               //step 1
+* + 4           //push childs and move to next
++ 4             //step 2
++ 4 1 -         //push childs and move to next
+4 1 -           //step 3 no childs move to next
+1 -             //step 4 no childs move to next
+-               //step 5
+- 2 3           //push childs and move to next
+2 3             //step 6 no childs move to next
+3               //step 7 found!

与DFS的后进先出不同的是,BFS会把先放入的东西给拿出去,也就是先进先出(First In First Out,FIFO)。用于实现FIFO的结构我们一般叫做队列(queue)。当然队列的应用也很广泛,特别是需要考虑进入的先后顺序,通常都是作为一种特殊的队列来处理。

树反应了具有某种特定结构的层级关系。当然这种结构并不一定是只有两个分支的,也并不一定非得是考虑顺序的。当我们在建模的时候,遇到层级结构,就可以考虑设计成一棵树。另外,递归。

练习

尝试用代码实现DFS和BFS。

`,40);function f(b,v,B,k,E,x){const l=p("mo"),o=p("mn"),c=p("mrow"),r=p("annotation"),F=p("semantics"),y=p("math");return A(),i("div",null,[u,t("p",null,[n("这个就是把 "),t("span",h,[t("span",g,[s(y,null,{default:a(()=>[s(F,null,{default:a(()=>[s(c,null,{default:a(()=>[s(l,null,{default:a(()=>[n("(")]),_:1}),s(o,null,{default:a(()=>[n("1")]),_:1}),s(l,null,{default:a(()=>[n("+")]),_:1}),s(l,null,{default:a(()=>[n("(")]),_:1}),s(o,null,{default:a(()=>[n("2")]),_:1}),s(l,null,{default:a(()=>[n("−")]),_:1}),s(o,null,{default:a(()=>[n("3")]),_:1}),s(l,null,{default:a(()=>[n(")")]),_:1}),s(l,null,{default:a(()=>[n(")")]),_:1}),s(l,null,{default:a(()=>[n("×")]),_:1}),s(o,null,{default:a(()=>[n("4")]),_:1})]),_:1}),s(r,{encoding:"application/x-tex"},{default:a(()=>[n("(1+(2-3))\\times4")]),_:1})]),_:1})]),_:1})]),m]),n("作为一种树行结构来表示。")]),_])}const S=C(d,[["render",f]]);export{T as __pageData,S as default}; diff --git a/assets/ch06_data.md.8876a824.lean.js b/assets/ch06_data.md.8876a824.lean.js new file mode 100644 index 0000000..d480433 --- /dev/null +++ b/assets/ch06_data.md.8876a824.lean.js @@ -0,0 +1 @@ +import{_ as D}from"./chunks/cons-cells.6ed9c323.js";import{_ as C,c as i,z as t,a as n,E as s,B as a,O as e,G as p,o as A}from"./chunks/framework.41379913.js";const T=JSON.parse('{"title":"数据","description":"","frontmatter":{},"headers":[],"relativePath":"ch06_data.md"}'),d={name:"ch06_data.md"},u=e("",74),h={class:"katex"},g={class:"katex-mathml"},m=e("",1),_=e("",40);function f(b,v,B,k,E,x){const l=p("mo"),o=p("mn"),c=p("mrow"),r=p("annotation"),F=p("semantics"),y=p("math");return A(),i("div",null,[u,t("p",null,[n("这个就是把 "),t("span",h,[t("span",g,[s(y,null,{default:a(()=>[s(F,null,{default:a(()=>[s(c,null,{default:a(()=>[s(l,null,{default:a(()=>[n("(")]),_:1}),s(o,null,{default:a(()=>[n("1")]),_:1}),s(l,null,{default:a(()=>[n("+")]),_:1}),s(l,null,{default:a(()=>[n("(")]),_:1}),s(o,null,{default:a(()=>[n("2")]),_:1}),s(l,null,{default:a(()=>[n("−")]),_:1}),s(o,null,{default:a(()=>[n("3")]),_:1}),s(l,null,{default:a(()=>[n(")")]),_:1}),s(l,null,{default:a(()=>[n(")")]),_:1}),s(l,null,{default:a(()=>[n("×")]),_:1}),s(o,null,{default:a(()=>[n("4")]),_:1})]),_:1}),s(r,{encoding:"application/x-tex"},{default:a(()=>[n("(1+(2-3))\\times4")]),_:1})]),_:1})]),_:1})]),m]),n("作为一种树行结构来表示。")]),_])}const S=C(d,[["render",f]]);export{T as __pageData,S as default}; diff --git a/assets/ch07_state.md.5efbe2c2.js b/assets/ch07_state.md.5efbe2c2.js new file mode 100644 index 0000000..9923bfb --- /dev/null +++ b/assets/ch07_state.md.5efbe2c2.js @@ -0,0 +1 @@ +import{_ as t,c as a,o as s,z as e,a as o}from"./chunks/framework.41379913.js";const $=JSON.parse('{"title":"状态","description":"","frontmatter":{},"headers":[],"relativePath":"ch07_state.md"}'),r={name:"ch07_state.md"},c=e("h1",{id:"状态",tabindex:"-1"},[o("状态 "),e("a",{class:"header-anchor",href:"#状态","aria-label":'Permalink to "状态"'},"​")],-1),n=e("p",null,"程序中的很多数据往往是在不停地变化的,比如时间,比如与用户的交互,又比如,一些程序运行中产生和保存的数据。当一些可变的数据或者对象是发生改变的时候,我们就可以说它们从一个状态转换到了另一个状态。",-1),_=e("p",null,"一个简单的例子就是迭代器(iterator)。这个JavaScript里面非常重要的概念,就是一个典型的可变对象。每一次需要向前或者向后访问一个序列的元素的时候,都需要改变一下迭代器的状态。",-1),i=[c,n,_];function d(l,h,p,m,f,u){return s(),a("div",null,i)}const k=t(r,[["render",d]]);export{$ as __pageData,k as default}; diff --git a/assets/ch07_state.md.5efbe2c2.lean.js b/assets/ch07_state.md.5efbe2c2.lean.js new file mode 100644 index 0000000..9923bfb --- /dev/null +++ b/assets/ch07_state.md.5efbe2c2.lean.js @@ -0,0 +1 @@ +import{_ as t,c as a,o as s,z as e,a as o}from"./chunks/framework.41379913.js";const $=JSON.parse('{"title":"状态","description":"","frontmatter":{},"headers":[],"relativePath":"ch07_state.md"}'),r={name:"ch07_state.md"},c=e("h1",{id:"状态",tabindex:"-1"},[o("状态 "),e("a",{class:"header-anchor",href:"#状态","aria-label":'Permalink to "状态"'},"​")],-1),n=e("p",null,"程序中的很多数据往往是在不停地变化的,比如时间,比如与用户的交互,又比如,一些程序运行中产生和保存的数据。当一些可变的数据或者对象是发生改变的时候,我们就可以说它们从一个状态转换到了另一个状态。",-1),_=e("p",null,"一个简单的例子就是迭代器(iterator)。这个JavaScript里面非常重要的概念,就是一个典型的可变对象。每一次需要向前或者向后访问一个序列的元素的时候,都需要改变一下迭代器的状态。",-1),i=[c,n,_];function d(l,h,p,m,f,u){return s(),a("div",null,i)}const k=t(r,[["render",d]]);export{$ as __pageData,k as default}; diff --git a/assets/ch08_reference.md.ea346374.js b/assets/ch08_reference.md.ea346374.js new file mode 100644 index 0000000..b289be6 --- /dev/null +++ b/assets/ch08_reference.md.ea346374.js @@ -0,0 +1 @@ +import{_ as t,c as a,o as r,z as e,a as c}from"./chunks/framework.41379913.js";const k=JSON.parse('{"title":"引用","description":"","frontmatter":{},"headers":[],"relativePath":"ch08_reference.md"}'),o={name:"ch08_reference.md"},n=e("h1",{id:"引用",tabindex:"-1"},[c("引用 "),e("a",{class:"header-anchor",href:"#引用","aria-label":'Permalink to "引用"'},"​")],-1),s=e("blockquote",null,[e("p",null,"注:本文中提及到的引用,是广义的“引用”概念,而不仅仅是JavaScript的引用。")],-1),_=e("p",null,"TODO: 重写",-1),l=[n,s,_];function d(i,h,p,f,m,u){return r(),a("div",null,l)}const $=t(o,[["render",d]]);export{k as __pageData,$ as default}; diff --git a/assets/ch08_reference.md.ea346374.lean.js b/assets/ch08_reference.md.ea346374.lean.js new file mode 100644 index 0000000..b289be6 --- /dev/null +++ b/assets/ch08_reference.md.ea346374.lean.js @@ -0,0 +1 @@ +import{_ as t,c as a,o as r,z as e,a as c}from"./chunks/framework.41379913.js";const k=JSON.parse('{"title":"引用","description":"","frontmatter":{},"headers":[],"relativePath":"ch08_reference.md"}'),o={name:"ch08_reference.md"},n=e("h1",{id:"引用",tabindex:"-1"},[c("引用 "),e("a",{class:"header-anchor",href:"#引用","aria-label":'Permalink to "引用"'},"​")],-1),s=e("blockquote",null,[e("p",null,"注:本文中提及到的引用,是广义的“引用”概念,而不仅仅是JavaScript的引用。")],-1),_=e("p",null,"TODO: 重写",-1),l=[n,s,_];function d(i,h,p,f,m,u){return r(),a("div",null,l)}const $=t(o,[["render",d]]);export{k as __pageData,$ as default}; diff --git a/assets/ch09_closure.md.a7117936.js b/assets/ch09_closure.md.a7117936.js new file mode 100644 index 0000000..eaaa5f8 --- /dev/null +++ b/assets/ch09_closure.md.a7117936.js @@ -0,0 +1 @@ +import{_ as a,c as t,o,z as e,a as r}from"./chunks/framework.41379913.js";const u=JSON.parse('{"title":"闭包","description":"","frontmatter":{},"headers":[],"relativePath":"ch09_closure.md"}'),s={name:"ch09_closure.md"},c=e("h1",{id:"闭包",tabindex:"-1"},[r("闭包 "),e("a",{class:"header-anchor",href:"#闭包","aria-label":'Permalink to "闭包"'},"​")],-1),n=[c];function _(d,i,l,h,p,m){return o(),t("div",null,n)}const x=a(s,[["render",_]]);export{u as __pageData,x as default}; diff --git a/assets/ch09_closure.md.a7117936.lean.js b/assets/ch09_closure.md.a7117936.lean.js new file mode 100644 index 0000000..eaaa5f8 --- /dev/null +++ b/assets/ch09_closure.md.a7117936.lean.js @@ -0,0 +1 @@ +import{_ as a,c as t,o,z as e,a as r}from"./chunks/framework.41379913.js";const u=JSON.parse('{"title":"闭包","description":"","frontmatter":{},"headers":[],"relativePath":"ch09_closure.md"}'),s={name:"ch09_closure.md"},c=e("h1",{id:"闭包",tabindex:"-1"},[r("闭包 "),e("a",{class:"header-anchor",href:"#闭包","aria-label":'Permalink to "闭包"'},"​")],-1),n=[c];function _(d,i,l,h,p,m){return o(),t("div",null,n)}const x=a(s,[["render",_]]);export{u as __pageData,x as default}; diff --git a/assets/ch10_object.md.a4f3746e.js b/assets/ch10_object.md.a4f3746e.js new file mode 100644 index 0000000..9171b61 --- /dev/null +++ b/assets/ch10_object.md.a4f3746e.js @@ -0,0 +1,21 @@ +import{_ as s,c as a,o as n,O as l}from"./chunks/framework.41379913.js";const h=JSON.parse('{"title":"对象","description":"","frontmatter":{},"headers":[],"relativePath":"ch10_object.md"}'),p={name:"ch10_object.md"},o=l(`

对象

广义地讲,任何一个在程序中出现的元素都被称作对象。于是对象也拥有自己的类型,比如整数对象、类型对象(对象的类型也算是类型,first-class type)以及函数对象等。

但是对象在另外一个编程流派也有着特殊的含义。

面向对象编程(Object-Oriented Programming)

面向对象编程是当前比较热门的一个编程范式(Programming Paradigm)。主要涉及通过抽象(abstraction)和多态(polymorphism)来进行程序建模和设计,以达到组件(Component)复用的目的。

简单地讲,就是面向对象会通过一种方式来定义一些共有的接口和符合接口的操作,然后通过对这些接口的实现来完成一些具有公共特性但细节上略有不一致的行为。

先看一个简单的例子。

不同的车(Vehicle)都可以走(Run),包括汽车(Car)、卡车(Truck)、推车(Wagon)等等。

我们可以简单地描述这种关系:

cpp
// Pseudo code
+interface Vehicle {
+    void Run();
+}
+
+class Car is Vehicle {
+    void Run() override { ... }
+}
+
+class Truck is Vehicle {
+    void Run() override { ... }
+}
+
+class Wagon is Vehicle {
+    void Run() override { ... }
+}
+
+
+Vehicle v = new Car{} // or new Wagon/Truck
+
+v->Run(); // run specific Run prodeure

基于继承(Inheritance)的多态

基于泛型(Generics)的多态

`,12),e=[o];function c(t,r,F,i,y,D){return n(),a("div",null,e)}const C=s(p,[["render",c]]);export{h as __pageData,C as default}; diff --git a/assets/ch10_object.md.a4f3746e.lean.js b/assets/ch10_object.md.a4f3746e.lean.js new file mode 100644 index 0000000..992f82f --- /dev/null +++ b/assets/ch10_object.md.a4f3746e.lean.js @@ -0,0 +1 @@ +import{_ as s,c as a,o as n,O as l}from"./chunks/framework.41379913.js";const h=JSON.parse('{"title":"对象","description":"","frontmatter":{},"headers":[],"relativePath":"ch10_object.md"}'),p={name:"ch10_object.md"},o=l("",12),e=[o];function c(t,r,F,i,y,D){return n(),a("div",null,e)}const C=s(p,[["render",c]]);export{h as __pageData,C as default}; diff --git a/assets/ch11_concurrency.md.2b82afad.js b/assets/ch11_concurrency.md.2b82afad.js new file mode 100644 index 0000000..9af4b66 --- /dev/null +++ b/assets/ch11_concurrency.md.2b82afad.js @@ -0,0 +1 @@ +import{_ as a,c,o as t,z as e,a as r}from"./chunks/framework.41379913.js";const u=JSON.parse('{"title":"并发","description":"","frontmatter":{},"headers":[],"relativePath":"ch11_concurrency.md"}'),o={name:"ch11_concurrency.md"},n=e("h1",{id:"并发",tabindex:"-1"},[r("并发 "),e("a",{class:"header-anchor",href:"#并发","aria-label":'Permalink to "并发"'},"​")],-1),s=[n];function _(d,i,h,l,p,m){return t(),c("div",null,s)}const x=a(o,[["render",_]]);export{u as __pageData,x as default}; diff --git a/assets/ch11_concurrency.md.2b82afad.lean.js b/assets/ch11_concurrency.md.2b82afad.lean.js new file mode 100644 index 0000000..9af4b66 --- /dev/null +++ b/assets/ch11_concurrency.md.2b82afad.lean.js @@ -0,0 +1 @@ +import{_ as a,c,o as t,z as e,a as r}from"./chunks/framework.41379913.js";const u=JSON.parse('{"title":"并发","description":"","frontmatter":{},"headers":[],"relativePath":"ch11_concurrency.md"}'),o={name:"ch11_concurrency.md"},n=e("h1",{id:"并发",tabindex:"-1"},[r("并发 "),e("a",{class:"header-anchor",href:"#并发","aria-label":'Permalink to "并发"'},"​")],-1),s=[n];function _(d,i,h,l,p,m){return t(),c("div",null,s)}const x=a(o,[["render",_]]);export{u as __pageData,x as default}; diff --git a/assets/chunks/cons-cells.6ed9c323.js b/assets/chunks/cons-cells.6ed9c323.js new file mode 100644 index 0000000..59d2697 --- /dev/null +++ b/assets/chunks/cons-cells.6ed9c323.js @@ -0,0 +1 @@ +const A="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAABOCAYAAABR7c9uAAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH2QwBDhAraEmrOAAADORJREFUeJztnX+wFWUZxz9c8BoXIQTBG+Ilc8zBRv8A8keh9GNMxXRAy5QcS6ZsnMnMbAy11MyUpvydlJZlNtpYpqXmpCg2SUqKjIlJ8UMyNYSQrijChXvP6Y/nrOzds3v2fd89u+/h7POZ2bnnnn139znvs/t9n/fnAlR1q9vuoHh6Mtjb7G17C9gQbAuyZKojB2ewt9nbphawIdjmZcnUVmRY6PMt3qyAs0KfXwAW+zKEwbb4wrcvdqt9/iPwb8+2+Ma3LzpawI4e4FiP188VX6VsmKvYWcpM92xLL34jQN+l7B3A1potPZ5t8R0Bnu3h2mEeBzbWbPFJq9ybTacjPYmiKEp7ogKoKEppUQFUFKW0qAAqilJaVAAVRSktKoCKopQWFUBFUUqLCqCiKKVlWHqS1OOnAd3AeuBpoD+rUY6MqdmyO7ACWO3JDoD9gdOB65GB1UXQCRwK7AW8CjwDVAq6dpS9gSlIAbscv7NJPgwcDvwIeLuga3YBHwT2BNYCz+FvMHMPMrC7AixDnlNfzAZmAPOB1zzaMQiX0fZDkVHhvQyeK7gZuLi234YsM0EmAvcAAxFbngc+YnkuaM5MkPBc0kuBdxsc4zravhO4EtjC4N+/ETgHGGJ5viwzQQ4EHkEetrAtTyCCaEszZoIcH7LjfESc0nCdCbIHcBPQx+Df/ypSINqSZSbINODJiB0VxD8HWp6rWTNB5oRsuQ54j+FxVeDulP1xn+P+jz3Y5iYbAtxJveAEWz9wL3YPnqsA9gD/JX7yfn/NxlkW54PmC2AVeIN0IXS5yTqBRchvjfNFBXsBcRXAg5ECcEeMHf2IKBxlaUuzBbAKbCBdCF0EsAv4G/G/PygQvmlpu6sAfhTJ77j7Ygfip4MtzpeHAFaBbZgJYSCApzTYH/c57v/Yg21usjOId3L0hp9rcU5XAXyc9JVL3kKqhabkIYDBA9BICF1usgtI//39wEyLc7oIYAfwTxrfFwNIteddFrbkIYBBwd1ICF0E8AbSfVEBplqc00UAu5BqblKAEojgCsz7APISwOC5SBPCKtKs8g9gXML+uM9x/w/CthOkAxGrtLbDoUg937YqbMMMpH1nt5R0w4Cv52iHCUE0PBK4DGkTS4sI0+gCvkX67+8AfpDhOiacAuxH4/uiAxgNfClnW9II7vmxSL78C/OqcRLdiFim+aICfC/DdUw4C7mvGj3bw5B26k/nbEsawXPRCZyLtJcmCeF6pKnnpmYaYNsJcggwwTDtOKQd4q+W1zBlFlKSdaak2x0peUyXE+pA2nLe524aExO+jwrh15AIZ77DNWYgdqYxBJiMlOJ5dUachFlhtzvSFna/xblHkc0X3QnfR4VwHnAt8gDaYrpU1FCkXbqL/DpkPoXkcxodiC+eNkgbPPNjyOaL8QnfR4XwXOA24CJgXSjd7cBpwMnAbzPYMQibasZxNA7xo9tsw/O6VIHvpL6xfVfdXgaOxK6acSbpTRHh7UOG53WpAj9hYUerby8gbWhVzKvA85BqnOk1Jhme16UKvNbCjlbfFiLRbPj37wusYnCzVjXhc9z/g7CNANelJxlEnl3d6zGLAEF64Uwb4J8FHgPOc7QLpJftwQb7q0iptxX4CXA5MMLyGq9h57//WJ7fhleQwsikSWUZ5lWvNYggX+JoF4iY/bTB/sAXbyDVq/nAey2vsQHz5p4KImp5sQ4z+yvIgrfnGKSdgIjx94EfO1sGJyJRdiObOpAC9VKkxzrKy8DVwA+BUzPYAtgL4HOI8OxtkHYj8JS1ReY8AHzFIF0fEi2+aHjeCtJxYpo+jiQxCwvf9YgjX085JonHkWpUWttVFViJtHXlxe+QJok0AexDBM0mbzdbpo8yOeH7wBe9iB9uAN50vMZDhukGEL9tcbyOCfcgQ47SqsEDmPsiGNu7yTB9EhsSvg+EbwnJwhfmZkT8ZiMjTpyx7QSpABcimdeIHch4wLR0WXgUybAdKekGkJLLJ0EYvhWp7vcg7RuvJx6RzlvAFaT//irSW5wndwEv0djfFSTKyhJBNIPAF73IsJRJwHdxFz+QGsbNSC9wIzqAb2S4jgkLkN/SaBD8AFJVvitnW9IIbHwSOBrp1EwTPxAffgEpuMZkMcBlKtxtwO9JzuAB4GHgVkebbPgcEiHEzT4ZQGyci4wV9EkzhS/M1cjNkzT7pgL8DLivSddLYgCp1gZjz6IEYzJPo7jZGEk0U/jCXITMPor7/YHoXkG+tSKQ/P0Mkt9JvugLpfFJIHzTMRO+MKuRJosbsxjgIoBVZNjDJdSH8m8jXdWzKCZzVyPjqh6hvrFzFdI757uUy0P4ArYDxyA9l32Rfb3I8I6iXiz0LNLrv5h6XyxHbvJFBdmSRB7CF7AZOAL4BfXCswGJWLK0ZdqwCMnv5ZHvq4h/piH+8sU63IUvzHXIcJ40kjpZ3tnpOti0E+mSriK9kibd73E046VI44FPIkMyDnI8BzRnIPQBSEEw1uKYrINNhyNtolXk5nKd592MlyJNRBq8Z2F2gyaR5d4MmI4UPiMtjsn6UqSR7Lynp+G+6EgzXoq0P+KHE0kenpVGswZCHwp8POM5mkrWxRC2s3Mc0VLqo5Ai2YB0jLQCq5CHrki2Ij2sINUsX4tSgPQKv+Lx+mEWU/xrVt9kZ/S1tOBrR1lT21qBvKv/1uhyWIqilBYVQEVRSosKoKIopUUFUFGU0qICqChKaVEBVBSltKgAKopSWlQAFUUpLcFA6LORqTwujKr9/Sruc27DE8TnU/zA1TBZVmluBlchKye7EIz0vwz3QelzQp/n4/eNbr6Zj/nafVGC2Ugui90GTEcWkMh6nqy4zgjaJfC96GErblmnwrkQTDdqhc1modW8t6xT4VyIvtPF57apBWwItqxT4dqS4MG1ecuUkg/TEV/4jmKVnS8AUloYbQNUFKW0qAAqilJaVAAVRSktKoCKopQWFUBFUUqLCqCiKKVFBVBRlNKiAqgoSmlRAVQUpbSoACqKUlpUABVFKS0qgIqilBYVQEVRSosKoKIopWVYepJB7AHsFvkuWHppFLBnZF8V6HWwS0lnFDA08t3I2t/R1BduA7gveqs0ZjQwJPLdiNrf6DMB0A+8matFSi5cgN0Cir/2Y2YpuBY7X9zgx8xScCd2vrjIj5lKVkZg52hdJDU/urHzxQQ/ZpaCydj5YmT8aZRdAZMosIJGf0VwDRr9tQqmUeCFvgxUmsMI5OVHAzQWQI3+8qcb2Ibkd5IftqHRXxFMJtkPgS82odFfW9AoCtTor1jSokCN/oojLQrU6K9NaBQFavRXLI2iQI3+iiUpCtTorw2JiwIrwG98GlVSkqJAjf6K51do9FcK4qJAjf78EBcFavTnh2gUqNFfGxOOAjX680s0CtTozx/RKFCjvzYliAJ13J9/uoGtaPTXCoSjQI3+2px5iKMX+zZEYQHii1t9G6LwKOKLi30bouTLCGAdGv21At1IxKHRn38mAxvQ6K8UTPVtgPIOU3wboLyDPheKoiiKoiiKoiiKouyqTATWIj1cUY4C7kKGxvQBzwNfpn6xSCU7xwMPIZ0e25Be+BNC+08Fltb2/Q+4G9ivYBvblbS8n4L0yPcS/5ycBNwLvA5sB1YgY2qjC9wqLcj9wOeJd2wVWAgchqwcfRDwJ+CSgmwrC98BFgEzkF74EcAngAdr++cCLwLHAV21bSbyoMatUKyYk5b3AH8HLgM+QPxzch9wOrAPsir7+4E/ANflZbTSHE4FHq59jnPsVdRHe/sA6/M0qmQcDSyh8esMViGFUJQjgG/nYVRJMMn7KHHPSRzjgTesLVIKYyxS9Q2qUaaOHYe+i6KZ3A/MSkmznfp3twB0ItVixQ2TvI9i+pyMRarESoHYvBXuWuBGRARtOB/4peUxSjKHAcOR6uzbwBbgL8DsUJqXiB8POAU4IG8D2xiTvLdlCBJU3AL8PKuBSj4cAzzF4EZak5JtDrAcHQ3fTPqRKm60fW8tcEYtzZnAGuBY5IEdXku/BthRsL3thEneR2n0nIQXTFiOvOlPaUFWAodEvksTwDnIzbJvLhaVl80kt++tDP1/MhKdbEGilcXAZ5HpWYobpnkfJu056QAOBB4Abnc3TcmTtBe9RPkiEm1MKsrAEvEMye1721OOnY20YyluuOS9aRvgXmhb+S5FkmPPA1YDPQXaUiYuJz4KOZzkKASk53IJMmRDccMl700FcAKw0cUoxQ9xjr0YuREmFmxLmRgD/Jn69r21yPhMkDFlH2NnO9WRyNi1Kwu2td0wyfsocc/JvYhPupDocSrwGDKMTNlFSBoInbSNLs60tmc/ZGZHLzLjZhlwWmj/CUibXx8yC2QhcGLBNrYraXkf0KipaCYieFuR9tllwDnojKnC+T99VG+4pps5MAAAAABJRU5ErkJggg==";export{A as _}; diff --git a/assets/chunks/framework.41379913.js b/assets/chunks/framework.41379913.js new file mode 100644 index 0000000..8830dad --- /dev/null +++ b/assets/chunks/framework.41379913.js @@ -0,0 +1,2 @@ +function Kn(e,t){const n=Object.create(null),s=e.split(",");for(let r=0;r!!n[r.toLowerCase()]:r=>!!n[r]}function kn(e){if(N(e)){const t={};for(let n=0;n{if(n){const s=n.split(_i);s.length>1&&(t[s[0].trim()]=s[1].trim())}}),t}function Wn(e){let t="";if(re(e))t=e;else if(N(e))for(let n=0;nre(e)?e:e==null?"":N(e)||ee(e)&&(e.toString===lr||!U(e.toString))?JSON.stringify(e,rr,2):String(e),rr=(e,t)=>t&&t.__v_isRef?rr(e,t.value):ut(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[s,r])=>(n[`${s} =>`]=r,n),{})}:ir(t)?{[`Set(${t.size})`]:[...t.values()]}:ee(t)&&!N(t)&&!cr(t)?String(t):t,te={},ft=[],Ie=()=>{},xi=()=>!1,Ei=/^on[^a-z]/,Lt=e=>Ei.test(e),qn=e=>e.startsWith("onUpdate:"),ce=Object.assign,Vn=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},vi=Object.prototype.hasOwnProperty,V=(e,t)=>vi.call(e,t),N=Array.isArray,ut=e=>rn(e)==="[object Map]",ir=e=>rn(e)==="[object Set]",U=e=>typeof e=="function",re=e=>typeof e=="string",zn=e=>typeof e=="symbol",ee=e=>e!==null&&typeof e=="object",or=e=>ee(e)&&U(e.then)&&U(e.catch),lr=Object.prototype.toString,rn=e=>lr.call(e),Ti=e=>rn(e).slice(8,-1),cr=e=>rn(e)==="[object Object]",Yn=e=>re(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Et=Kn(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),on=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Ai=/-(\w)/g,Se=on(e=>e.replace(Ai,(t,n)=>n?n.toUpperCase():"")),Ri=/\B([A-Z])/g,it=on(e=>e.replace(Ri,"-$1").toLowerCase()),ln=on(e=>e.charAt(0).toUpperCase()+e.slice(1)),Vt=on(e=>e?`on${ln(e)}`:""),It=(e,t)=>!Object.is(e,t),Cn=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},Ii=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Oi=e=>{const t=re(e)?Number(e):NaN;return isNaN(t)?e:t};let ms;const Pi=()=>ms||(ms=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});let _e;class Fi{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=_e,!t&&_e&&(this.index=(_e.scopes||(_e.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=_e;try{return _e=this,t()}finally{_e=n}}}on(){_e=this}off(){_e=this.parent}stop(t){if(this._active){let n,s;for(n=0,s=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},fr=e=>(e.w&qe)>0,ur=e=>(e.n&qe)>0,Li=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let s=0;s{(d==="length"||d>=f)&&l.push(a)})}else switch(n!==void 0&&l.push(o.get(n)),t){case"add":N(e)?Yn(n)&&l.push(o.get("length")):(l.push(o.get(st)),ut(e)&&l.push(o.get(Fn)));break;case"delete":N(e)||(l.push(o.get(st)),ut(e)&&l.push(o.get(Fn)));break;case"set":ut(e)&&l.push(o.get(st));break}if(l.length===1)l[0]&&Mn(l[0]);else{const f=[];for(const a of l)a&&f.push(...a);Mn(Jn(f))}}function Mn(e,t){const n=N(e)?e:[...e];for(const s of n)s.computed&&bs(s);for(const s of n)s.computed||bs(s)}function bs(e,t){(e!==Ae||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}function Hi(e,t){var n;return(n=Zt.get(e))===null||n===void 0?void 0:n.get(t)}const $i=Kn("__proto__,__v_isRef,__isVue"),hr=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(zn)),ji=Zn(),Ui=Zn(!1,!0),Bi=Zn(!0),ys=Di();function Di(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const s=z(this);for(let i=0,o=this.length;i{e[t]=function(...n){bt();const s=z(this)[t].apply(this,n);return yt(),s}}),e}function Ki(e){const t=z(this);return pe(t,"has",e),t.hasOwnProperty(e)}function Zn(e=!1,t=!1){return function(s,r,i){if(r==="__v_isReactive")return!e;if(r==="__v_isReadonly")return e;if(r==="__v_isShallow")return t;if(r==="__v_raw"&&i===(e?t?ro:br:t?_r:mr).get(s))return s;const o=N(s);if(!e){if(o&&V(ys,r))return Reflect.get(ys,r,i);if(r==="hasOwnProperty")return Ki}const l=Reflect.get(s,r,i);return(zn(r)?hr.has(r):$i(r))||(e||pe(s,"get",r),t)?l:oe(l)?o&&Yn(r)?l:l.value:ee(l)?e?yr(l):fn(l):l}}const ki=pr(),Wi=pr(!0);function pr(e=!1){return function(n,s,r,i){let o=n[s];if(gt(o)&&oe(o)&&!oe(r))return!1;if(!e&&(!Qt(r)&&!gt(r)&&(o=z(o),r=z(r)),!N(n)&&oe(o)&&!oe(r)))return o.value=r,!0;const l=N(n)&&Yn(s)?Number(s)e,cn=e=>Reflect.getPrototypeOf(e);function $t(e,t,n=!1,s=!1){e=e.__v_raw;const r=z(e),i=z(t);n||(t!==i&&pe(r,"get",t),pe(r,"get",i));const{has:o}=cn(r),l=s?Qn:n?ts:Ot;if(o.call(r,t))return l(e.get(t));if(o.call(r,i))return l(e.get(i));e!==r&&e.get(t)}function jt(e,t=!1){const n=this.__v_raw,s=z(n),r=z(e);return t||(e!==r&&pe(s,"has",e),pe(s,"has",r)),e===r?n.has(e):n.has(e)||n.has(r)}function Ut(e,t=!1){return e=e.__v_raw,!t&&pe(z(e),"iterate",st),Reflect.get(e,"size",e)}function ws(e){e=z(e);const t=z(this);return cn(t).has.call(t,e)||(t.add(e),He(t,"add",e,e)),this}function Cs(e,t){t=z(t);const n=z(this),{has:s,get:r}=cn(n);let i=s.call(n,e);i||(e=z(e),i=s.call(n,e));const o=r.call(n,e);return n.set(e,t),i?It(t,o)&&He(n,"set",e,t):He(n,"add",e,t),this}function xs(e){const t=z(this),{has:n,get:s}=cn(t);let r=n.call(t,e);r||(e=z(e),r=n.call(t,e)),s&&s.call(t,e);const i=t.delete(e);return r&&He(t,"delete",e,void 0),i}function Es(){const e=z(this),t=e.size!==0,n=e.clear();return t&&He(e,"clear",void 0,void 0),n}function Bt(e,t){return function(s,r){const i=this,o=i.__v_raw,l=z(o),f=t?Qn:e?ts:Ot;return!e&&pe(l,"iterate",st),o.forEach((a,d)=>s.call(r,f(a),f(d),i))}}function Dt(e,t,n){return function(...s){const r=this.__v_raw,i=z(r),o=ut(i),l=e==="entries"||e===Symbol.iterator&&o,f=e==="keys"&&o,a=r[e](...s),d=n?Qn:t?ts:Ot;return!t&&pe(i,"iterate",f?Fn:st),{next(){const{value:p,done:y}=a.next();return y?{value:p,done:y}:{value:l?[d(p[0]),d(p[1])]:d(p),done:y}},[Symbol.iterator](){return this}}}}function je(e){return function(...t){return e==="delete"?!1:this}}function Xi(){const e={get(i){return $t(this,i)},get size(){return Ut(this)},has:jt,add:ws,set:Cs,delete:xs,clear:Es,forEach:Bt(!1,!1)},t={get(i){return $t(this,i,!1,!0)},get size(){return Ut(this)},has:jt,add:ws,set:Cs,delete:xs,clear:Es,forEach:Bt(!1,!0)},n={get(i){return $t(this,i,!0)},get size(){return Ut(this,!0)},has(i){return jt.call(this,i,!0)},add:je("add"),set:je("set"),delete:je("delete"),clear:je("clear"),forEach:Bt(!0,!1)},s={get(i){return $t(this,i,!0,!0)},get size(){return Ut(this,!0)},has(i){return jt.call(this,i,!0)},add:je("add"),set:je("set"),delete:je("delete"),clear:je("clear"),forEach:Bt(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(i=>{e[i]=Dt(i,!1,!1),n[i]=Dt(i,!0,!1),t[i]=Dt(i,!1,!0),s[i]=Dt(i,!0,!0)}),[e,n,t,s]}const[Zi,Qi,Gi,eo]=Xi();function Gn(e,t){const n=t?e?eo:Gi:e?Qi:Zi;return(s,r,i)=>r==="__v_isReactive"?!e:r==="__v_isReadonly"?e:r==="__v_raw"?s:Reflect.get(V(n,r)&&r in s?n:s,r,i)}const to={get:Gn(!1,!1)},no={get:Gn(!1,!0)},so={get:Gn(!0,!1)},mr=new WeakMap,_r=new WeakMap,br=new WeakMap,ro=new WeakMap;function io(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function oo(e){return e.__v_skip||!Object.isExtensible(e)?0:io(Ti(e))}function fn(e){return gt(e)?e:es(e,!1,gr,to,mr)}function lo(e){return es(e,!1,Ji,no,_r)}function yr(e){return es(e,!0,Yi,so,br)}function es(e,t,n,s,r){if(!ee(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=r.get(e);if(i)return i;const o=oo(e);if(o===0)return e;const l=new Proxy(e,o===2?s:n);return r.set(e,l),l}function at(e){return gt(e)?at(e.__v_raw):!!(e&&e.__v_isReactive)}function gt(e){return!!(e&&e.__v_isReadonly)}function Qt(e){return!!(e&&e.__v_isShallow)}function wr(e){return at(e)||gt(e)}function z(e){const t=e&&e.__v_raw;return t?z(t):e}function vt(e){return Xt(e,"__v_skip",!0),e}const Ot=e=>ee(e)?fn(e):e,ts=e=>ee(e)?yr(e):e;function ns(e){ke&&Ae&&(e=z(e),dr(e.dep||(e.dep=Jn())))}function ss(e,t){e=z(e);const n=e.dep;n&&Mn(n)}function oe(e){return!!(e&&e.__v_isRef===!0)}function Cr(e){return xr(e,!1)}function co(e){return xr(e,!0)}function xr(e,t){return oe(e)?e:new fo(e,t)}class fo{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:z(t),this._value=n?t:Ot(t)}get value(){return ns(this),this._value}set value(t){const n=this.__v_isShallow||Qt(t)||gt(t);t=n?t:z(t),It(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Ot(t),ss(this))}}function uo(e){return oe(e)?e.value:e}const ao={get:(e,t,n)=>uo(Reflect.get(e,t,n)),set:(e,t,n,s)=>{const r=e[t];return oe(r)&&!oe(n)?(r.value=n,!0):Reflect.set(e,t,n,s)}};function Er(e){return at(e)?e:new Proxy(e,ao)}class ho{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:s}=t(()=>ns(this),()=>ss(this));this._get=n,this._set=s}get value(){return this._get()}set value(t){this._set(t)}}function vc(e){return new ho(e)}class po{constructor(t,n,s){this._object=t,this._key=n,this._defaultValue=s,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return Hi(z(this._object),this._key)}}function Tc(e,t,n){const s=e[t];return oe(s)?s:new po(e,t,n)}var vr;class go{constructor(t,n,s,r){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this[vr]=!1,this._dirty=!0,this.effect=new Xn(t,()=>{this._dirty||(this._dirty=!0,ss(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!r,this.__v_isReadonly=s}get value(){const t=z(this);return ns(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}vr="__v_isReadonly";function mo(e,t,n=!1){let s,r;const i=U(e);return i?(s=e,r=Ie):(s=e.get,r=e.set),new go(s,r,i||!r,n)}function We(e,t,n,s){let r;try{r=s?e(...s):e()}catch(i){un(i,t,n)}return r}function xe(e,t,n,s){if(U(e)){const i=We(e,t,n,s);return i&&or(i)&&i.catch(o=>{un(o,t,n)}),i}const r=[];for(let i=0;i>>1;Ft(fe[s])Me&&fe.splice(t,1)}function wo(e){N(e)?dt.push(...e):(!Ne||!Ne.includes(e,e.allowRecurse?Ge+1:Ge))&&dt.push(e),Rr()}function vs(e,t=Pt?Me+1:0){for(;tFt(n)-Ft(s)),Ge=0;Gee.id==null?1/0:e.id,Co=(e,t)=>{const n=Ft(e)-Ft(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function Ir(e){Sn=!1,Pt=!0,fe.sort(Co);const t=Ie;try{for(Me=0;Mere(R)?R.trim():R)),p&&(r=n.map(Ii))}let l,f=s[l=Vt(t)]||s[l=Vt(Se(t))];!f&&i&&(f=s[l=Vt(it(t))]),f&&xe(f,e,6,r);const a=s[l+"Once"];if(a){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,xe(a,e,6,r)}}function Or(e,t,n=!1){const s=t.emitsCache,r=s.get(e);if(r!==void 0)return r;const i=e.emits;let o={},l=!1;if(!U(e)){const f=a=>{const d=Or(a,t,!0);d&&(l=!0,ce(o,d))};!n&&t.mixins.length&&t.mixins.forEach(f),e.extends&&f(e.extends),e.mixins&&e.mixins.forEach(f)}return!i&&!l?(ee(e)&&s.set(e,null),null):(N(i)?i.forEach(f=>o[f]=null):ce(o,i),ee(e)&&s.set(e,o),o)}function an(e,t){return!e||!Lt(t)?!1:(t=t.slice(2).replace(/Once$/,""),V(e,t[0].toLowerCase()+t.slice(1))||V(e,it(t))||V(e,t))}let ue=null,dn=null;function en(e){const t=ue;return ue=e,dn=e&&e.type.__scopeId||null,t}function Ac(e){dn=e}function Rc(){dn=null}function Eo(e,t=ue,n){if(!t||e._n)return e;const s=(...r)=>{s._d&&Ls(-1);const i=en(t);let o;try{o=e(...r)}finally{en(i),s._d&&Ls(1)}return o};return s._n=!0,s._c=!0,s._d=!0,s}function xn(e){const{type:t,vnode:n,proxy:s,withProxy:r,props:i,propsOptions:[o],slots:l,attrs:f,emit:a,render:d,renderCache:p,data:y,setupState:R,ctx:L,inheritAttrs:P}=e;let W,_;const E=en(e);try{if(n.shapeFlag&4){const $=r||s;W=Te(d.call($,$,p,i,R,y,L)),_=f}else{const $=t;W=Te($.length>1?$(i,{attrs:f,slots:l,emit:a}):$(i,null)),_=t.props?f:vo(f)}}catch($){Rt.length=0,un($,e,1),W=le(be)}let A=W;if(_&&P!==!1){const $=Object.keys(_),{shapeFlag:D}=A;$.length&&D&7&&(o&&$.some(qn)&&(_=To(_,o)),A=Ve(A,_))}return n.dirs&&(A=Ve(A),A.dirs=A.dirs?A.dirs.concat(n.dirs):n.dirs),n.transition&&(A.transition=n.transition),W=A,en(E),W}const vo=e=>{let t;for(const n in e)(n==="class"||n==="style"||Lt(n))&&((t||(t={}))[n]=e[n]);return t},To=(e,t)=>{const n={};for(const s in e)(!qn(s)||!(s.slice(9)in t))&&(n[s]=e[s]);return n};function Ao(e,t,n){const{props:s,children:r,component:i}=e,{props:o,children:l,patchFlag:f}=t,a=i.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&f>=0){if(f&1024)return!0;if(f&16)return s?Ts(s,o,a):!!o;if(f&8){const d=t.dynamicProps;for(let p=0;pe.__isSuspense;function Pr(e,t){t&&t.pendingBranch?N(e)?t.effects.push(...e):t.effects.push(e):wo(e)}function Oo(e,t){if(se){let n=se.provides;const s=se.parent&&se.parent.provides;s===n&&(n=se.provides=Object.create(s)),n[e]=t}}function ht(e,t,n=!1){const s=se||ue;if(s){const r=s.parent==null?s.vnode.appContext&&s.vnode.appContext.provides:s.parent.provides;if(r&&e in r)return r[e];if(arguments.length>1)return n&&U(t)?t.call(s.proxy):t}}function Po(e,t){return hn(e,null,t)}function Ic(e,t){return hn(e,null,{flush:"post"})}const Kt={};function zt(e,t,n){return hn(e,t,n)}function hn(e,t,{immediate:n,deep:s,flush:r,onTrack:i,onTrigger:o}=te){const l=Si()===(se==null?void 0:se.scope)?se:null;let f,a=!1,d=!1;if(oe(e)?(f=()=>e.value,a=Qt(e)):at(e)?(f=()=>e,s=!0):N(e)?(d=!0,a=e.some(A=>at(A)||Qt(A)),f=()=>e.map(A=>{if(oe(A))return A.value;if(at(A))return ct(A);if(U(A))return We(A,l,2)})):U(e)?t?f=()=>We(e,l,2):f=()=>{if(!(l&&l.isUnmounted))return p&&p(),xe(e,l,3,[y])}:f=Ie,t&&s){const A=f;f=()=>ct(A())}let p,y=A=>{p=_.onStop=()=>{We(A,l,4)}},R;if(St)if(y=Ie,t?n&&xe(t,l,3,[f(),d?[]:void 0,y]):f(),r==="sync"){const A=xl();R=A.__watcherHandles||(A.__watcherHandles=[])}else return Ie;let L=d?new Array(e.length).fill(Kt):Kt;const P=()=>{if(_.active)if(t){const A=_.run();(s||a||(d?A.some(($,D)=>It($,L[D])):It(A,L)))&&(p&&p(),xe(t,l,3,[A,L===Kt?void 0:d&&L[0]===Kt?[]:L,y]),L=A)}else _.run()};P.allowRecurse=!!t;let W;r==="sync"?W=P:r==="post"?W=()=>de(P,l&&l.suspense):(P.pre=!0,l&&(P.id=l.uid),W=()=>is(P));const _=new Xn(f,W);t?n?P():L=_.run():r==="post"?de(_.run.bind(_),l&&l.suspense):_.run();const E=()=>{_.stop(),l&&l.scope&&Vn(l.scope.effects,_)};return R&&R.push(E),E}function Fo(e,t,n){const s=this.proxy,r=re(e)?e.includes(".")?Fr(s,e):()=>s[e]:e.bind(s,s);let i;U(t)?i=t:(i=t.handler,n=t);const o=se;_t(this);const l=hn(r,i.bind(s),n);return o?_t(o):rt(),l}function Fr(e,t){const n=t.split(".");return()=>{let s=e;for(let r=0;r{ct(n,t)});else if(cr(e))for(const n in e)ct(e[n],t);return e}function Mo(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return mn(()=>{e.isMounted=!0}),$r(()=>{e.isUnmounting=!0}),e}const ye=[Function,Array],So={name:"BaseTransition",props:{mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:ye,onEnter:ye,onAfterEnter:ye,onEnterCancelled:ye,onBeforeLeave:ye,onLeave:ye,onAfterLeave:ye,onLeaveCancelled:ye,onBeforeAppear:ye,onAppear:ye,onAfterAppear:ye,onAppearCancelled:ye},setup(e,{slots:t}){const n=ti(),s=Mo();let r;return()=>{const i=t.default&&Lr(t.default(),!0);if(!i||!i.length)return;let o=i[0];if(i.length>1){for(const P of i)if(P.type!==be){o=P;break}}const l=z(e),{mode:f}=l;if(s.isLeaving)return En(o);const a=As(o);if(!a)return En(o);const d=Ln(a,l,s,n);Nn(a,d);const p=n.subTree,y=p&&As(p);let R=!1;const{getTransitionKey:L}=a.type;if(L){const P=L();r===void 0?r=P:P!==r&&(r=P,R=!0)}if(y&&y.type!==be&&(!et(a,y)||R)){const P=Ln(y,l,s,n);if(Nn(y,P),f==="out-in")return s.isLeaving=!0,P.afterLeave=()=>{s.isLeaving=!1,n.update.active!==!1&&n.update()},En(o);f==="in-out"&&a.type!==be&&(P.delayLeave=(W,_,E)=>{const A=Sr(s,y);A[String(y.key)]=y,W._leaveCb=()=>{_(),W._leaveCb=void 0,delete d.delayedLeave},d.delayedLeave=E})}return o}}},Mr=So;function Sr(e,t){const{leavingVNodes:n}=e;let s=n.get(t.type);return s||(s=Object.create(null),n.set(t.type,s)),s}function Ln(e,t,n,s){const{appear:r,mode:i,persisted:o=!1,onBeforeEnter:l,onEnter:f,onAfterEnter:a,onEnterCancelled:d,onBeforeLeave:p,onLeave:y,onAfterLeave:R,onLeaveCancelled:L,onBeforeAppear:P,onAppear:W,onAfterAppear:_,onAppearCancelled:E}=t,A=String(e.key),$=Sr(n,e),D=(T,B)=>{T&&xe(T,s,9,B)},Z=(T,B)=>{const j=B[1];D(T,B),N(T)?T.every(Y=>Y.length<=1)&&j():T.length<=1&&j()},q={mode:i,persisted:o,beforeEnter(T){let B=l;if(!n.isMounted)if(r)B=P||l;else return;T._leaveCb&&T._leaveCb(!0);const j=$[A];j&&et(e,j)&&j.el._leaveCb&&j.el._leaveCb(),D(B,[T])},enter(T){let B=f,j=a,Y=d;if(!n.isMounted)if(r)B=W||f,j=_||a,Y=E||d;else return;let I=!1;const K=T._enterCb=F=>{I||(I=!0,F?D(Y,[T]):D(j,[T]),q.delayedLeave&&q.delayedLeave(),T._enterCb=void 0)};B?Z(B,[T,K]):K()},leave(T,B){const j=String(e.key);if(T._enterCb&&T._enterCb(!0),n.isUnmounting)return B();D(p,[T]);let Y=!1;const I=T._leaveCb=K=>{Y||(Y=!0,B(),K?D(L,[T]):D(R,[T]),T._leaveCb=void 0,$[j]===e&&delete $[j])};$[j]=e,y?Z(y,[T,I]):I()},clone(T){return Ln(T,t,n,s)}};return q}function En(e){if(pn(e))return e=Ve(e),e.children=null,e}function As(e){return pn(e)?e.children?e.children[0]:void 0:e}function Nn(e,t){e.shapeFlag&6&&e.component?Nn(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Lr(e,t=!1,n){let s=[],r=0;for(let i=0;i1)for(let i=0;i!!e.type.__asyncLoader,pn=e=>e.type.__isKeepAlive;function Lo(e,t){Hr(e,"a",t)}function No(e,t){Hr(e,"da",t)}function Hr(e,t,n=se){const s=e.__wdc||(e.__wdc=()=>{let r=n;for(;r;){if(r.isDeactivated)return;r=r.parent}return e()});if(gn(t,s,n),n){let r=n.parent;for(;r&&r.parent;)pn(r.parent.vnode)&&Ho(s,t,n,r),r=r.parent}}function Ho(e,t,n,s){const r=gn(t,e,s,!0);_n(()=>{Vn(s[t],r)},n)}function gn(e,t,n=se,s=!1){if(n){const r=n[e]||(n[e]=[]),i=t.__weh||(t.__weh=(...o)=>{if(n.isUnmounted)return;bt(),_t(n);const l=xe(t,n,e,o);return rt(),yt(),l});return s?r.unshift(i):r.push(i),i}}const $e=e=>(t,n=se)=>(!St||e==="sp")&&gn(e,(...s)=>t(...s),n),$o=$e("bm"),mn=$e("m"),jo=$e("bu"),Uo=$e("u"),$r=$e("bum"),_n=$e("um"),Bo=$e("sp"),Do=$e("rtg"),Ko=$e("rtc");function ko(e,t=se){gn("ec",e,t)}function Fe(e,t,n,s){const r=e.dirs,i=t&&t.dirs;for(let o=0;ot(o,l,void 0,i&&i[l]));else{const o=Object.keys(e);r=new Array(o.length);for(let l=0,f=o.length;lsn(t)?!(t.type===be||t.type===he&&!Br(t.children)):!0)?e:null}function Sc(e,t){const n={};for(const s in e)n[t&&/[A-Z]/.test(s)?`on:${s}`:Vt(s)]=e[s];return n}const Hn=e=>e?ni(e)?us(e)||e.proxy:Hn(e.parent):null,Tt=ce(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Hn(e.parent),$root:e=>Hn(e.root),$emit:e=>e.emit,$options:e=>ls(e),$forceUpdate:e=>e.f||(e.f=()=>is(e.update)),$nextTick:e=>e.n||(e.n=Ar.bind(e.proxy)),$watch:e=>Fo.bind(e)}),vn=(e,t)=>e!==te&&!e.__isScriptSetup&&V(e,t),Wo={get({_:e},t){const{ctx:n,setupState:s,data:r,props:i,accessCache:o,type:l,appContext:f}=e;let a;if(t[0]!=="$"){const R=o[t];if(R!==void 0)switch(R){case 1:return s[t];case 2:return r[t];case 4:return n[t];case 3:return i[t]}else{if(vn(s,t))return o[t]=1,s[t];if(r!==te&&V(r,t))return o[t]=2,r[t];if((a=e.propsOptions[0])&&V(a,t))return o[t]=3,i[t];if(n!==te&&V(n,t))return o[t]=4,n[t];$n&&(o[t]=0)}}const d=Tt[t];let p,y;if(d)return t==="$attrs"&&pe(e,"get",t),d(e);if((p=l.__cssModules)&&(p=p[t]))return p;if(n!==te&&V(n,t))return o[t]=4,n[t];if(y=f.config.globalProperties,V(y,t))return y[t]},set({_:e},t,n){const{data:s,setupState:r,ctx:i}=e;return vn(r,t)?(r[t]=n,!0):s!==te&&V(s,t)?(s[t]=n,!0):V(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:s,appContext:r,propsOptions:i}},o){let l;return!!n[o]||e!==te&&V(e,o)||vn(t,o)||(l=i[0])&&V(l,o)||V(s,o)||V(Tt,o)||V(r.config.globalProperties,o)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:V(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};let $n=!0;function qo(e){const t=ls(e),n=e.proxy,s=e.ctx;$n=!1,t.beforeCreate&&Is(t.beforeCreate,e,"bc");const{data:r,computed:i,methods:o,watch:l,provide:f,inject:a,created:d,beforeMount:p,mounted:y,beforeUpdate:R,updated:L,activated:P,deactivated:W,beforeDestroy:_,beforeUnmount:E,destroyed:A,unmounted:$,render:D,renderTracked:Z,renderTriggered:q,errorCaptured:T,serverPrefetch:B,expose:j,inheritAttrs:Y,components:I,directives:K,filters:F}=t;if(a&&Vo(a,s,null,e.appContext.config.unwrapInjectedRef),o)for(const ne in o){const Q=o[ne];U(Q)&&(s[ne]=Q.bind(n))}if(r){const ne=r.call(n,n);ee(ne)&&(e.data=fn(ne))}if($n=!0,i)for(const ne in i){const Q=i[ne],ze=U(Q)?Q.bind(n,n):U(Q.get)?Q.get.bind(n,n):Ie,Nt=!U(Q)&&U(Q.set)?Q.set.bind(n):Ie,Ye=ve({get:ze,set:Nt});Object.defineProperty(s,ne,{enumerable:!0,configurable:!0,get:()=>Ye.value,set:Oe=>Ye.value=Oe})}if(l)for(const ne in l)Dr(l[ne],s,n,ne);if(f){const ne=U(f)?f.call(n):f;Reflect.ownKeys(ne).forEach(Q=>{Oo(Q,ne[Q])})}d&&Is(d,e,"c");function J(ne,Q){N(Q)?Q.forEach(ze=>ne(ze.bind(n))):Q&&ne(Q.bind(n))}if(J($o,p),J(mn,y),J(jo,R),J(Uo,L),J(Lo,P),J(No,W),J(ko,T),J(Ko,Z),J(Do,q),J($r,E),J(_n,$),J(Bo,B),N(j))if(j.length){const ne=e.exposed||(e.exposed={});j.forEach(Q=>{Object.defineProperty(ne,Q,{get:()=>n[Q],set:ze=>n[Q]=ze})})}else e.exposed||(e.exposed={});D&&e.render===Ie&&(e.render=D),Y!=null&&(e.inheritAttrs=Y),I&&(e.components=I),K&&(e.directives=K)}function Vo(e,t,n=Ie,s=!1){N(e)&&(e=jn(e));for(const r in e){const i=e[r];let o;ee(i)?"default"in i?o=ht(i.from||r,i.default,!0):o=ht(i.from||r):o=ht(i),oe(o)&&s?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>o.value,set:l=>o.value=l}):t[r]=o}}function Is(e,t,n){xe(N(e)?e.map(s=>s.bind(t.proxy)):e.bind(t.proxy),t,n)}function Dr(e,t,n,s){const r=s.includes(".")?Fr(n,s):()=>n[s];if(re(e)){const i=t[e];U(i)&&zt(r,i)}else if(U(e))zt(r,e.bind(n));else if(ee(e))if(N(e))e.forEach(i=>Dr(i,t,n,s));else{const i=U(e.handler)?e.handler.bind(n):t[e.handler];U(i)&&zt(r,i,e)}}function ls(e){const t=e.type,{mixins:n,extends:s}=t,{mixins:r,optionsCache:i,config:{optionMergeStrategies:o}}=e.appContext,l=i.get(t);let f;return l?f=l:!r.length&&!n&&!s?f=t:(f={},r.length&&r.forEach(a=>tn(f,a,o,!0)),tn(f,t,o)),ee(t)&&i.set(t,f),f}function tn(e,t,n,s=!1){const{mixins:r,extends:i}=t;i&&tn(e,i,n,!0),r&&r.forEach(o=>tn(e,o,n,!0));for(const o in t)if(!(s&&o==="expose")){const l=zo[o]||n&&n[o];e[o]=l?l(e[o],t[o]):t[o]}return e}const zo={data:Os,props:Qe,emits:Qe,methods:Qe,computed:Qe,beforeCreate:ae,created:ae,beforeMount:ae,mounted:ae,beforeUpdate:ae,updated:ae,beforeDestroy:ae,beforeUnmount:ae,destroyed:ae,unmounted:ae,activated:ae,deactivated:ae,errorCaptured:ae,serverPrefetch:ae,components:Qe,directives:Qe,watch:Jo,provide:Os,inject:Yo};function Os(e,t){return t?e?function(){return ce(U(e)?e.call(this,this):e,U(t)?t.call(this,this):t)}:t:e}function Yo(e,t){return Qe(jn(e),jn(t))}function jn(e){if(N(e)){const t={};for(let n=0;n0)&&!(o&16)){if(o&8){const d=e.vnode.dynamicProps;for(let p=0;p{f=!0;const[y,R]=kr(p,t,!0);ce(o,y),R&&l.push(...R)};!n&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!i&&!f)return ee(e)&&s.set(e,ft),ft;if(N(i))for(let d=0;d-1,R[1]=P<0||L-1||V(R,"default"))&&l.push(p)}}}const a=[o,l];return ee(e)&&s.set(e,a),a}function Ps(e){return e[0]!=="$"}function Fs(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function Ms(e,t){return Fs(e)===Fs(t)}function Ss(e,t){return N(t)?t.findIndex(n=>Ms(n,e)):U(t)&&Ms(t,e)?0:-1}const Wr=e=>e[0]==="_"||e==="$stable",cs=e=>N(e)?e.map(Te):[Te(e)],Qo=(e,t,n)=>{if(t._n)return t;const s=Eo((...r)=>cs(t(...r)),n);return s._c=!1,s},qr=(e,t,n)=>{const s=e._ctx;for(const r in e){if(Wr(r))continue;const i=e[r];if(U(i))t[r]=Qo(r,i,s);else if(i!=null){const o=cs(i);t[r]=()=>o}}},Vr=(e,t)=>{const n=cs(t);e.slots.default=()=>n},Go=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=z(t),Xt(t,"_",n)):qr(t,e.slots={})}else e.slots={},t&&Vr(e,t);Xt(e.slots,bn,1)},el=(e,t,n)=>{const{vnode:s,slots:r}=e;let i=!0,o=te;if(s.shapeFlag&32){const l=t._;l?n&&l===1?i=!1:(ce(r,t),!n&&l===1&&delete r._):(i=!t.$stable,qr(t,r)),o=t}else t&&(Vr(e,t),o={default:1});if(i)for(const l in r)!Wr(l)&&!(l in o)&&delete r[l]};function zr(){return{app:null,config:{isNativeTag:xi,performance:!1,globalProperties:{},optionMergeStrategies:{},errorHandler:void 0,warnHandler:void 0,compilerOptions:{}},mixins:[],components:{},directives:{},provides:Object.create(null),optionsCache:new WeakMap,propsCache:new WeakMap,emitsCache:new WeakMap}}let tl=0;function nl(e,t){return function(s,r=null){U(s)||(s=Object.assign({},s)),r!=null&&!ee(r)&&(r=null);const i=zr(),o=new Set;let l=!1;const f=i.app={_uid:tl++,_component:s,_props:r,_container:null,_context:i,_instance:null,version:El,get config(){return i.config},set config(a){},use(a,...d){return o.has(a)||(a&&U(a.install)?(o.add(a),a.install(f,...d)):U(a)&&(o.add(a),a(f,...d))),f},mixin(a){return i.mixins.includes(a)||i.mixins.push(a),f},component(a,d){return d?(i.components[a]=d,f):i.components[a]},directive(a,d){return d?(i.directives[a]=d,f):i.directives[a]},mount(a,d,p){if(!l){const y=le(s,r);return y.appContext=i,d&&t?t(y,a):e(y,a,p),l=!0,f._container=a,a.__vue_app__=f,us(y.component)||y.component.proxy}},unmount(){l&&(e(null,f._container),delete f._container.__vue_app__)},provide(a,d){return i.provides[a]=d,f}};return f}}function nn(e,t,n,s,r=!1){if(N(e)){e.forEach((y,R)=>nn(y,t&&(N(t)?t[R]:t),n,s,r));return}if(pt(s)&&!r)return;const i=s.shapeFlag&4?us(s.component)||s.component.proxy:s.el,o=r?null:i,{i:l,r:f}=e,a=t&&t.r,d=l.refs===te?l.refs={}:l.refs,p=l.setupState;if(a!=null&&a!==f&&(re(a)?(d[a]=null,V(p,a)&&(p[a]=null)):oe(a)&&(a.value=null)),U(f))We(f,l,12,[o,d]);else{const y=re(f),R=oe(f);if(y||R){const L=()=>{if(e.f){const P=y?V(p,f)?p[f]:d[f]:f.value;r?N(P)&&Vn(P,i):N(P)?P.includes(i)||P.push(i):y?(d[f]=[i],V(p,f)&&(p[f]=d[f])):(f.value=[i],e.k&&(d[e.k]=f.value))}else y?(d[f]=o,V(p,f)&&(p[f]=o)):R&&(f.value=o,e.k&&(d[e.k]=o))};o?(L.id=-1,de(L,n)):L()}}}let Ue=!1;const kt=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",Wt=e=>e.nodeType===8;function sl(e){const{mt:t,p:n,o:{patchProp:s,createText:r,nextSibling:i,parentNode:o,remove:l,insert:f,createComment:a}}=e,d=(_,E)=>{if(!E.hasChildNodes()){n(null,_,E),Gt(),E._vnode=_;return}Ue=!1,p(E.firstChild,_,null,null,null),Gt(),E._vnode=_,Ue&&console.error("Hydration completed but contains mismatches.")},p=(_,E,A,$,D,Z=!1)=>{const q=Wt(_)&&_.data==="[",T=()=>P(_,E,A,$,D,q),{type:B,ref:j,shapeFlag:Y,patchFlag:I}=E;let K=_.nodeType;E.el=_,I===-2&&(Z=!1,E.dynamicChildren=null);let F=null;switch(B){case mt:K!==3?E.children===""?(f(E.el=r(""),o(_),_),F=_):F=T():(_.data!==E.children&&(Ue=!0,_.data=E.children),F=i(_));break;case be:K!==8||q?F=T():F=i(_);break;case At:if(q&&(_=i(_),K=_.nodeType),K===1||K===3){F=_;const ge=!E.children.length;for(let J=0;J{Z=Z||!!E.dynamicChildren;const{type:q,props:T,patchFlag:B,shapeFlag:j,dirs:Y}=E,I=q==="input"&&Y||q==="option";if(I||B!==-1){if(Y&&Fe(E,null,A,"created"),T)if(I||!Z||B&48)for(const F in T)(I&&F.endsWith("value")||Lt(F)&&!Et(F))&&s(_,F,null,T[F],!1,void 0,A);else T.onClick&&s(_,"onClick",null,T.onClick,!1,void 0,A);let K;if((K=T&&T.onVnodeBeforeMount)&&we(K,A,E),Y&&Fe(E,null,A,"beforeMount"),((K=T&&T.onVnodeMounted)||Y)&&Pr(()=>{K&&we(K,A,E),Y&&Fe(E,null,A,"mounted")},$),j&16&&!(T&&(T.innerHTML||T.textContent))){let F=R(_.firstChild,E,_,A,$,D,Z);for(;F;){Ue=!0;const ge=F;F=F.nextSibling,l(ge)}}else j&8&&_.textContent!==E.children&&(Ue=!0,_.textContent=E.children)}return _.nextSibling},R=(_,E,A,$,D,Z,q)=>{q=q||!!E.dynamicChildren;const T=E.children,B=T.length;for(let j=0;j{const{slotScopeIds:q}=E;q&&(D=D?D.concat(q):q);const T=o(_),B=R(i(_),E,T,A,$,D,Z);return B&&Wt(B)&&B.data==="]"?i(E.anchor=B):(Ue=!0,f(E.anchor=a("]"),T,B),B)},P=(_,E,A,$,D,Z)=>{if(Ue=!0,E.el=null,Z){const B=W(_);for(;;){const j=i(_);if(j&&j!==B)l(j);else break}}const q=i(_),T=o(_);return l(_),n(null,E,T,q,A,$,kt(T),D),q},W=_=>{let E=0;for(;_;)if(_=i(_),_&&Wt(_)&&(_.data==="["&&E++,_.data==="]")){if(E===0)return i(_);E--}return _};return[d,p]}const de=Pr;function rl(e){return il(e,sl)}function il(e,t){const n=Pi();n.__VUE__=!0;const{insert:s,remove:r,patchProp:i,createElement:o,createText:l,createComment:f,setText:a,setElementText:d,parentNode:p,nextSibling:y,setScopeId:R=Ie,insertStaticContent:L}=e,P=(c,u,h,m=null,g=null,C=null,v=!1,w=null,x=!!u.dynamicChildren)=>{if(c===u)return;c&&!et(c,u)&&(m=Ht(c),Oe(c,g,C,!0),c=null),u.patchFlag===-2&&(x=!1,u.dynamicChildren=null);const{type:b,ref:M,shapeFlag:O}=u;switch(b){case mt:W(c,u,h,m);break;case be:_(c,u,h,m);break;case At:c==null&&E(u,h,m,v);break;case he:I(c,u,h,m,g,C,v,w,x);break;default:O&1?D(c,u,h,m,g,C,v,w,x):O&6?K(c,u,h,m,g,C,v,w,x):(O&64||O&128)&&b.process(c,u,h,m,g,C,v,w,x,ot)}M!=null&&g&&nn(M,c&&c.ref,C,u||c,!u)},W=(c,u,h,m)=>{if(c==null)s(u.el=l(u.children),h,m);else{const g=u.el=c.el;u.children!==c.children&&a(g,u.children)}},_=(c,u,h,m)=>{c==null?s(u.el=f(u.children||""),h,m):u.el=c.el},E=(c,u,h,m)=>{[c.el,c.anchor]=L(c.children,u,h,m,c.el,c.anchor)},A=({el:c,anchor:u},h,m)=>{let g;for(;c&&c!==u;)g=y(c),s(c,h,m),c=g;s(u,h,m)},$=({el:c,anchor:u})=>{let h;for(;c&&c!==u;)h=y(c),r(c),c=h;r(u)},D=(c,u,h,m,g,C,v,w,x)=>{v=v||u.type==="svg",c==null?Z(u,h,m,g,C,v,w,x):B(c,u,g,C,v,w,x)},Z=(c,u,h,m,g,C,v,w)=>{let x,b;const{type:M,props:O,shapeFlag:S,transition:H,dirs:k}=c;if(x=c.el=o(c.type,C,O&&O.is,O),S&8?d(x,c.children):S&16&&T(c.children,x,null,m,g,C&&M!=="foreignObject",v,w),k&&Fe(c,null,m,"created"),q(x,c,c.scopeId,v,m),O){for(const X in O)X!=="value"&&!Et(X)&&i(x,X,null,O[X],C,c.children,m,g,Le);"value"in O&&i(x,"value",null,O.value),(b=O.onVnodeBeforeMount)&&we(b,m,c)}k&&Fe(c,null,m,"beforeMount");const G=(!g||g&&!g.pendingBranch)&&H&&!H.persisted;G&&H.beforeEnter(x),s(x,u,h),((b=O&&O.onVnodeMounted)||G||k)&&de(()=>{b&&we(b,m,c),G&&H.enter(x),k&&Fe(c,null,m,"mounted")},g)},q=(c,u,h,m,g)=>{if(h&&R(c,h),m)for(let C=0;C{for(let b=x;b{const w=u.el=c.el;let{patchFlag:x,dynamicChildren:b,dirs:M}=u;x|=c.patchFlag&16;const O=c.props||te,S=u.props||te;let H;h&&Je(h,!1),(H=S.onVnodeBeforeUpdate)&&we(H,h,u,c),M&&Fe(u,c,h,"beforeUpdate"),h&&Je(h,!0);const k=g&&u.type!=="foreignObject";if(b?j(c.dynamicChildren,b,w,h,m,k,C):v||Q(c,u,w,null,h,m,k,C,!1),x>0){if(x&16)Y(w,u,O,S,h,m,g);else if(x&2&&O.class!==S.class&&i(w,"class",null,S.class,g),x&4&&i(w,"style",O.style,S.style,g),x&8){const G=u.dynamicProps;for(let X=0;X{H&&we(H,h,u,c),M&&Fe(u,c,h,"updated")},m)},j=(c,u,h,m,g,C,v)=>{for(let w=0;w{if(h!==m){if(h!==te)for(const w in h)!Et(w)&&!(w in m)&&i(c,w,h[w],null,v,u.children,g,C,Le);for(const w in m){if(Et(w))continue;const x=m[w],b=h[w];x!==b&&w!=="value"&&i(c,w,b,x,v,u.children,g,C,Le)}"value"in m&&i(c,"value",h.value,m.value)}},I=(c,u,h,m,g,C,v,w,x)=>{const b=u.el=c?c.el:l(""),M=u.anchor=c?c.anchor:l("");let{patchFlag:O,dynamicChildren:S,slotScopeIds:H}=u;H&&(w=w?w.concat(H):H),c==null?(s(b,h,m),s(M,h,m),T(u.children,h,M,g,C,v,w,x)):O>0&&O&64&&S&&c.dynamicChildren?(j(c.dynamicChildren,S,h,g,C,v,w),(u.key!=null||g&&u===g.subTree)&&Yr(c,u,!0)):Q(c,u,h,M,g,C,v,w,x)},K=(c,u,h,m,g,C,v,w,x)=>{u.slotScopeIds=w,c==null?u.shapeFlag&512?g.ctx.activate(u,h,m,v,x):F(u,h,m,g,C,v,x):ge(c,u,x)},F=(c,u,h,m,g,C,v)=>{const w=c.component=pl(c,m,g);if(pn(c)&&(w.ctx.renderer=ot),gl(w),w.asyncDep){if(g&&g.registerDep(w,J),!c.el){const x=w.subTree=le(be);_(null,x,u,h)}return}J(w,c,u,h,g,C,v)},ge=(c,u,h)=>{const m=u.component=c.component;if(Ao(c,u,h))if(m.asyncDep&&!m.asyncResolved){ne(m,u,h);return}else m.next=u,yo(m.update),m.update();else u.el=c.el,m.vnode=u},J=(c,u,h,m,g,C,v)=>{const w=()=>{if(c.isMounted){let{next:M,bu:O,u:S,parent:H,vnode:k}=c,G=M,X;Je(c,!1),M?(M.el=k.el,ne(c,M,v)):M=k,O&&Cn(O),(X=M.props&&M.props.onVnodeBeforeUpdate)&&we(X,H,M,k),Je(c,!0);const ie=xn(c),Ee=c.subTree;c.subTree=ie,P(Ee,ie,p(Ee.el),Ht(Ee),c,g,C),M.el=ie.el,G===null&&Ro(c,ie.el),S&&de(S,g),(X=M.props&&M.props.onVnodeUpdated)&&de(()=>we(X,H,M,k),g)}else{let M;const{el:O,props:S}=u,{bm:H,m:k,parent:G}=c,X=pt(u);if(Je(c,!1),H&&Cn(H),!X&&(M=S&&S.onVnodeBeforeMount)&&we(M,G,u),Je(c,!0),O&&wn){const ie=()=>{c.subTree=xn(c),wn(O,c.subTree,c,g,null)};X?u.type.__asyncLoader().then(()=>!c.isUnmounted&&ie()):ie()}else{const ie=c.subTree=xn(c);P(null,ie,h,m,c,g,C),u.el=ie.el}if(k&&de(k,g),!X&&(M=S&&S.onVnodeMounted)){const ie=u;de(()=>we(M,G,ie),g)}(u.shapeFlag&256||G&&pt(G.vnode)&&G.vnode.shapeFlag&256)&&c.a&&de(c.a,g),c.isMounted=!0,u=h=m=null}},x=c.effect=new Xn(w,()=>is(b),c.scope),b=c.update=()=>x.run();b.id=c.uid,Je(c,!0),b()},ne=(c,u,h)=>{u.component=c;const m=c.vnode.props;c.vnode=u,c.next=null,Zo(c,u.props,m,h),el(c,u.children,h),bt(),vs(),yt()},Q=(c,u,h,m,g,C,v,w,x=!1)=>{const b=c&&c.children,M=c?c.shapeFlag:0,O=u.children,{patchFlag:S,shapeFlag:H}=u;if(S>0){if(S&128){Nt(b,O,h,m,g,C,v,w,x);return}else if(S&256){ze(b,O,h,m,g,C,v,w,x);return}}H&8?(M&16&&Le(b,g,C),O!==b&&d(h,O)):M&16?H&16?Nt(b,O,h,m,g,C,v,w,x):Le(b,g,C,!0):(M&8&&d(h,""),H&16&&T(O,h,m,g,C,v,w,x))},ze=(c,u,h,m,g,C,v,w,x)=>{c=c||ft,u=u||ft;const b=c.length,M=u.length,O=Math.min(b,M);let S;for(S=0;SM?Le(c,g,C,!0,!1,O):T(u,h,m,g,C,v,w,x,O)},Nt=(c,u,h,m,g,C,v,w,x)=>{let b=0;const M=u.length;let O=c.length-1,S=M-1;for(;b<=O&&b<=S;){const H=c[b],k=u[b]=x?Ke(u[b]):Te(u[b]);if(et(H,k))P(H,k,h,null,g,C,v,w,x);else break;b++}for(;b<=O&&b<=S;){const H=c[O],k=u[S]=x?Ke(u[S]):Te(u[S]);if(et(H,k))P(H,k,h,null,g,C,v,w,x);else break;O--,S--}if(b>O){if(b<=S){const H=S+1,k=HS)for(;b<=O;)Oe(c[b],g,C,!0),b++;else{const H=b,k=b,G=new Map;for(b=k;b<=S;b++){const me=u[b]=x?Ke(u[b]):Te(u[b]);me.key!=null&&G.set(me.key,b)}let X,ie=0;const Ee=S-k+1;let lt=!1,hs=0;const wt=new Array(Ee);for(b=0;b=Ee){Oe(me,g,C,!0);continue}let Pe;if(me.key!=null)Pe=G.get(me.key);else for(X=k;X<=S;X++)if(wt[X-k]===0&&et(me,u[X])){Pe=X;break}Pe===void 0?Oe(me,g,C,!0):(wt[Pe-k]=b+1,Pe>=hs?hs=Pe:lt=!0,P(me,u[Pe],h,null,g,C,v,w,x),ie++)}const ps=lt?ol(wt):ft;for(X=ps.length-1,b=Ee-1;b>=0;b--){const me=k+b,Pe=u[me],gs=me+1{const{el:C,type:v,transition:w,children:x,shapeFlag:b}=c;if(b&6){Ye(c.component.subTree,u,h,m);return}if(b&128){c.suspense.move(u,h,m);return}if(b&64){v.move(c,u,h,ot);return}if(v===he){s(C,u,h);for(let O=0;Ow.enter(C),g);else{const{leave:O,delayLeave:S,afterLeave:H}=w,k=()=>s(C,u,h),G=()=>{O(C,()=>{k(),H&&H()})};S?S(C,k,G):G()}else s(C,u,h)},Oe=(c,u,h,m=!1,g=!1)=>{const{type:C,props:v,ref:w,children:x,dynamicChildren:b,shapeFlag:M,patchFlag:O,dirs:S}=c;if(w!=null&&nn(w,null,h,c,!0),M&256){u.ctx.deactivate(c);return}const H=M&1&&S,k=!pt(c);let G;if(k&&(G=v&&v.onVnodeBeforeUnmount)&&we(G,u,c),M&6)gi(c.component,h,m);else{if(M&128){c.suspense.unmount(h,m);return}H&&Fe(c,null,u,"beforeUnmount"),M&64?c.type.remove(c,u,h,g,ot,m):b&&(C!==he||O>0&&O&64)?Le(b,u,h,!1,!0):(C===he&&O&384||!g&&M&16)&&Le(x,u,h),m&&as(c)}(k&&(G=v&&v.onVnodeUnmounted)||H)&&de(()=>{G&&we(G,u,c),H&&Fe(c,null,u,"unmounted")},h)},as=c=>{const{type:u,el:h,anchor:m,transition:g}=c;if(u===he){pi(h,m);return}if(u===At){$(c);return}const C=()=>{r(h),g&&!g.persisted&&g.afterLeave&&g.afterLeave()};if(c.shapeFlag&1&&g&&!g.persisted){const{leave:v,delayLeave:w}=g,x=()=>v(h,C);w?w(c.el,C,x):x()}else C()},pi=(c,u)=>{let h;for(;c!==u;)h=y(c),r(c),c=h;r(u)},gi=(c,u,h)=>{const{bum:m,scope:g,update:C,subTree:v,um:w}=c;m&&Cn(m),g.stop(),C&&(C.active=!1,Oe(v,c,u,h)),w&&de(w,u),de(()=>{c.isUnmounted=!0},u),u&&u.pendingBranch&&!u.isUnmounted&&c.asyncDep&&!c.asyncResolved&&c.suspenseId===u.pendingId&&(u.deps--,u.deps===0&&u.resolve())},Le=(c,u,h,m=!1,g=!1,C=0)=>{for(let v=C;vc.shapeFlag&6?Ht(c.component.subTree):c.shapeFlag&128?c.suspense.next():y(c.anchor||c.el),ds=(c,u,h)=>{c==null?u._vnode&&Oe(u._vnode,null,null,!0):P(u._vnode||null,c,u,null,null,null,h),vs(),Gt(),u._vnode=c},ot={p:P,um:Oe,m:Ye,r:as,mt:F,mc:T,pc:Q,pbc:j,n:Ht,o:e};let yn,wn;return t&&([yn,wn]=t(ot)),{render:ds,hydrate:yn,createApp:nl(ds,yn)}}function Je({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function Yr(e,t,n=!1){const s=e.children,r=t.children;if(N(s)&&N(r))for(let i=0;i>1,e[n[l]]0&&(t[s]=n[i-1]),n[i]=s)}}for(i=n.length,o=n[i-1];i-- >0;)n[i]=o,o=t[o];return n}const ll=e=>e.__isTeleport,he=Symbol(void 0),mt=Symbol(void 0),be=Symbol(void 0),At=Symbol(void 0),Rt=[];let Re=null;function Jr(e=!1){Rt.push(Re=e?null:[])}function cl(){Rt.pop(),Re=Rt[Rt.length-1]||null}let Mt=1;function Ls(e){Mt+=e}function Xr(e){return e.dynamicChildren=Mt>0?Re||ft:null,cl(),Mt>0&&Re&&Re.push(e),e}function Lc(e,t,n,s,r,i){return Xr(Gr(e,t,n,s,r,i,!0))}function Zr(e,t,n,s,r){return Xr(le(e,t,n,s,r,!0))}function sn(e){return e?e.__v_isVNode===!0:!1}function et(e,t){return e.type===t.type&&e.key===t.key}const bn="__vInternal",Qr=({key:e})=>e??null,Yt=({ref:e,ref_key:t,ref_for:n})=>e!=null?re(e)||oe(e)||U(e)?{i:ue,r:e,k:t,f:!!n}:e:null;function Gr(e,t=null,n=null,s=0,r=null,i=e===he?0:1,o=!1,l=!1){const f={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Qr(t),ref:t&&Yt(t),scopeId:dn,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:s,dynamicProps:r,dynamicChildren:null,appContext:null,ctx:ue};return l?(fs(f,n),i&128&&e.normalize(f)):n&&(f.shapeFlag|=re(n)?8:16),Mt>0&&!o&&Re&&(f.patchFlag>0||i&6)&&f.patchFlag!==32&&Re.push(f),f}const le=fl;function fl(e,t=null,n=null,s=0,r=null,i=!1){if((!e||e===jr)&&(e=be),sn(e)){const l=Ve(e,t,!0);return n&&fs(l,n),Mt>0&&!i&&Re&&(l.shapeFlag&6?Re[Re.indexOf(e)]=l:Re.push(l)),l.patchFlag|=-2,l}if(yl(e)&&(e=e.__vccOpts),t){t=ul(t);let{class:l,style:f}=t;l&&!re(l)&&(t.class=Wn(l)),ee(f)&&(wr(f)&&!N(f)&&(f=ce({},f)),t.style=kn(f))}const o=re(e)?1:Io(e)?128:ll(e)?64:ee(e)?4:U(e)?2:0;return Gr(e,t,n,s,r,o,i,!0)}function ul(e){return e?wr(e)||bn in e?ce({},e):e:null}function Ve(e,t,n=!1){const{props:s,ref:r,patchFlag:i,children:o}=e,l=t?al(s||{},t):s;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&Qr(l),ref:t&&t.ref?n&&r?N(r)?r.concat(Yt(t)):[r,Yt(t)]:Yt(t):r,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:o,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==he?i===-1?16:i|16:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Ve(e.ssContent),ssFallback:e.ssFallback&&Ve(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function ei(e=" ",t=0){return le(mt,null,e,t)}function Nc(e,t){const n=le(At,null,e);return n.staticCount=t,n}function Hc(e="",t=!1){return t?(Jr(),Zr(be,null,e)):le(be,null,e)}function Te(e){return e==null||typeof e=="boolean"?le(be):N(e)?le(he,null,e.slice()):typeof e=="object"?Ke(e):le(mt,null,String(e))}function Ke(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Ve(e)}function fs(e,t){let n=0;const{shapeFlag:s}=e;if(t==null)t=null;else if(N(t))n=16;else if(typeof t=="object")if(s&65){const r=t.default;r&&(r._c&&(r._d=!1),fs(e,r()),r._c&&(r._d=!0));return}else{n=32;const r=t._;!r&&!(bn in t)?t._ctx=ue:r===3&&ue&&(ue.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else U(t)?(t={default:t,_ctx:ue},n=32):(t=String(t),s&64?(n=16,t=[ei(t)]):n=8);e.children=t,e.shapeFlag|=n}function al(...e){const t={};for(let n=0;nse||ue,_t=e=>{se=e,e.scope.on()},rt=()=>{se&&se.scope.off(),se=null};function ni(e){return e.vnode.shapeFlag&4}let St=!1;function gl(e,t=!1){St=t;const{props:n,children:s}=e.vnode,r=ni(e);Xo(e,n,r,t),Go(e,s);const i=r?ml(e,t):void 0;return St=!1,i}function ml(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=vt(new Proxy(e.ctx,Wo));const{setup:s}=n;if(s){const r=e.setupContext=s.length>1?ri(e):null;_t(e),bt();const i=We(s,e,0,[e.props,r]);if(yt(),rt(),or(i)){if(i.then(rt,rt),t)return i.then(o=>{Ns(e,o,t)}).catch(o=>{un(o,e,0)});e.asyncDep=i}else Ns(e,i,t)}else si(e,t)}function Ns(e,t,n){U(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:ee(t)&&(e.setupState=Er(t)),si(e,n)}let Hs;function si(e,t,n){const s=e.type;if(!e.render){if(!t&&Hs&&!s.render){const r=s.template||ls(e).template;if(r){const{isCustomElement:i,compilerOptions:o}=e.appContext.config,{delimiters:l,compilerOptions:f}=s,a=ce(ce({isCustomElement:i,delimiters:l},o),f);s.render=Hs(r,a)}}e.render=s.render||Ie}_t(e),bt(),qo(e),yt(),rt()}function _l(e){return new Proxy(e.attrs,{get(t,n){return pe(e,"get","$attrs"),t[n]}})}function ri(e){const t=s=>{e.exposed=s||{}};let n;return{get attrs(){return n||(n=_l(e))},slots:e.slots,emit:e.emit,expose:t}}function us(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Er(vt(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Tt)return Tt[n](e)},has(t,n){return n in t||n in Tt}}))}function bl(e,t=!0){return U(e)?e.displayName||e.name:e.name||t&&e.__name}function yl(e){return U(e)&&"__vccOpts"in e}const ve=(e,t)=>mo(e,t,St);function $c(){return wl().slots}function wl(){const e=ti();return e.setupContext||(e.setupContext=ri(e))}function Bn(e,t,n){const s=arguments.length;return s===2?ee(t)&&!N(t)?sn(t)?le(e,null,[t]):le(e,t):le(e,null,t):(s>3?n=Array.prototype.slice.call(arguments,2):s===3&&sn(n)&&(n=[n]),le(e,t,n))}const Cl=Symbol(""),xl=()=>ht(Cl),El="3.2.47",vl="http://www.w3.org/2000/svg",tt=typeof document<"u"?document:null,$s=tt&&tt.createElement("template"),Tl={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,s)=>{const r=t?tt.createElementNS(vl,e):tt.createElement(e,n?{is:n}:void 0);return e==="select"&&s&&s.multiple!=null&&r.setAttribute("multiple",s.multiple),r},createText:e=>tt.createTextNode(e),createComment:e=>tt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>tt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,s,r,i){const o=n?n.previousSibling:t.lastChild;if(r&&(r===i||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),!(r===i||!(r=r.nextSibling)););else{$s.innerHTML=s?`${e}`:e;const l=$s.content;if(s){const f=l.firstChild;for(;f.firstChild;)l.appendChild(f.firstChild);l.removeChild(f)}t.insertBefore(l,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}};function Al(e,t,n){const s=e._vtc;s&&(t=(t?[t,...s]:[...s]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}function Rl(e,t,n){const s=e.style,r=re(n);if(n&&!r){if(t&&!re(t))for(const i in t)n[i]==null&&Dn(s,i,"");for(const i in n)Dn(s,i,n[i])}else{const i=s.display;r?t!==n&&(s.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(s.display=i)}}const js=/\s*!important$/;function Dn(e,t,n){if(N(n))n.forEach(s=>Dn(e,t,s));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const s=Il(e,t);js.test(n)?e.setProperty(it(s),n.replace(js,""),"important"):e[s]=n}}const Us=["Webkit","Moz","ms"],Tn={};function Il(e,t){const n=Tn[t];if(n)return n;let s=Se(t);if(s!=="filter"&&s in e)return Tn[t]=s;s=ln(s);for(let r=0;rAn||(Nl.then(()=>An=0),An=Date.now());function $l(e,t){const n=s=>{if(!s._vts)s._vts=Date.now();else if(s._vts<=n.attached)return;xe(jl(s,n.value),t,5,[s])};return n.value=e,n.attached=Hl(),n}function jl(e,t){if(N(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(s=>r=>!r._stopped&&s&&s(r))}else return t}const Ks=/^on[a-z]/,Ul=(e,t,n,s,r=!1,i,o,l,f)=>{t==="class"?Al(e,s,r):t==="style"?Rl(e,n,s):Lt(t)?qn(t)||Sl(e,t,n,s,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Bl(e,t,s,r))?Pl(e,t,s,i,o,l,f):(t==="true-value"?e._trueValue=s:t==="false-value"&&(e._falseValue=s),Ol(e,t,s,r))};function Bl(e,t,n,s){return s?!!(t==="innerHTML"||t==="textContent"||t in e&&Ks.test(t)&&U(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||Ks.test(t)&&re(n)?!1:t in e}const Be="transition",Ct="animation",ii=(e,{slots:t})=>Bn(Mr,Dl(e),t);ii.displayName="Transition";const oi={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};ii.props=ce({},Mr.props,oi);const Xe=(e,t=[])=>{N(e)?e.forEach(n=>n(...t)):e&&e(...t)},ks=e=>e?N(e)?e.some(t=>t.length>1):e.length>1:!1;function Dl(e){const t={};for(const I in e)I in oi||(t[I]=e[I]);if(e.css===!1)return t;const{name:n="v",type:s,duration:r,enterFromClass:i=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:f=i,appearActiveClass:a=o,appearToClass:d=l,leaveFromClass:p=`${n}-leave-from`,leaveActiveClass:y=`${n}-leave-active`,leaveToClass:R=`${n}-leave-to`}=e,L=Kl(r),P=L&&L[0],W=L&&L[1],{onBeforeEnter:_,onEnter:E,onEnterCancelled:A,onLeave:$,onLeaveCancelled:D,onBeforeAppear:Z=_,onAppear:q=E,onAppearCancelled:T=A}=t,B=(I,K,F)=>{Ze(I,K?d:l),Ze(I,K?a:o),F&&F()},j=(I,K)=>{I._isLeaving=!1,Ze(I,p),Ze(I,R),Ze(I,y),K&&K()},Y=I=>(K,F)=>{const ge=I?q:E,J=()=>B(K,I,F);Xe(ge,[K,J]),Ws(()=>{Ze(K,I?f:i),De(K,I?d:l),ks(ge)||qs(K,s,P,J)})};return ce(t,{onBeforeEnter(I){Xe(_,[I]),De(I,i),De(I,o)},onBeforeAppear(I){Xe(Z,[I]),De(I,f),De(I,a)},onEnter:Y(!1),onAppear:Y(!0),onLeave(I,K){I._isLeaving=!0;const F=()=>j(I,K);De(I,p),ql(),De(I,y),Ws(()=>{I._isLeaving&&(Ze(I,p),De(I,R),ks($)||qs(I,s,W,F))}),Xe($,[I,F])},onEnterCancelled(I){B(I,!1),Xe(A,[I])},onAppearCancelled(I){B(I,!0),Xe(T,[I])},onLeaveCancelled(I){j(I),Xe(D,[I])}})}function Kl(e){if(e==null)return null;if(ee(e))return[Rn(e.enter),Rn(e.leave)];{const t=Rn(e);return[t,t]}}function Rn(e){return Oi(e)}function De(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e._vtc||(e._vtc=new Set)).add(t)}function Ze(e,t){t.split(/\s+/).forEach(s=>s&&e.classList.remove(s));const{_vtc:n}=e;n&&(n.delete(t),n.size||(e._vtc=void 0))}function Ws(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let kl=0;function qs(e,t,n,s){const r=e._endId=++kl,i=()=>{r===e._endId&&s()};if(n)return setTimeout(i,n);const{type:o,timeout:l,propCount:f}=Wl(e,t);if(!o)return s();const a=o+"end";let d=0;const p=()=>{e.removeEventListener(a,y),i()},y=R=>{R.target===e&&++d>=f&&p()};setTimeout(()=>{d(n[L]||"").split(", "),r=s(`${Be}Delay`),i=s(`${Be}Duration`),o=Vs(r,i),l=s(`${Ct}Delay`),f=s(`${Ct}Duration`),a=Vs(l,f);let d=null,p=0,y=0;t===Be?o>0&&(d=Be,p=o,y=i.length):t===Ct?a>0&&(d=Ct,p=a,y=f.length):(p=Math.max(o,a),d=p>0?o>a?Be:Ct:null,y=d?d===Be?i.length:f.length:0);const R=d===Be&&/\b(transform|all)(,|$)/.test(s(`${Be}Property`).toString());return{type:d,timeout:p,propCount:y,hasTransform:R}}function Vs(e,t){for(;e.lengthzs(n)+zs(e[s])))}function zs(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function ql(){return document.body.offsetHeight}const Vl=["ctrl","shift","alt","meta"],zl={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Vl.some(n=>e[`${n}Key`]&&!t.includes(n))},jc=(e,t)=>(n,...s)=>{for(let r=0;rn=>{if(!("key"in n))return;const s=it(n.key);if(t.some(r=>r===s||Yl[r]===s))return e(n)},Jl=ce({patchProp:Ul},Tl);let In,Ys=!1;function Xl(){return In=Ys?In:rl(Jl),Ys=!0,In}const Bc=(...e)=>{const t=Xl().createApp(...e),{mount:n}=t;return t.mount=s=>{const r=Zl(s);if(r)return n(r,!0,r instanceof SVGElement)},t};function Zl(e){return re(e)?document.querySelector(e):e}const Dc=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},Ql="modulepreload",Gl=function(e){return"/"+e},Js={},Kc=function(t,n,s){if(!n||n.length===0)return t();const r=document.getElementsByTagName("link");return Promise.all(n.map(i=>{if(i=Gl(i),i in Js)return;Js[i]=!0;const o=i.endsWith(".css"),l=o?'[rel="stylesheet"]':"";if(!!s)for(let d=r.length-1;d>=0;d--){const p=r[d];if(p.href===i&&(!o||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${l}`))return;const a=document.createElement("link");if(a.rel=o?"stylesheet":Ql,o||(a.as="script",a.crossOrigin=""),a.href=i,document.head.appendChild(a),o)return new Promise((d,p)=>{a.addEventListener("load",d),a.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${i}`)))})})).then(()=>t())},ec=window.__VP_SITE_DATA__,li=/^[a-z]+:/i,kc=/^pathname:\/\//,Wc="vitepress-theme-appearance",ci=/#.*$/,tc=/(index)?\.(md|html)$/,Ce=typeof document<"u",fi={relativePath:"",title:"404",description:"Not Found",headers:[],frontmatter:{sidebar:!1,layout:"page"},lastUpdated:0,isNotFound:!0};function nc(e,t,n=!1){if(t===void 0)return!1;if(e=Xs(`/${e}`),n)return new RegExp(t).test(e);if(Xs(t)!==e)return!1;const s=t.match(ci);return s?(Ce?location.hash:"")===s[0]:!0}function Xs(e){return decodeURI(e).replace(ci,"").replace(tc,"")}function sc(e){return li.test(e)}function rc(e,t){var s,r,i,o,l,f,a;const n=Object.keys(e.locales).find(d=>d!=="root"&&!sc(d)&&nc(t,`/${d}/`,!0))||"root";return Object.assign({},e,{localeIndex:n,lang:((s=e.locales[n])==null?void 0:s.lang)??e.lang,dir:((r=e.locales[n])==null?void 0:r.dir)??e.dir,title:((i=e.locales[n])==null?void 0:i.title)??e.title,titleTemplate:((o=e.locales[n])==null?void 0:o.titleTemplate)??e.titleTemplate,description:((l=e.locales[n])==null?void 0:l.description)??e.description,head:ai(e.head,((f=e.locales[n])==null?void 0:f.head)??[]),themeConfig:{...e.themeConfig,...(a=e.locales[n])==null?void 0:a.themeConfig}})}function ui(e,t){const n=t.title||e.title,s=t.titleTemplate??e.titleTemplate;if(typeof s=="string"&&s.includes(":title"))return s.replace(/:title/g,n);const r=ic(e.title,s);return`${n}${r}`}function ic(e,t){return t===!1?"":t===!0||t===void 0?` | ${e}`:e===t?"":` | ${t}`}function oc(e,t){const[n,s]=t;if(n!=="meta")return!1;const r=Object.entries(s)[0];return r==null?!1:e.some(([i,o])=>i===n&&o[r[0]]===r[1])}function ai(e,t){return[...e.filter(n=>!oc(t,n)),...t]}const lc=/[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g,cc=/^[a-z]:/i;function Zs(e){const t=cc.exec(e),n=t?t[0]:"";return n+e.slice(n.length).replace(lc,"_").replace(/(^|\/)_+(?=[^/]*$)/,"$1")}const fc=Symbol(),nt=co(ec);function qc(e){const t=ve(()=>rc(nt.value,e.data.relativePath));return{site:t,theme:ve(()=>t.value.themeConfig),page:ve(()=>e.data),frontmatter:ve(()=>e.data.frontmatter),params:ve(()=>e.data.params),lang:ve(()=>t.value.lang),dir:ve(()=>t.value.dir),localeIndex:ve(()=>t.value.localeIndex||"root"),title:ve(()=>ui(t.value,e.data)),description:ve(()=>e.data.description||t.value.description),isDark:Cr(!1)}}function Vc(){const e=ht(fc);if(!e)throw new Error("vitepress data not properly injected in app");return e}function uc(e,t){return`${e}${t}`.replace(/\/+/g,"/")}function Qs(e){return li.test(e)||e.startsWith(".")?e:uc(nt.value.base,e)}function ac(e){let t=e.replace(/\.html$/,"");if(t=decodeURIComponent(t),t=t.replace(/\/$/,"/index"),Ce){const n="/";t=Zs(t.slice(n.length).replace(/\//g,"_")||"index")+".md";let s=__VP_HASH_MAP__[t.toLowerCase()];s||(t=t.endsWith("_index.md")?t.slice(0,-9)+".md":t.slice(0,-3)+"_index.md",s=__VP_HASH_MAP__[t.toLowerCase()]),t=`${n}assets/${t}.${s}.js`}else t=`./${Zs(t.slice(1).replace(/\//g,"_"))}.md.js`;return t}let Jt=[];function zc(e){Jt.push(e),_n(()=>{Jt=Jt.filter(t=>t!==e)})}const dc=Symbol(),Gs="http://a.com",hc=()=>({path:"/",component:null,data:fi});function Yc(e,t){const n=fn(hc()),s={route:n,go:r};async function r(l=Ce?location.href:"/"){var a,d;await((a=s.onBeforeRouteChange)==null?void 0:a.call(s,l));const f=new URL(l,Gs);nt.value.cleanUrls||!f.pathname.endsWith("/")&&!f.pathname.endsWith(".html")&&(f.pathname+=".html",l=f.pathname+f.search+f.hash),Ce&&l!==location.href&&(history.replaceState({scrollPosition:window.scrollY},document.title),history.pushState(null,"",l)),await o(l),await((d=s.onAfterRouteChanged)==null?void 0:d.call(s,l))}let i=null;async function o(l,f=0,a=!1){const d=new URL(l,Gs),p=i=d.pathname;try{let y=await e(p);if(i===p){i=null;const{default:R,__pageData:L}=y;if(!R)throw new Error(`Invalid route component: ${R}`);n.path=Ce?p:Qs(p),n.component=vt(R),n.data=vt(L),Ce&&Ar(()=>{let P=nt.value.base+L.relativePath.replace(/(?:(^|\/)index)?\.md$/,"$1");if(!nt.value.cleanUrls&&!P.endsWith("/")&&(P+=".html"),P!==d.pathname&&(d.pathname=P,l=P+d.search+d.hash,history.replaceState(null,"",l)),d.hash&&!f){let W=null;try{W=document.querySelector(decodeURIComponent(d.hash))}catch(_){console.warn(_)}if(W){er(W,d.hash);return}}window.scrollTo(0,f)})}}catch(y){if(!/fetch/.test(y.message)&&!/^\/404(\.html|\/)?$/.test(l)&&console.error(y),!a)try{const R=await fetch(nt.value.base+"hashmap.json");window.__VP_HASH_MAP__=await R.json(),await o(l,f,!0);return}catch{}i===p&&(i=null,n.path=Ce?p:Qs(p),n.component=t?vt(t):null,n.data=fi)}}return Ce&&(window.addEventListener("click",l=>{if(l.target.closest("button"))return;const a=l.target.closest("a");if(a&&!a.closest(".vp-raw")&&(a instanceof SVGElement||!a.download)){const{target:d}=a,{href:p,origin:y,pathname:R,hash:L,search:P}=new URL(a.href instanceof SVGAnimatedString?a.href.animVal:a.href,a.baseURI),W=window.location,_=R.match(/\.\w+$/);!l.ctrlKey&&!l.shiftKey&&!l.altKey&&!l.metaKey&&d!=="_blank"&&y===W.origin&&!(_&&_[0]!==".html")&&(l.preventDefault(),R===W.pathname&&P===W.search?L&&L!==W.hash&&(history.pushState(null,"",L),window.dispatchEvent(new Event("hashchange")),er(a,L,a.classList.contains("header-anchor"))):r(p))}},{capture:!0}),window.addEventListener("popstate",l=>{o(location.href,l.state&&l.state.scrollPosition||0)}),window.addEventListener("hashchange",l=>{l.preventDefault()})),s}function pc(){const e=ht(dc);if(!e)throw new Error("useRouter() is called without provider.");return e}function di(){return pc().route}function er(e,t,n=!1){let s=null;try{s=e.classList.contains("header-anchor")?e:document.querySelector(decodeURIComponent(t))}catch(r){console.warn(r)}if(s){const r=nt.value.scrollOffset;let i=0;if(typeof r=="number")i=r;else if(typeof r=="string")i=tr(r);else if(Array.isArray(r))for(const f of r){const a=tr(f);if(a){i=a;break}}const o=parseInt(window.getComputedStyle(s).paddingTop,10),l=window.scrollY+s.getBoundingClientRect().top-i+o;!n||Math.abs(l-window.scrollY)>window.innerHeight?window.scrollTo(0,l):window.scrollTo({left:0,top:l,behavior:"smooth"})}}function tr(e){const t=document.querySelector(e);if(!t)return 0;const n=t.getBoundingClientRect().bottom;return n<0?0:n+24}const nr=()=>Jt.forEach(e=>e()),Jc=Nr({name:"VitePressContent",props:{as:{type:[Object,String],default:"div"}},setup(e){const t=di();return()=>Bn(e.as,{style:{position:"relative"}},[t.component?Bn(t.component,{onVnodeMounted:nr,onVnodeUpdated:nr}):"404 Page Not Found"])}});function Xc(e,t){let n=[],s=!0;const r=i=>{if(s){s=!1;return}n.forEach(o=>document.head.removeChild(o)),n=[],i.forEach(o=>{const l=gc(o);document.head.appendChild(l),n.push(l)})};Po(()=>{const i=e.data,o=t.value,l=i&&i.description,f=i&&i.frontmatter.head||[];document.title=ui(o,i),document.querySelector("meta[name=description]").setAttribute("content",l||o.description),r(ai(o.head,_c(f)))})}function gc([e,t,n]){const s=document.createElement(e);for(const r in t)s.setAttribute(r,t[r]);return n&&(s.innerHTML=n),s}function mc(e){return e[0]==="meta"&&e[1]&&e[1].name==="description"}function _c(e){return e.filter(t=>!mc(t))}const On=new Set,hi=()=>document.createElement("link"),bc=e=>{const t=hi();t.rel="prefetch",t.href=e,document.head.appendChild(t)},yc=e=>{const t=new XMLHttpRequest;t.open("GET",e,t.withCredentials=!0),t.send()};let qt;const wc=Ce&&(qt=hi())&&qt.relList&&qt.relList.supports&&qt.relList.supports("prefetch")?bc:yc;function Zc(){if(!Ce||!window.IntersectionObserver)return;let e;if((e=navigator.connection)&&(e.saveData||/2g/.test(e.effectiveType)))return;const t=window.requestIdleCallback||setTimeout;let n=null;const s=()=>{n&&n.disconnect(),n=new IntersectionObserver(i=>{i.forEach(o=>{if(o.isIntersecting){const l=o.target;n.unobserve(l);const{pathname:f}=l;if(!On.has(f)){On.add(f);const a=ac(f);wc(a)}}})}),t(()=>{document.querySelectorAll("#app a").forEach(i=>{const{target:o}=i,{hostname:l,pathname:f}=new URL(i.href instanceof SVGAnimatedString?i.href.animVal:i.href,i.baseURI),a=f.match(/\.\w+$/);a&&a[0]!==".html"||o!=="_blank"&&l===location.hostname&&(f!==location.pathname?n.observe(i):On.add(f))})})};mn(s);const r=di();zt(()=>r.path,s),_n(()=>{n&&n.disconnect()})}const Qc=Nr({setup(e,{slots:t}){const n=Cr(!1);return mn(()=>{n.value=!0}),()=>n.value&&t.default?t.default():null}});function Gc(){if(Ce){const e=new Map;window.addEventListener("click",t=>{var s;const n=t.target;if(n.matches('div[class*="language-"] > button.copy')){const r=n.parentElement,i=(s=n.nextElementSibling)==null?void 0:s.nextElementSibling;if(!r||!i)return;const o=/language-(shellscript|shell|bash|sh|zsh)/.test(r.className);let l="";i.querySelectorAll("span.line:not(.diff.remove)").forEach(f=>l+=(f.textContent||"")+` +`),l=l.slice(0,-1),o&&(l=l.replace(/^ *(\$|>) /gm,"").trim()),Cc(l).then(()=>{n.classList.add("copied"),clearTimeout(e.get(n));const f=setTimeout(()=>{n.classList.remove("copied"),n.blur(),e.delete(n)},2e3);e.set(n,f)})}})}}async function Cc(e){try{return navigator.clipboard.writeText(e)}catch{const t=document.createElement("textarea"),n=document.activeElement;t.value=e,t.setAttribute("readonly",""),t.style.contain="strict",t.style.position="absolute",t.style.left="-9999px",t.style.fontSize="12pt";const s=document.getSelection(),r=s?s.rangeCount>0&&s.getRangeAt(0):null;document.body.appendChild(t),t.select(),t.selectionStart=0,t.selectionEnd=e.length,document.execCommand("copy"),document.body.removeChild(t),r&&(s.removeAllRanges(),s.addRange(r)),n&&n.focus()}}function ef(){Ce&&window.addEventListener("click",e=>{var n,s;const t=e.target;if(t.matches(".vp-code-group input")){const r=(n=t.parentElement)==null?void 0:n.parentElement,i=Array.from((r==null?void 0:r.querySelectorAll("input"))||[]).indexOf(t),o=r==null?void 0:r.querySelector('div[class*="language-"].active'),l=(s=r==null?void 0:r.querySelectorAll('div[class*="language-"]:not(.language-id)'))==null?void 0:s[i];o&&l&&o!==l&&(o.classList.remove("active"),l.classList.add("active"))}})}export{Ic as $,Zr as A,Eo as B,Hc as C,al as D,le as E,he as F,Oc as G,kn as H,Pc as I,li as J,Ce as K,Fc as L,Ac as M,Rc as N,Nc as O,kc as P,Wc as Q,ht as R,Oo as S,ii as T,Uo as U,zc as V,co as W,Ar as X,Sc as Y,Uc as Z,Dc as _,ei as a,jc as a0,$c as a1,Xc as a2,Zc as a3,Gc as a4,ef as a5,Bn as a6,dc as a7,qc as a8,fc as a9,Jc as aa,Qc as ab,nt as ac,Bc as ad,Yc as ae,ac as af,Kc as ag,uo as b,Lc as c,Nr as d,Tc as e,yr as f,vc as g,Cr as h,Si as i,Ec as j,Po as k,ve as l,ti as m,Wn as n,Jr as o,mn as p,sc as q,Mc as r,Qs as s,xc as t,Vc as u,nc as v,zt as w,di as x,_n as y,Gr as z}; diff --git a/assets/chunks/theme.0fe641a0.js b/assets/chunks/theme.0fe641a0.js new file mode 100644 index 0000000..f0e58c0 --- /dev/null +++ b/assets/chunks/theme.0fe641a0.js @@ -0,0 +1,7 @@ +import{d as g,o as a,c as i,r as u,n as M,a as I,t as P,_ as p,u as Qe,b as c,e as Je,f as Ae,g as Ze,h as x,i as et,j as tt,w as q,k as Q,l as k,m as nt,p as F,q as st,P as ot,s as he,v as K,x as ee,y as fe,z as r,F as S,A as y,B as v,C as m,T as pe,D as se,E as h,G as R,H as He,I as U,J as Ee,K as at,L as N,M as H,N as E,O as ct,Q as Be,R as me,S as oe,U as lt,V as J,W as ge,X as it,Y as rt,Z as ut,$ as dt,a0 as _t,a1 as vt}from"./framework.41379913.js";const ht=g({__name:"VPBadge",props:{text:null,type:null},setup(e){return(t,n)=>(a(),i("span",{class:M(["VPBadge",e.type??"tip"])},[u(t.$slots,"default",{},()=>[I(P(e.text),1)],!0)],2))}});const ft=p(ht,[["__scopeId","data-v-350d3852"]]),V=Qe;function ze(e){return et()?(tt(e),!0):!1}function De(e){return typeof e=="function"?e():c(e)}const pt=typeof window<"u",Fe=()=>{};function mt(...e){if(e.length!==1)return Je(...e);const t=e[0];return typeof t=="function"?Ae(Ze(()=>({get:t,set:Fe}))):x(t)}function gt(e){var t;const n=De(e);return(t=n==null?void 0:n.$el)!=null?t:n}const ye=pt?window:void 0;function yt(...e){let t,n,s,o;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,s,o]=e,t=ye):[t,n,s,o]=e,!t)return Fe;Array.isArray(n)||(n=[n]),Array.isArray(s)||(s=[s]);const l=[],d=()=>{l.forEach(L=>L()),l.length=0},f=(L,C,w,$)=>(L.addEventListener(C,w,$),()=>L.removeEventListener(C,w,$)),_=q(()=>[gt(t),De(o)],([L,C])=>{d(),L&&l.push(...n.flatMap(w=>s.map($=>f(L,w,$,C))))},{immediate:!0,flush:"post"}),b=()=>{_(),d()};return ze(b),b}function bt(){const e=x(!1);return nt()&&F(()=>{e.value=!0}),e}function kt(e){const t=bt();return k(()=>(t.value,!!e()))}function de(e,t={}){const{window:n=ye}=t,s=kt(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let o;const l=x(!1),d=()=>{o&&("removeEventListener"in o?o.removeEventListener("change",f):o.removeListener(f))},f=()=>{s.value&&(d(),o=n.matchMedia(mt(e).value),l.value=!!(o!=null&&o.matches),o&&("addEventListener"in o?o.addEventListener("change",f):o.addListener(f)))};return Q(f),ze(()=>d()),l}function $t({window:e=ye}={}){if(!e)return{x:x(0),y:x(0)};const t=x(e.scrollX),n=x(e.scrollY);return yt(e,"scroll",()=>{t.value=e.scrollX,n.value=e.scrollY},{capture:!1,passive:!0}),{x:t,y:n}}function Pt(e,t){let n,s=!1;return()=>{n&&clearTimeout(n),s?n=setTimeout(e,t):(e(),s=!0,setTimeout(()=>{s=!1},t))}}function _e(e){return/^\//.test(e)?e:`/${e}`}function Z(e){if(st(e))return e.replace(ot,"");const{site:t}=V(),{pathname:n,search:s,hash:o}=new URL(e,"http://example.com"),l=n.endsWith("/")||n.endsWith(".html")?e:e.replace(/(?:(^\.+)\/)?.*$/,`$1${n.replace(/(\.md)?$/,t.value.cleanUrls?"":".html")}${s}${o}`);return he(l)}function Oe(e,t){if(Array.isArray(e))return e;if(e==null)return[];t=_e(t);const n=Object.keys(e).sort((s,o)=>o.split("/").length-s.split("/").length).find(s=>t.startsWith(_e(s)));return n?e[n]:[]}function Vt(e){const t=[];let n=0;for(const s in e){const o=e[s];if(o.items){n=t.push(o);continue}t[n]||t.push({items:[]}),t[n].items.push(o)}return t}function wt(e){const t=[];function n(s){for(const o of s)o.text&&o.link&&t.push({text:o.text,link:o.link}),o.items&&n(o.items)}return n(e),t}function ve(e,t){return Array.isArray(t)?t.some(n=>ve(e,n)):K(e,t.link)?!0:t.items?ve(e,t.items):!1}function z(){const e=ee(),{theme:t,frontmatter:n}=V(),s=de("(min-width: 960px)"),o=x(!1),l=k(()=>{const B=t.value.sidebar,T=e.data.relativePath;return B?Oe(B,T):[]}),d=k(()=>n.value.sidebar!==!1&&l.value.length>0&&n.value.layout!=="home"),f=k(()=>_?n.value.aside==null?t.value.aside==="left":n.value.aside==="left":!1),_=k(()=>n.value.layout==="home"?!1:n.value.aside!=null?!!n.value.aside:t.value.aside!==!1),b=k(()=>d.value&&s.value),L=k(()=>d.value?Vt(l.value):[]);function C(){o.value=!0}function w(){o.value=!1}function $(){o.value?w():C()}return{isOpen:o,sidebar:l,sidebarGroups:L,hasSidebar:d,hasAside:_,leftAside:f,isSidebarEnabled:b,open:C,close:w,toggle:$}}function xt(e,t){let n;Q(()=>{n=e.value?document.activeElement:void 0}),F(()=>{window.addEventListener("keyup",s)}),fe(()=>{window.removeEventListener("keyup",s)});function s(o){o.key==="Escape"&&e.value&&(t(),n==null||n.focus())}}function St(e){const{page:t}=V(),n=x(!1),s=k(()=>e.value.collapsed!=null),o=k(()=>!!e.value.link),l=k(()=>K(t.value.relativePath,e.value.link)),d=k(()=>l.value?!0:e.value.items?ve(t.value.relativePath,e.value.items):!1),f=k(()=>!!(e.value.items&&e.value.items.length));Q(()=>{n.value=!!(s.value&&e.value.collapsed)}),Q(()=>{(l.value||d.value)&&(n.value=!1)});function _(){s.value&&(n.value=!n.value)}return{collapsed:n,collapsible:s,isLink:o,isActiveLink:l,hasActiveLink:d,hasChildren:f,toggle:_}}const Lt=g({__name:"VPSkipLink",setup(e){const t=ee(),n=x();q(()=>t.path,()=>n.value.focus());function s({target:o}){const l=document.querySelector(decodeURIComponent(o.hash));if(l){const d=()=>{l.removeAttribute("tabindex"),l.removeEventListener("blur",d)};l.setAttribute("tabindex","-1"),l.addEventListener("blur",d),l.focus(),window.scrollTo(0,0)}}return(o,l)=>(a(),i(S,null,[r("span",{ref_key:"backToTop",ref:n,tabindex:"-1"},null,512),r("a",{href:"#VPContent",class:"VPSkipLink visually-hidden",onClick:s}," Skip to content ")],64))}});const Mt=p(Lt,[["__scopeId","data-v-c8616af1"]]),Ct={key:0,class:"VPBackdrop"},Bt=g({__name:"VPBackdrop",props:{show:{type:Boolean}},setup(e){return(t,n)=>(a(),y(pe,{name:"fade"},{default:v(()=>[e.show?(a(),i("div",Ct)):m("",!0)]),_:1}))}});const It=p(Bt,[["__scopeId","data-v-c79a1216"]]);function Nt(){const e=x(!1);function t(){e.value=!0,window.addEventListener("resize",o)}function n(){e.value=!1,window.removeEventListener("resize",o)}function s(){e.value?n():t()}function o(){window.outerWidth>=768&&n()}const l=ee();return q(()=>l.path,n),{isScreenOpen:e,openScreen:t,closeScreen:n,toggleScreen:s}}function te({removeCurrent:e=!0,correspondingLink:t=!1}={}){const{site:n,localeIndex:s,page:o,theme:l}=V(),d=k(()=>{var _,b;return{label:(_=n.value.locales[s.value])==null?void 0:_.label,link:((b=n.value.locales[s.value])==null?void 0:b.link)||(s.value==="root"?"/":`/${s.value}/`)}});return{localeLinks:k(()=>Object.entries(n.value.locales).flatMap(([_,b])=>e&&d.value.label===b.label?[]:{text:b.label,link:Tt(b.link||(_==="root"?"/":`/${_}/`),l.value.i18nRouting!==!1&&t,o.value.relativePath.slice(d.value.link.length-1),!n.value.cleanUrls)})),currentLang:d}}function Tt(e,t,n,s){return t?e.replace(/\/$/,"")+_e(n.replace(/(^|\/)?index.md$/,"$1").replace(/\.md$/,s?".html":"")):e}const At=["src","alt"],Ht={inheritAttrs:!1},Et=g({...Ht,__name:"VPImage",props:{image:null,alt:null},setup(e){return(t,n)=>{const s=R("VPImage",!0);return e.image?(a(),i(S,{key:0},[typeof e.image=="string"||"src"in e.image?(a(),i("img",se({key:0,class:"VPImage"},typeof e.image=="string"?t.$attrs:{...e.image,...t.$attrs},{src:c(he)(typeof e.image=="string"?e.image:e.image.src),alt:e.alt??(typeof e.image=="string"?"":e.image.alt||"")}),null,16,At)):(a(),i(S,{key:1},[h(s,se({class:"dark",image:e.image.dark,alt:e.image.alt},t.$attrs),null,16,["image","alt"]),h(s,se({class:"light",image:e.image.light,alt:e.image.alt},t.$attrs),null,16,["image","alt"])],64))],64)):m("",!0)}}});const be=p(Et,[["__scopeId","data-v-6db2186b"]]),zt=["href"],Dt=g({__name:"VPNavBarTitle",setup(e){const{site:t,theme:n}=V(),{hasSidebar:s}=z(),{currentLang:o}=te();return(l,d)=>(a(),i("div",{class:M(["VPNavBarTitle",{"has-sidebar":c(s)}])},[r("a",{class:"title",href:c(Z)(c(o).link)},[u(l.$slots,"nav-bar-title-before",{},void 0,!0),c(n).logo?(a(),y(be,{key:0,class:"logo",image:c(n).logo},null,8,["image"])):m("",!0),c(n).siteTitle?(a(),i(S,{key:1},[I(P(c(n).siteTitle),1)],64)):c(n).siteTitle===void 0?(a(),i(S,{key:2},[I(P(c(t).title),1)],64)):m("",!0),u(l.$slots,"nav-bar-title-after",{},void 0,!0)],8,zt)],2))}});const Ft=p(Dt,[["__scopeId","data-v-f4ef19a3"]]);const Ot={type:"button",class:"DocSearch DocSearch-Button","aria-label":"Search"},Gt={class:"DocSearch-Button-Container"},Rt=r("svg",{class:"DocSearch-Search-Icon",width:"20",height:"20",viewBox:"0 0 20 20","aria-label":"search icon"},[r("path",{d:"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z",stroke:"currentColor",fill:"none","fill-rule":"evenodd","stroke-linecap":"round","stroke-linejoin":"round"})],-1),Ut={class:"DocSearch-Button-Placeholder"},jt=r("span",{class:"DocSearch-Button-Keys"},[r("kbd",{class:"DocSearch-Button-Key"}),r("kbd",{class:"DocSearch-Button-Key"},"K")],-1),Ie=g({__name:"VPNavBarSearchButton",props:{placeholder:null},setup(e){return(t,n)=>(a(),i("button",Ot,[r("span",Gt,[Rt,r("span",Ut,P(e.placeholder),1)]),jt]))}});const qt={id:"local-search"},Kt={key:1,id:"docsearch"},Wt=g({__name:"VPNavBarSearch",setup(e){const t=()=>null,n=()=>null,{theme:s,localeIndex:o}=V(),l=x(!1),d=k(()=>{var $,B,T,A,D,ne,W;const w=(($=s.value.search)==null?void 0:$.options)??s.value.algolia;return((D=(A=(T=(B=w==null?void 0:w.locales)==null?void 0:B[o.value])==null?void 0:T.translations)==null?void 0:A.button)==null?void 0:D.buttonText)||((W=(ne=w==null?void 0:w.translations)==null?void 0:ne.button)==null?void 0:W.buttonText)||"Search"});F(()=>{});function f(){l.value||(l.value=!0,setTimeout(_,16))}function _(){const w=new Event("keydown");w.key="k",w.metaKey=!0,window.dispatchEvent(w),setTimeout(()=>{document.querySelector(".DocSearch-Modal")||_()},16)}const b=x(!1),L=x("'Meta'");F(()=>{L.value=/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)?"'⌘'":"'Ctrl'"});const C="";return(w,$)=>{var B;return a(),i("div",{class:"VPNavBarSearch",style:He({"--vp-meta-key":L.value})},[c(C)==="local"?(a(),i(S,{key:0},[b.value?(a(),y(c(t),{key:0,placeholder:c(d),onClose:$[0]||($[0]=T=>b.value=!1)},null,8,["placeholder"])):m("",!0),r("div",qt,[h(Ie,{placeholder:c(d),onClick:$[1]||($[1]=T=>b.value=!0)},null,8,["placeholder"])])],64)):c(C)==="algolia"?(a(),i(S,{key:1},[l.value?(a(),y(c(n),{key:0,algolia:((B=c(s).search)==null?void 0:B.options)??c(s).algolia},null,8,["algolia"])):(a(),i("div",Kt,[h(Ie,{placeholder:c(d),onClick:f},null,8,["placeholder"])]))],64)):m("",!0)],4)}}});const Yt={},Xt={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",height:"24px",viewBox:"0 0 24 24",width:"24px"},Qt=r("path",{d:"M0 0h24v24H0V0z",fill:"none"},null,-1),Jt=r("path",{d:"M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z"},null,-1),Zt=[Qt,Jt];function en(e,t){return a(),i("svg",Xt,Zt)}const tn=p(Yt,[["render",en]]),nn=g({__name:"VPLink",props:{tag:null,href:null,noIcon:{type:Boolean},target:null,rel:null},setup(e){const t=e,n=k(()=>t.tag??t.href?"a":"span"),s=k(()=>t.href&&Ee.test(t.href));return(o,l)=>(a(),y(U(c(n)),{class:M(["VPLink",{link:e.href}]),href:e.href?c(Z)(e.href):void 0,target:e.target||(c(s)?"_blank":void 0),rel:e.rel||(c(s)?"noreferrer":void 0)},{default:v(()=>[u(o.$slots,"default",{},void 0,!0),c(s)&&!e.noIcon?(a(),y(tn,{key:0,class:"icon"})):m("",!0)]),_:3},8,["class","href","target","rel"]))}});const O=p(nn,[["__scopeId","data-v-8f4dc553"]]),sn=g({__name:"VPNavBarMenuLink",props:{item:null},setup(e){const{page:t}=V();return(n,s)=>(a(),y(O,{class:M({VPNavBarMenuLink:!0,active:c(K)(c(t).relativePath,e.item.activeMatch||e.item.link,!!e.item.activeMatch)}),href:e.item.link,target:e.item.target,rel:e.item.rel,tabindex:"0"},{default:v(()=>[I(P(e.item.text),1)]),_:1},8,["class","href","target","rel"]))}});const on=p(sn,[["__scopeId","data-v-37adc828"]]),ke=x();let Ge=!1,ue=0;function an(e){const t=x(!1);if(at){!Ge&&cn(),ue++;const n=q(ke,s=>{var o,l,d;s===e.el.value||(o=e.el.value)!=null&&o.contains(s)?(t.value=!0,(l=e.onFocus)==null||l.call(e)):(t.value=!1,(d=e.onBlur)==null||d.call(e))});fe(()=>{n(),ue--,ue||ln()})}return Ae(t)}function cn(){document.addEventListener("focusin",Re),Ge=!0,ke.value=document.activeElement}function ln(){document.removeEventListener("focusin",Re)}function Re(){ke.value=document.activeElement}const rn={},un={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},dn=r("path",{d:"M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"},null,-1),_n=[dn];function vn(e,t){return a(),i("svg",un,_n)}const Ue=p(rn,[["render",vn]]),hn={},fn={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},pn=r("circle",{cx:"12",cy:"12",r:"2"},null,-1),mn=r("circle",{cx:"19",cy:"12",r:"2"},null,-1),gn=r("circle",{cx:"5",cy:"12",r:"2"},null,-1),yn=[pn,mn,gn];function bn(e,t){return a(),i("svg",fn,yn)}const kn=p(hn,[["render",bn]]),$n={class:"VPMenuLink"},Pn=g({__name:"VPMenuLink",props:{item:null},setup(e){const{page:t}=V();return(n,s)=>(a(),i("div",$n,[h(O,{class:M({active:c(K)(c(t).relativePath,e.item.activeMatch||e.item.link,!!e.item.activeMatch)}),href:e.item.link,target:e.item.target,rel:e.item.rel},{default:v(()=>[I(P(e.item.text),1)]),_:1},8,["class","href","target","rel"])]))}});const ie=p(Pn,[["__scopeId","data-v-d2c93bab"]]),Vn={class:"VPMenuGroup"},wn={key:0,class:"title"},xn=g({__name:"VPMenuGroup",props:{text:null,items:null},setup(e){return(t,n)=>(a(),i("div",Vn,[e.text?(a(),i("p",wn,P(e.text),1)):m("",!0),(a(!0),i(S,null,N(e.items,s=>(a(),i(S,null,["link"in s?(a(),y(ie,{key:0,item:s},null,8,["item"])):m("",!0)],64))),256))]))}});const Sn=p(xn,[["__scopeId","data-v-69e747b5"]]),Ln={class:"VPMenu"},Mn={key:0,class:"items"},Cn=g({__name:"VPMenu",props:{items:null},setup(e){return(t,n)=>(a(),i("div",Ln,[e.items?(a(),i("div",Mn,[(a(!0),i(S,null,N(e.items,s=>(a(),i(S,{key:s.text},["link"in s?(a(),y(ie,{key:0,item:s},null,8,["item"])):(a(),y(Sn,{key:1,text:s.text,items:s.items},null,8,["text","items"]))],64))),128))])):m("",!0),u(t.$slots,"default",{},void 0,!0)]))}});const Bn=p(Cn,[["__scopeId","data-v-e7ea1737"]]),In=["aria-expanded","aria-label"],Nn={key:0,class:"text"},Tn={class:"menu"},An=g({__name:"VPFlyout",props:{icon:null,button:null,label:null,items:null},setup(e){const t=x(!1),n=x();an({el:n,onBlur:s});function s(){t.value=!1}return(o,l)=>(a(),i("div",{class:"VPFlyout",ref_key:"el",ref:n,onMouseenter:l[1]||(l[1]=d=>t.value=!0),onMouseleave:l[2]||(l[2]=d=>t.value=!1)},[r("button",{type:"button",class:"button","aria-haspopup":"true","aria-expanded":t.value,"aria-label":e.label,onClick:l[0]||(l[0]=d=>t.value=!t.value)},[e.button||e.icon?(a(),i("span",Nn,[e.icon?(a(),y(U(e.icon),{key:0,class:"option-icon"})):m("",!0),I(" "+P(e.button)+" ",1),h(Ue,{class:"text-icon"})])):(a(),y(kn,{key:1,class:"icon"}))],8,In),r("div",Tn,[h(Bn,{items:e.items},{default:v(()=>[u(o.$slots,"default",{},void 0,!0)]),_:3},8,["items"])])],544))}});const $e=p(An,[["__scopeId","data-v-764effdf"]]),Hn=g({__name:"VPNavBarMenuGroup",props:{item:null},setup(e){const{page:t}=V();return(n,s)=>(a(),y($e,{class:M({VPNavBarMenuGroup:!0,active:c(K)(c(t).relativePath,e.item.activeMatch,!!e.item.activeMatch)}),button:e.item.text,items:e.item.items},null,8,["class","button","items"]))}}),En=e=>(H("data-v-7f418b0f"),e=e(),E(),e),zn={key:0,"aria-labelledby":"main-nav-aria-label",class:"VPNavBarMenu"},Dn=En(()=>r("span",{id:"main-nav-aria-label",class:"visually-hidden"},"Main Navigation",-1)),Fn=g({__name:"VPNavBarMenu",setup(e){const{theme:t}=V();return(n,s)=>c(t).nav?(a(),i("nav",zn,[Dn,(a(!0),i(S,null,N(c(t).nav,o=>(a(),i(S,{key:o.text},["link"in o?(a(),y(on,{key:0,item:o},null,8,["item"])):(a(),y(Hn,{key:1,item:o},null,8,["item"]))],64))),128))])):m("",!0)}});const On=p(Fn,[["__scopeId","data-v-7f418b0f"]]),Gn={},Rn={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Un=r("path",{d:"M0 0h24v24H0z",fill:"none"},null,-1),jn=r("path",{d:" M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z ",class:"css-c4d79v"},null,-1),qn=[Un,jn];function Kn(e,t){return a(),i("svg",Rn,qn)}const je=p(Gn,[["render",Kn]]),Wn={class:"items"},Yn={class:"title"},Xn=g({__name:"VPNavBarTranslations",setup(e){const{theme:t}=V(),{localeLinks:n,currentLang:s}=te({correspondingLink:!0});return(o,l)=>c(n).length&&c(s).label?(a(),y($e,{key:0,class:"VPNavBarTranslations",icon:je,label:c(t).langMenuLabel||"Change language"},{default:v(()=>[r("div",Wn,[r("p",Yn,P(c(s).label),1),(a(!0),i(S,null,N(c(n),d=>(a(),y(ie,{key:d.link,item:d},null,8,["item"]))),128))])]),_:1},8,["label"])):m("",!0)}});const Qn=p(Xn,[["__scopeId","data-v-74abcbb9"]]);const Jn={},Zn={class:"VPSwitch",type:"button",role:"switch"},es={class:"check"},ts={key:0,class:"icon"};function ns(e,t){return a(),i("button",Zn,[r("span",es,[e.$slots.default?(a(),i("span",ts,[u(e.$slots,"default",{},void 0,!0)])):m("",!0)])])}const ss=p(Jn,[["render",ns],["__scopeId","data-v-f3c41672"]]),os={},as={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},cs=ct('',9),ls=[cs];function is(e,t){return a(),i("svg",as,ls)}const rs=p(os,[["render",is]]),us={},ds={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},_s=r("path",{d:"M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"},null,-1),vs=[_s];function hs(e,t){return a(),i("svg",ds,vs)}const fs=p(us,[["render",hs]]),ps={title:"toggle dark mode"},ms=g({__name:"VPSwitchAppearance",setup(e){const{site:t,isDark:n}=V(),s=x(!1),o=typeof localStorage<"u"?l():()=>{};F(()=>{s.value=document.documentElement.classList.contains("dark")});function l(){const d=window.matchMedia("(prefers-color-scheme: dark)"),f=document.documentElement.classList;let _=localStorage.getItem(Be),b=t.value.appearance==="dark"&&_==null||(_==="auto"||_==null?d.matches:_==="dark");d.onchange=w=>{_==="auto"&&C(b=w.matches)};function L(){C(b=!b),_=b?d.matches?"auto":"dark":d.matches?"light":"auto",localStorage.setItem(Be,_)}function C(w){const $=document.createElement("style");$.type="text/css",$.appendChild(document.createTextNode(`:not(.VPSwitchAppearance):not(.VPSwitchAppearance *) { + -webkit-transition: none !important; + -moz-transition: none !important; + -o-transition: none !important; + -ms-transition: none !important; + transition: none !important; +}`)),document.head.appendChild($),s.value=w,f[w?"add":"remove"]("dark"),window.getComputedStyle($).opacity,document.head.removeChild($)}return L}return q(s,d=>{n.value=d}),(d,f)=>(a(),i("label",ps,[h(ss,{class:"VPSwitchAppearance","aria-checked":s.value,onClick:c(o)},{default:v(()=>[h(rs,{class:"sun"}),h(fs,{class:"moon"})]),_:1},8,["aria-checked","onClick"])]))}});const Pe=p(ms,[["__scopeId","data-v-a9c8afb8"]]),gs={key:0,class:"VPNavBarAppearance"},ys=g({__name:"VPNavBarAppearance",setup(e){const{site:t}=V();return(n,s)=>c(t).appearance?(a(),i("div",gs,[h(Pe)])):m("",!0)}});const bs=p(ys,[["__scopeId","data-v-f6a63727"]]),ks={discord:'Discord',facebook:'Facebook',github:'GitHub',instagram:'Instagram',linkedin:'LinkedIn',mastodon:'Mastodon',slack:'Slack',twitter:'Twitter',youtube:'YouTube'},$s=["href","aria-label","innerHTML"],Ps=g({__name:"VPSocialLink",props:{icon:null,link:null},setup(e){const t=e,n=k(()=>typeof t.icon=="object"?t.icon.svg:ks[t.icon]);return(s,o)=>(a(),i("a",{class:"VPSocialLink",href:e.link,"aria-label":typeof e.icon=="string"?e.icon:"",target:"_blank",rel:"noopener",innerHTML:c(n)},null,8,$s))}});const Vs=p(Ps,[["__scopeId","data-v-c530cc0a"]]),ws={class:"VPSocialLinks"},xs=g({__name:"VPSocialLinks",props:{links:null},setup(e){return(t,n)=>(a(),i("div",ws,[(a(!0),i(S,null,N(e.links,({link:s,icon:o})=>(a(),y(Vs,{key:s,icon:o,link:s},null,8,["icon","link"]))),128))]))}});const Ve=p(xs,[["__scopeId","data-v-f6988cfb"]]),Ss=g({__name:"VPNavBarSocialLinks",setup(e){const{theme:t}=V();return(n,s)=>c(t).socialLinks?(a(),y(Ve,{key:0,class:"VPNavBarSocialLinks",links:c(t).socialLinks},null,8,["links"])):m("",!0)}});const Ls=p(Ss,[["__scopeId","data-v-0394ad82"]]),Ms={key:0,class:"group translations"},Cs={class:"trans-title"},Bs={key:1,class:"group"},Is={class:"item appearance"},Ns={class:"label"},Ts={class:"appearance-action"},As={key:2,class:"group"},Hs={class:"item social-links"},Es=g({__name:"VPNavBarExtra",setup(e){const{site:t,theme:n}=V(),{localeLinks:s,currentLang:o}=te({correspondingLink:!0}),l=k(()=>s.value.length&&o.value.label||t.value.appearance||n.value.socialLinks);return(d,f)=>c(l)?(a(),y($e,{key:0,class:"VPNavBarExtra",label:"extra navigation"},{default:v(()=>[c(s).length&&c(o).label?(a(),i("div",Ms,[r("p",Cs,P(c(o).label),1),(a(!0),i(S,null,N(c(s),_=>(a(),y(ie,{key:_.link,item:_},null,8,["item"]))),128))])):m("",!0),c(t).appearance?(a(),i("div",Bs,[r("div",Is,[r("p",Ns,P(c(n).darkModeSwitchLabel||"Appearance"),1),r("div",Ts,[h(Pe)])])])):m("",!0),c(n).socialLinks?(a(),i("div",As,[r("div",Hs,[h(Ve,{class:"social-links-list",links:c(n).socialLinks},null,8,["links"])])])):m("",!0)]),_:1})):m("",!0)}});const zs=p(Es,[["__scopeId","data-v-40855f84"]]),Ds=e=>(H("data-v-e5dd9c1c"),e=e(),E(),e),Fs=["aria-expanded"],Os=Ds(()=>r("span",{class:"container"},[r("span",{class:"top"}),r("span",{class:"middle"}),r("span",{class:"bottom"})],-1)),Gs=[Os],Rs=g({__name:"VPNavBarHamburger",props:{active:{type:Boolean}},emits:["click"],setup(e){return(t,n)=>(a(),i("button",{type:"button",class:M(["VPNavBarHamburger",{active:e.active}]),"aria-label":"mobile navigation","aria-expanded":e.active,"aria-controls":"VPNavScreen",onClick:n[0]||(n[0]=s=>t.$emit("click"))},Gs,10,Fs))}});const Us=p(Rs,[["__scopeId","data-v-e5dd9c1c"]]),js=e=>(H("data-v-94c81dcc"),e=e(),E(),e),qs={class:"container"},Ks={class:"title"},Ws={class:"content"},Ys=js(()=>r("div",{class:"curtain"},null,-1)),Xs={class:"content-body"},Qs=g({__name:"VPNavBar",props:{isScreenOpen:{type:Boolean}},emits:["toggle-screen"],setup(e){const{y:t}=$t(),{hasSidebar:n}=z(),s=k(()=>({"has-sidebar":n.value,fill:t.value>0}));return(o,l)=>(a(),i("div",{class:M(["VPNavBar",c(s)])},[r("div",qs,[r("div",Ks,[h(Ft,null,{"nav-bar-title-before":v(()=>[u(o.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[u(o.$slots,"nav-bar-title-after",{},void 0,!0)]),_:3})]),r("div",Ws,[Ys,r("div",Xs,[u(o.$slots,"nav-bar-content-before",{},void 0,!0),h(Wt,{class:"search"}),h(On,{class:"menu"}),h(Qn,{class:"translations"}),h(bs,{class:"appearance"}),h(Ls,{class:"social-links"}),h(zs,{class:"extra"}),u(o.$slots,"nav-bar-content-after",{},void 0,!0),h(Us,{class:"hamburger",active:e.isScreenOpen,onClick:l[0]||(l[0]=d=>o.$emit("toggle-screen"))},null,8,["active"])])])])],2))}});const Js=p(Qs,[["__scopeId","data-v-94c81dcc"]]);function Zs(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t1),j=[],ce=!1,xe=-1,Y=void 0,G=void 0,X=void 0,qe=function(t){return j.some(function(n){return!!(n.options.allowTouchMove&&n.options.allowTouchMove(t))})},le=function(t){var n=t||window.event;return qe(n.target)||n.touches.length>1?!0:(n.preventDefault&&n.preventDefault(),!1)},eo=function(t){if(X===void 0){var n=!!t&&t.reserveScrollBarGap===!0,s=window.innerWidth-document.documentElement.clientWidth;if(n&&s>0){var o=parseInt(window.getComputedStyle(document.body).getPropertyValue("padding-right"),10);X=document.body.style.paddingRight,document.body.style.paddingRight=o+s+"px"}}Y===void 0&&(Y=document.body.style.overflow,document.body.style.overflow="hidden")},to=function(){X!==void 0&&(document.body.style.paddingRight=X,X=void 0),Y!==void 0&&(document.body.style.overflow=Y,Y=void 0)},no=function(){return window.requestAnimationFrame(function(){if(G===void 0){G={position:document.body.style.position,top:document.body.style.top,left:document.body.style.left};var t=window,n=t.scrollY,s=t.scrollX,o=t.innerHeight;document.body.style.position="fixed",document.body.style.top=-n,document.body.style.left=-s,setTimeout(function(){return window.requestAnimationFrame(function(){var l=o-window.innerHeight;l&&n>=o&&(document.body.style.top=-(n+l))})},300)}})},so=function(){if(G!==void 0){var t=-parseInt(document.body.style.top,10),n=-parseInt(document.body.style.left,10);document.body.style.position=G.position,document.body.style.top=G.top,document.body.style.left=G.left,window.scrollTo(n,t),G=void 0}},oo=function(t){return t?t.scrollHeight-t.scrollTop<=t.clientHeight:!1},ao=function(t,n){var s=t.targetTouches[0].clientY-xe;return qe(t.target)?!1:n&&n.scrollTop===0&&s>0||oo(n)&&s<0?le(t):(t.stopPropagation(),!0)},Ke=function(t,n){if(!t){console.error("disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.");return}if(!j.some(function(o){return o.targetElement===t})){var s={targetElement:t,options:n||{}};j=[].concat(Zs(j),[s]),ae?no():eo(n),ae&&(t.ontouchstart=function(o){o.targetTouches.length===1&&(xe=o.targetTouches[0].clientY)},t.ontouchmove=function(o){o.targetTouches.length===1&&ao(o,t)},ce||(document.addEventListener("touchmove",le,we?{passive:!1}:void 0),ce=!0))}},We=function(){ae&&(j.forEach(function(t){t.targetElement.ontouchstart=null,t.targetElement.ontouchmove=null}),ce&&(document.removeEventListener("touchmove",le,we?{passive:!1}:void 0),ce=!1),xe=-1),ae?so():to(),j=[]};const co=g({__name:"VPNavScreenMenuLink",props:{text:null,link:null},setup(e){const t=me("close-screen");return(n,s)=>(a(),y(O,{class:"VPNavScreenMenuLink",href:e.link,onClick:c(t)},{default:v(()=>[I(P(e.text),1)]),_:1},8,["href","onClick"]))}});const lo=p(co,[["__scopeId","data-v-c328f34f"]]),io={},ro={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},uo=r("path",{d:"M18.9,10.9h-6v-6c0-0.6-0.4-1-1-1s-1,0.4-1,1v6h-6c-0.6,0-1,0.4-1,1s0.4,1,1,1h6v6c0,0.6,0.4,1,1,1s1-0.4,1-1v-6h6c0.6,0,1-0.4,1-1S19.5,10.9,18.9,10.9z"},null,-1),_o=[uo];function vo(e,t){return a(),i("svg",ro,_o)}const ho=p(io,[["render",vo]]),fo=g({__name:"VPNavScreenMenuGroupLink",props:{text:null,link:null},setup(e){const t=me("close-screen");return(n,s)=>(a(),y(O,{class:"VPNavScreenMenuGroupLink",href:e.link,onClick:c(t)},{default:v(()=>[I(P(e.text),1)]),_:1},8,["href","onClick"]))}});const Ye=p(fo,[["__scopeId","data-v-3d20956d"]]),po={class:"VPNavScreenMenuGroupSection"},mo={key:0,class:"title"},go=g({__name:"VPNavScreenMenuGroupSection",props:{text:null,items:null},setup(e){return(t,n)=>(a(),i("div",po,[e.text?(a(),i("p",mo,P(e.text),1)):m("",!0),(a(!0),i(S,null,N(e.items,s=>(a(),y(Ye,{key:s.text,text:s.text,link:s.link},null,8,["text","link"]))),128))]))}});const yo=p(go,[["__scopeId","data-v-7478538b"]]),bo=["aria-controls","aria-expanded"],ko={class:"button-text"},$o=["id"],Po={key:1,class:"group"},Vo=g({__name:"VPNavScreenMenuGroup",props:{text:null,items:null},setup(e){const t=e,n=x(!1),s=k(()=>`NavScreenGroup-${t.text.replace(" ","-").toLowerCase()}`);function o(){n.value=!n.value}return(l,d)=>(a(),i("div",{class:M(["VPNavScreenMenuGroup",{open:n.value}])},[r("button",{class:"button","aria-controls":c(s),"aria-expanded":n.value,onClick:o},[r("span",ko,P(e.text),1),h(ho,{class:"button-icon"})],8,bo),r("div",{id:c(s),class:"items"},[(a(!0),i(S,null,N(e.items,f=>(a(),i(S,{key:f.text},["link"in f?(a(),i("div",{key:f.text,class:"item"},[h(Ye,{text:f.text,link:f.link},null,8,["text","link"])])):(a(),i("div",Po,[h(yo,{text:f.text,items:f.items},null,8,["text","items"])]))],64))),128))],8,$o)],2))}});const wo=p(Vo,[["__scopeId","data-v-a9a19324"]]),xo={key:0,class:"VPNavScreenMenu"},So=g({__name:"VPNavScreenMenu",setup(e){const{theme:t}=V();return(n,s)=>c(t).nav?(a(),i("nav",xo,[(a(!0),i(S,null,N(c(t).nav,o=>(a(),i(S,{key:o.text},["link"in o?(a(),y(lo,{key:0,text:o.text,link:o.link},null,8,["text","link"])):(a(),y(wo,{key:1,text:o.text||"",items:o.items},null,8,["text","items"]))],64))),128))])):m("",!0)}}),Lo={key:0,class:"VPNavScreenAppearance"},Mo={class:"text"},Co=g({__name:"VPNavScreenAppearance",setup(e){const{site:t,theme:n}=V();return(s,o)=>c(t).appearance?(a(),i("div",Lo,[r("p",Mo,P(c(n).darkModeSwitchLabel||"Appearance"),1),h(Pe)])):m("",!0)}});const Bo=p(Co,[["__scopeId","data-v-add8f686"]]),Io={class:"list"},No=g({__name:"VPNavScreenTranslations",setup(e){const{localeLinks:t,currentLang:n}=te({correspondingLink:!0}),s=x(!1);function o(){s.value=!s.value}return(l,d)=>c(t).length&&c(n).label?(a(),i("div",{key:0,class:M(["VPNavScreenTranslations",{open:s.value}])},[r("button",{class:"title",onClick:o},[h(je,{class:"icon lang"}),I(" "+P(c(n).label)+" ",1),h(Ue,{class:"icon chevron"})]),r("ul",Io,[(a(!0),i(S,null,N(c(t),f=>(a(),i("li",{key:f.link,class:"item"},[h(O,{class:"link",href:f.link},{default:v(()=>[I(P(f.text),1)]),_:2},1032,["href"])]))),128))])],2)):m("",!0)}});const To=p(No,[["__scopeId","data-v-d72aa483"]]),Ao=g({__name:"VPNavScreenSocialLinks",setup(e){const{theme:t}=V();return(n,s)=>c(t).socialLinks?(a(),y(Ve,{key:0,class:"VPNavScreenSocialLinks",links:c(t).socialLinks},null,8,["links"])):m("",!0)}}),Ho={class:"container"},Eo=g({__name:"VPNavScreen",props:{open:{type:Boolean}},setup(e){const t=x(null);function n(){Ke(t.value,{reserveScrollBarGap:!0})}function s(){We()}return(o,l)=>(a(),y(pe,{name:"fade",onEnter:n,onAfterLeave:s},{default:v(()=>[e.open?(a(),i("div",{key:0,class:"VPNavScreen",ref_key:"screen",ref:t},[r("div",Ho,[u(o.$slots,"nav-screen-content-before",{},void 0,!0),h(So,{class:"menu"}),h(To,{class:"translations"}),h(Bo,{class:"appearance"}),h(Ao,{class:"social-links"}),u(o.$slots,"nav-screen-content-after",{},void 0,!0)])],512)):m("",!0)]),_:3}))}});const zo=p(Eo,[["__scopeId","data-v-724636ae"]]),Do={class:"VPNav"},Fo=g({__name:"VPNav",setup(e){const{isScreenOpen:t,closeScreen:n,toggleScreen:s}=Nt();return oe("close-screen",n),(o,l)=>(a(),i("header",Do,[h(Js,{"is-screen-open":c(t),onToggleScreen:c(s)},{"nav-bar-title-before":v(()=>[u(o.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[u(o.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":v(()=>[u(o.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":v(()=>[u(o.$slots,"nav-bar-content-after",{},void 0,!0)]),_:3},8,["is-screen-open","onToggleScreen"]),h(zo,{open:c(t)},{"nav-screen-content-before":v(()=>[u(o.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":v(()=>[u(o.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3},8,["open"])]))}});const Oo=p(Fo,[["__scopeId","data-v-7e5bc4a5"]]),Go={},Ro={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Uo=r("path",{d:"M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"},null,-1),jo=r("path",{d:"M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"},null,-1),qo=r("path",{d:"M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"},null,-1),Ko=r("path",{d:"M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"},null,-1),Wo=[Uo,jo,qo,Ko];function Yo(e,t){return a(),i("svg",Ro,Wo)}const Xo=p(Go,[["render",Yo]]);function Qo(){const{hasSidebar:e}=z(),t=de("(min-width: 960px)"),n=de("(min-width: 1280px)");return{isAsideEnabled:k(()=>!n.value&&!t.value?!1:e.value?n.value:t.value)}}const Jo=71;function Se(e){return typeof e.outline=="object"&&!Array.isArray(e.outline)&&e.outline.label||e.outlineTitle||"On this page"}function Le(e){const t=[...document.querySelectorAll(".VPDoc h2,h3,h4,h5,h6")].filter(n=>n.id&&n.hasChildNodes()).map(n=>{const s=Number(n.tagName[1]);return{title:Zo(n),link:"#"+n.id,level:s}});return ea(t,e)}function Zo(e){let t="";for(const n of e.childNodes)if(n.nodeType===1){if(n.classList.contains("VPBadge")||n.classList.contains("header-anchor"))continue;t+=n.textContent}else n.nodeType===3&&(t+=n.textContent);return t.trim()}function ea(e,t){if(t===!1)return[];const n=(typeof t=="object"&&!Array.isArray(t)?t.level:t)||2,[s,o]=typeof n=="number"?[n,n]:n==="deep"?[2,6]:n;e=e.filter(d=>d.level>=s&&d.level<=o);const l=[];e:for(let d=0;d=0;_--){const b=e[_];if(b.level{requestAnimationFrame(l),window.addEventListener("scroll",s)}),lt(()=>{d(location.hash)}),fe(()=>{window.removeEventListener("scroll",s)});function l(){if(!n.value)return;const f=[].slice.call(e.value.querySelectorAll(".outline-link")),_=[].slice.call(document.querySelectorAll(".content .header-anchor")).filter($=>f.some(B=>B.hash===$.hash&&$.offsetParent!==null)),b=window.scrollY,L=window.innerHeight,C=document.body.offsetHeight,w=Math.abs(b+L-C)<1;if(_.length&&w){d(_[_.length-1].hash);return}for(let $=0;$<_.length;$++){const B=_[$],T=_[$+1],[A,D]=na($,B,T);if(A){d(D);return}}}function d(f){o&&o.classList.remove("active"),f!==null&&(o=e.value.querySelector(`a[href="${decodeURIComponent(f)}"]`));const _=o;_?(_.classList.add("active"),t.value.style.top=_.offsetTop+33+"px",t.value.style.opacity="1"):(t.value.style.top="33px",t.value.style.opacity="0")}}function Te(e){return e.parentElement.offsetTop-Jo}function na(e,t,n){const s=window.scrollY;return e===0&&s===0?[!0,null]:s{const o=R("VPDocOutlineItem",!0);return a(),i("ul",{class:M(e.root?"root":"nested")},[(a(!0),i(S,null,N(e.headers,({children:l,link:d,title:f})=>(a(),i("li",null,[r("a",{class:"outline-link",href:d,onClick:t,title:f},P(f),9,sa),l!=null&&l.length?(a(),y(o,{key:0,headers:l},null,8,["headers"])):m("",!0)]))),256))],2)}}});const Me=p(oa,[["__scopeId","data-v-9a431c33"]]),aa={},ca={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},la=r("path",{d:"M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"},null,-1),ia=[la];function ra(e,t){return a(),i("svg",ca,ia)}const Ce=p(aa,[["render",ra]]),ua=g({__name:"VPLocalNavOutlineDropdown",setup(e){const{frontmatter:t,theme:n}=V(),s=x(!1),o=x(0),l=x();J(()=>{s.value=!1});function d(){s.value=!s.value,o.value=window.innerHeight+Math.min(window.scrollY-64,0)}function f(L){L.target.classList.contains("outline-link")&&(l.value&&(l.value.style.transition="none"),it(()=>{s.value=!1}))}function _(){s.value=!1,window.scrollTo({top:0,left:0,behavior:"smooth"})}const b=ge([]);return J(()=>{b.value=Le(t.value.outline??n.value.outline)}),(L,C)=>(a(),i("div",{class:"VPLocalNavOutlineDropdown",style:He({"--vp-vh":o.value+"px"})},[c(b).length>0?(a(),i("button",{key:0,onClick:d,class:M({open:s.value})},[I(P(c(Se)(c(n)))+" ",1),h(Ce,{class:"icon"})],2)):(a(),i("button",{key:1,onClick:_},P(c(n).returnToTopLabel||"Return to top"),1)),h(pe,{name:"flyout"},{default:v(()=>[s.value?(a(),i("div",{key:0,ref_key:"items",ref:l,class:"items",onClick:f},[r("a",{class:"top-link",href:"#",onClick:_},P(c(n).returnToTopLabel||"Return to top"),1),h(Me,{headers:c(b)},null,8,["headers"])],512)):m("",!0)]),_:1})],4))}});const da=p(ua,[["__scopeId","data-v-079b16a8"]]),_a={key:0,class:"VPLocalNav"},va=["aria-expanded"],ha={class:"menu-text"},fa=g({__name:"VPLocalNav",props:{open:{type:Boolean}},emits:["open-menu"],setup(e){const{theme:t}=V(),{hasSidebar:n}=z();return(s,o)=>c(n)?(a(),i("div",_a,[r("button",{class:"menu","aria-expanded":e.open,"aria-controls":"VPSidebarNav",onClick:o[0]||(o[0]=l=>s.$emit("open-menu"))},[h(Xo,{class:"menu-icon"}),r("span",ha,P(c(t).sidebarMenuLabel||"Menu"),1)],8,va),h(da)])):m("",!0)}});const pa=p(fa,[["__scopeId","data-v-f5a2ca58"]]),ma=e=>(H("data-v-c4656e6d"),e=e(),E(),e),ga=["role","tabindex"],ya=ma(()=>r("div",{class:"indicator"},null,-1)),ba=["onKeydown"],ka={key:1,class:"items"},$a=g({__name:"VPSidebarItem",props:{item:null,depth:null},setup(e){const t=e,{collapsed:n,collapsible:s,isLink:o,isActiveLink:l,hasActiveLink:d,hasChildren:f,toggle:_}=St(k(()=>t.item)),b=k(()=>f.value?"section":"div"),L=k(()=>o.value?"a":"div"),C=k(()=>f.value?t.depth+2===7?"p":`h${t.depth+2}`:"p"),w=k(()=>o.value?void 0:"button"),$=k(()=>[[`level-${t.depth}`],{collapsible:s.value},{collapsed:n.value},{"is-link":o.value},{"is-active":l.value},{"has-active":d.value}]);function B(A){"key"in A&&A.key!=="Enter"||!t.item.link&&_()}function T(){t.item.link&&_()}return(A,D)=>{const ne=R("VPSidebarItem",!0);return a(),y(U(c(b)),{class:M(["VPSidebarItem",c($)])},{default:v(()=>[e.item.text?(a(),i("div",se({key:0,class:"item",role:c(w)},rt(e.item.items?{click:B,keydown:B}:{},!0),{tabindex:e.item.items&&0}),[ya,e.item.link?(a(),y(O,{key:0,tag:c(L),class:"link",href:e.item.link},{default:v(()=>[(a(),y(U(c(C)),{class:"text",innerHTML:e.item.text},null,8,["innerHTML"]))]),_:1},8,["tag","href"])):(a(),y(U(c(C)),{key:1,class:"text",innerHTML:e.item.text},null,8,["innerHTML"])),e.item.collapsed!=null?(a(),i("div",{key:2,class:"caret",role:"button","aria-label":"toggle section",onClick:T,onKeydown:ut(T,["enter"]),tabindex:"0"},[h(Ce,{class:"caret-icon"})],40,ba)):m("",!0)],16,ga)):m("",!0),e.item.items&&e.item.items.length?(a(),i("div",ka,[e.depth<5?(a(!0),i(S,{key:0},N(e.item.items,W=>(a(),y(ne,{key:W.text,item:W,depth:e.depth+1},null,8,["item","depth"]))),128)):m("",!0)])):m("",!0)]),_:1},8,["class"])}}});const Pa=p($a,[["__scopeId","data-v-c4656e6d"]]),Xe=e=>(H("data-v-af16598e"),e=e(),E(),e),Va=Xe(()=>r("div",{class:"curtain"},null,-1)),wa={class:"nav",id:"VPSidebarNav","aria-labelledby":"sidebar-aria-label",tabindex:"-1"},xa=Xe(()=>r("span",{class:"visually-hidden",id:"sidebar-aria-label"}," Sidebar Navigation ",-1)),Sa=g({__name:"VPSidebar",props:{open:{type:Boolean}},setup(e){const t=e,{sidebarGroups:n,hasSidebar:s}=z();let o=x(null);function l(){Ke(o.value,{reserveScrollBarGap:!0})}function d(){We()}return dt(async()=>{var f;t.open?(l(),(f=o.value)==null||f.focus()):d()}),(f,_)=>c(s)?(a(),i("aside",{key:0,class:M(["VPSidebar",{open:e.open}]),ref_key:"navEl",ref:o,onClick:_[0]||(_[0]=_t(()=>{},["stop"]))},[Va,r("nav",wa,[xa,u(f.$slots,"sidebar-nav-before",{},void 0,!0),(a(!0),i(S,null,N(c(n),b=>(a(),i("div",{key:b.text,class:"group"},[h(Pa,{item:b,depth:0},null,8,["item"])]))),128)),u(f.$slots,"sidebar-nav-after",{},void 0,!0)])],2)):m("",!0)}});const La=p(Sa,[["__scopeId","data-v-af16598e"]]),Ma={},Ca={class:"VPPage"};function Ba(e,t){const n=R("Content");return a(),i("div",Ca,[u(e.$slots,"page-top"),h(n),u(e.$slots,"page-bottom")])}const Ia=p(Ma,[["render",Ba]]),Na=g({__name:"VPButton",props:{tag:null,size:null,theme:null,text:null,href:null},setup(e){const t=e,n=k(()=>[t.size??"medium",t.theme??"brand"]),s=k(()=>t.href&&Ee.test(t.href)),o=k(()=>t.tag?t.tag:t.href?"a":"button");return(l,d)=>(a(),y(U(c(o)),{class:M(["VPButton",c(n)]),href:e.href?c(Z)(e.href):void 0,target:c(s)?"_blank":void 0,rel:c(s)?"noreferrer":void 0},{default:v(()=>[I(P(e.text),1)]),_:1},8,["class","href","target","rel"]))}});const Ta=p(Na,[["__scopeId","data-v-567ba664"]]),Aa=e=>(H("data-v-fd2650d5"),e=e(),E(),e),Ha={class:"container"},Ea={class:"main"},za={key:0,class:"name"},Da={class:"clip"},Fa={key:1,class:"text"},Oa={key:2,class:"tagline"},Ga={key:0,class:"actions"},Ra={key:0,class:"image"},Ua={class:"image-container"},ja=Aa(()=>r("div",{class:"image-bg"},null,-1)),qa=g({__name:"VPHero",props:{name:null,text:null,tagline:null,image:null,actions:null},setup(e){const t=me("hero-image-slot-exists");return(n,s)=>(a(),i("div",{class:M(["VPHero",{"has-image":e.image||c(t)}])},[r("div",Ha,[r("div",Ea,[u(n.$slots,"home-hero-info",{},()=>[e.name?(a(),i("h1",za,[r("span",Da,P(e.name),1)])):m("",!0),e.text?(a(),i("p",Fa,P(e.text),1)):m("",!0),e.tagline?(a(),i("p",Oa,P(e.tagline),1)):m("",!0)],!0),e.actions?(a(),i("div",Ga,[(a(!0),i(S,null,N(e.actions,o=>(a(),i("div",{key:o.link,class:"action"},[h(Ta,{tag:"a",size:"medium",theme:o.theme,text:o.text,href:o.link},null,8,["theme","text","href"])]))),128))])):m("",!0)]),e.image||c(t)?(a(),i("div",Ra,[r("div",Ua,[ja,u(n.$slots,"home-hero-image",{},()=>[e.image?(a(),y(be,{key:0,class:"image-src",image:e.image},null,8,["image"])):m("",!0)],!0)])])):m("",!0)])],2))}});const Ka=p(qa,[["__scopeId","data-v-fd2650d5"]]),Wa=g({__name:"VPHomeHero",setup(e){const{frontmatter:t}=V();return(n,s)=>c(t).hero?(a(),y(Ka,{key:0,class:"VPHomeHero",name:c(t).hero.name,text:c(t).hero.text,tagline:c(t).hero.tagline,image:c(t).hero.image,actions:c(t).hero.actions},{"home-hero-info":v(()=>[u(n.$slots,"home-hero-info")]),"home-hero-image":v(()=>[u(n.$slots,"home-hero-image")]),_:3},8,["name","text","tagline","image","actions"])):m("",!0)}}),Ya={},Xa={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Qa=r("path",{d:"M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"},null,-1),Ja=[Qa];function Za(e,t){return a(),i("svg",Xa,Ja)}const ec=p(Ya,[["render",Za]]),tc={class:"box"},nc=["innerHTML"],sc=["innerHTML"],oc=["innerHTML"],ac={key:3,class:"link-text"},cc={class:"link-text-value"},lc=g({__name:"VPFeature",props:{icon:null,title:null,details:null,link:null,linkText:null},setup(e){return(t,n)=>(a(),y(O,{class:"VPFeature",href:e.link,"no-icon":!0},{default:v(()=>[r("article",tc,[typeof e.icon=="object"?(a(),y(be,{key:0,image:e.icon,alt:e.icon.alt,height:e.icon.height,width:e.icon.width},null,8,["image","alt","height","width"])):e.icon?(a(),i("div",{key:1,class:"icon",innerHTML:e.icon},null,8,nc)):m("",!0),r("h2",{class:"title",innerHTML:e.title},null,8,sc),e.details?(a(),i("p",{key:2,class:"details",innerHTML:e.details},null,8,oc)):m("",!0),e.linkText?(a(),i("div",ac,[r("p",cc,[I(P(e.linkText)+" ",1),h(ec,{class:"link-text-icon"})])])):m("",!0)])]),_:1},8,["href"]))}});const ic=p(lc,[["__scopeId","data-v-837f6cca"]]),rc={key:0,class:"VPFeatures"},uc={class:"container"},dc={class:"items"},_c=g({__name:"VPFeatures",props:{features:null},setup(e){const t=e,n=k(()=>{const s=t.features.length;if(s){if(s===2)return"grid-2";if(s===3)return"grid-3";if(s%3===0)return"grid-6";if(s%2===0)return"grid-4"}else return});return(s,o)=>e.features?(a(),i("div",rc,[r("div",uc,[r("div",dc,[(a(!0),i(S,null,N(e.features,l=>(a(),i("div",{key:l.title,class:M(["item",[c(n)]])},[h(ic,{icon:l.icon,title:l.title,details:l.details,link:l.link,"link-text":l.linkText},null,8,["icon","title","details","link","link-text"])],2))),128))])])])):m("",!0)}});const vc=p(_c,[["__scopeId","data-v-6816157f"]]),hc=g({__name:"VPHomeFeatures",setup(e){const{frontmatter:t}=V();return(n,s)=>c(t).features?(a(),y(vc,{key:0,class:"VPHomeFeatures",features:c(t).features},null,8,["features"])):m("",!0)}}),fc={class:"VPHome"},pc=g({__name:"VPHome",setup(e){return(t,n)=>{const s=R("Content");return a(),i("div",fc,[u(t.$slots,"home-hero-before",{},void 0,!0),h(Wa,null,{"home-hero-info":v(()=>[u(t.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[u(t.$slots,"home-hero-image",{},void 0,!0)]),_:3}),u(t.$slots,"home-hero-after",{},void 0,!0),u(t.$slots,"home-features-before",{},void 0,!0),h(hc),u(t.$slots,"home-features-after",{},void 0,!0),h(s)])}}});const mc=p(pc,[["__scopeId","data-v-d82743a8"]]),gc=e=>(H("data-v-ff0f39c8"),e=e(),E(),e),yc={class:"content"},bc={class:"outline-title"},kc={"aria-labelledby":"doc-outline-aria-label"},$c=gc(()=>r("span",{class:"visually-hidden",id:"doc-outline-aria-label"}," Table of Contents for current page ",-1)),Pc=g({__name:"VPDocAsideOutline",setup(e){const{frontmatter:t,theme:n}=V(),s=ge([]);J(()=>{s.value=Le(t.value.outline??n.value.outline)});const o=x(),l=x();return ta(o,l),(d,f)=>(a(),i("div",{class:M(["VPDocAsideOutline",{"has-outline":c(s).length>0}]),ref_key:"container",ref:o},[r("div",yc,[r("div",{class:"outline-marker",ref_key:"marker",ref:l},null,512),r("div",bc,P(c(Se)(c(n))),1),r("nav",kc,[$c,h(Me,{headers:c(s),root:!0},null,8,["headers"])])])],2))}});const Vc=p(Pc,[["__scopeId","data-v-ff0f39c8"]]),wc={class:"VPDocAsideCarbonAds"},xc=g({__name:"VPDocAsideCarbonAds",props:{carbonAds:null},setup(e){const t=()=>null;return(n,s)=>(a(),i("div",wc,[h(c(t),{"carbon-ads":e.carbonAds},null,8,["carbon-ads"])]))}}),Sc=e=>(H("data-v-3f215769"),e=e(),E(),e),Lc={class:"VPDocAside"},Mc=Sc(()=>r("div",{class:"spacer"},null,-1)),Cc=g({__name:"VPDocAside",setup(e){const{theme:t}=V();return(n,s)=>(a(),i("div",Lc,[u(n.$slots,"aside-top",{},void 0,!0),u(n.$slots,"aside-outline-before",{},void 0,!0),h(Vc),u(n.$slots,"aside-outline-after",{},void 0,!0),Mc,u(n.$slots,"aside-ads-before",{},void 0,!0),c(t).carbonAds?(a(),y(xc,{key:0,"carbon-ads":c(t).carbonAds},null,8,["carbon-ads"])):m("",!0),u(n.$slots,"aside-ads-after",{},void 0,!0),u(n.$slots,"aside-bottom",{},void 0,!0)]))}});const Bc=p(Cc,[["__scopeId","data-v-3f215769"]]);function Ic(){const{theme:e,page:t}=V();return k(()=>{const{text:n="Edit this page",pattern:s=""}=e.value.editLink||{},{relativePath:o}=t.value;let l;return typeof s=="function"?l=s({relativePath:o}):l=s.replace(/:path/g,o),{url:l,text:n}})}function Nc(){const{page:e,theme:t,frontmatter:n}=V();return k(()=>{var d,f,_,b;const s=Oe(t.value.sidebar,e.value.relativePath),o=wt(s),l=o.findIndex(L=>K(e.value.relativePath,L.link));return{prev:n.value.prev===!1?void 0:{text:(typeof n.value.prev=="string"?n.value.prev:typeof n.value.prev=="object"?n.value.prev.text:void 0)??((d=o[l-1])==null?void 0:d.text),link:(typeof n.value.prev=="object"?n.value.prev.link:void 0)??((f=o[l-1])==null?void 0:f.link)},next:n.value.next===!1?void 0:{text:(typeof n.value.next=="string"?n.value.next:typeof n.value.next=="object"?n.value.next.text:void 0)??((_=o[l+1])==null?void 0:_.text),link:(typeof n.value.next=="object"?n.value.next.link:void 0)??((b=o[l+1])==null?void 0:b.link)}}})}const Tc={},Ac={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Hc=r("path",{d:"M18,23H4c-1.7,0-3-1.3-3-3V6c0-1.7,1.3-3,3-3h7c0.6,0,1,0.4,1,1s-0.4,1-1,1H4C3.4,5,3,5.4,3,6v14c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-7c0-0.6,0.4-1,1-1s1,0.4,1,1v7C21,21.7,19.7,23,18,23z"},null,-1),Ec=r("path",{d:"M8,17c-0.3,0-0.5-0.1-0.7-0.3C7,16.5,6.9,16.1,7,15.8l1-4c0-0.2,0.1-0.3,0.3-0.5l9.5-9.5c1.2-1.2,3.2-1.2,4.4,0c1.2,1.2,1.2,3.2,0,4.4l-9.5,9.5c-0.1,0.1-0.3,0.2-0.5,0.3l-4,1C8.2,17,8.1,17,8,17zM9.9,12.5l-0.5,2.1l2.1-0.5l9.3-9.3c0.4-0.4,0.4-1.1,0-1.6c-0.4-0.4-1.2-0.4-1.6,0l0,0L9.9,12.5z M18.5,2.5L18.5,2.5L18.5,2.5z"},null,-1),zc=[Hc,Ec];function Dc(e,t){return a(),i("svg",Ac,zc)}const Fc=p(Tc,[["render",Dc]]),Oc={class:"VPLastUpdated"},Gc=["datetime"],Rc=g({__name:"VPDocFooterLastUpdated",setup(e){const{theme:t,page:n,lang:s}=V(),o=k(()=>new Date(n.value.lastUpdated)),l=k(()=>o.value.toISOString()),d=x("");return F(()=>{Q(()=>{d.value=o.value.toLocaleString(s.value)})}),(f,_)=>(a(),i("p",Oc,[I(P(c(t).lastUpdatedText||"Last updated")+": ",1),r("time",{datetime:c(l)},P(d.value),9,Gc)]))}});const Uc=p(Rc,[["__scopeId","data-v-7b3ebfe1"]]),jc={key:0,class:"VPDocFooter"},qc={key:0,class:"edit-info"},Kc={key:0,class:"edit-link"},Wc={key:1,class:"last-updated"},Yc={key:1,class:"prev-next"},Xc={class:"pager"},Qc=["href"],Jc=["innerHTML"],Zc=["innerHTML"],el=["href"],tl=["innerHTML"],nl=["innerHTML"],sl=g({__name:"VPDocFooter",setup(e){const{theme:t,page:n,frontmatter:s}=V(),o=Ic(),l=Nc(),d=k(()=>t.value.editLink&&s.value.editLink!==!1),f=k(()=>n.value.lastUpdated&&s.value.lastUpdated!==!1),_=k(()=>d.value||f.value||l.value.prev||l.value.next);return(b,L)=>{var C,w,$,B,T,A,D;return c(_)?(a(),i("footer",jc,[u(b.$slots,"doc-footer-before",{},void 0,!0),c(d)||c(f)?(a(),i("div",qc,[c(d)?(a(),i("div",Kc,[h(O,{class:"edit-link-button",href:c(o).url,"no-icon":!0},{default:v(()=>[h(Fc,{class:"edit-link-icon","aria-label":"edit icon"}),I(" "+P(c(o).text),1)]),_:1},8,["href"])])):m("",!0),c(f)?(a(),i("div",Wc,[h(Uc)])):m("",!0)])):m("",!0),(C=c(l).prev)!=null&&C.link||(w=c(l).next)!=null&&w.link?(a(),i("div",Yc,[r("div",Xc,[($=c(l).prev)!=null&&$.link?(a(),i("a",{key:0,class:"pager-link prev",href:c(Z)(c(l).prev.link)},[r("span",{class:"desc",innerHTML:((B=c(t).docFooter)==null?void 0:B.prev)||"Previous page"},null,8,Jc),r("span",{class:"title",innerHTML:c(l).prev.text},null,8,Zc)],8,Qc)):m("",!0)]),r("div",{class:M(["pager",{"has-prev":(T=c(l).prev)==null?void 0:T.link}])},[(A=c(l).next)!=null&&A.link?(a(),i("a",{key:0,class:"pager-link next",href:c(Z)(c(l).next.link)},[r("span",{class:"desc",innerHTML:((D=c(t).docFooter)==null?void 0:D.next)||"Next page"},null,8,tl),r("span",{class:"title",innerHTML:c(l).next.text},null,8,nl)],8,el)):m("",!0)],2)])):m("",!0)])):m("",!0)}}});const ol=p(sl,[["__scopeId","data-v-face870a"]]),al={key:0,class:"VPDocOutlineDropdown"},cl={key:0,class:"items"},ll=g({__name:"VPDocOutlineDropdown",setup(e){const{frontmatter:t,theme:n}=V(),s=x(!1);J(()=>{s.value=!1});const o=ge([]);return J(()=>{o.value=Le(t.value.outline??n.value.outline)}),(l,d)=>c(o).length>0?(a(),i("div",al,[r("button",{onClick:d[0]||(d[0]=f=>s.value=!s.value),class:M({open:s.value})},[I(P(c(Se)(c(n)))+" ",1),h(Ce,{class:"icon"})],2),s.value?(a(),i("div",cl,[h(Me,{headers:c(o)},null,8,["headers"])])):m("",!0)])):m("",!0)}});const il=p(ll,[["__scopeId","data-v-2edece88"]]),rl=e=>(H("data-v-c4b0d3cf"),e=e(),E(),e),ul={class:"container"},dl=rl(()=>r("div",{class:"aside-curtain"},null,-1)),_l={class:"aside-container"},vl={class:"aside-content"},hl={class:"content"},fl={class:"content-container"},pl={class:"main"},ml=g({__name:"VPDoc",setup(e){const t=ee(),{hasSidebar:n,hasAside:s,leftAside:o}=z(),l=k(()=>t.path.replace(/[./]+/g,"_").replace(/_html$/,""));return(d,f)=>{const _=R("Content");return a(),i("div",{class:M(["VPDoc",{"has-sidebar":c(n),"has-aside":c(s)}])},[u(d.$slots,"doc-top",{},void 0,!0),r("div",ul,[c(s)?(a(),i("div",{key:0,class:M(["aside",{"left-aside":c(o)}])},[dl,r("div",_l,[r("div",vl,[h(Bc,null,{"aside-top":v(()=>[u(d.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":v(()=>[u(d.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":v(()=>[u(d.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[u(d.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[u(d.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[u(d.$slots,"aside-ads-after",{},void 0,!0)]),_:3})])])],2)):m("",!0),r("div",hl,[r("div",fl,[u(d.$slots,"doc-before",{},void 0,!0),h(il),r("main",pl,[h(_,{class:M(["vp-doc",c(l)])},null,8,["class"])]),h(ol,null,{"doc-footer-before":v(()=>[u(d.$slots,"doc-footer-before",{},void 0,!0)]),_:3}),u(d.$slots,"doc-after",{},void 0,!0)])])]),u(d.$slots,"doc-bottom",{},void 0,!0)],2)}}});const gl=p(ml,[["__scopeId","data-v-c4b0d3cf"]]),re=e=>(H("data-v-c70503b8"),e=e(),E(),e),yl={class:"NotFound"},bl=re(()=>r("p",{class:"code"},"404",-1)),kl=re(()=>r("h1",{class:"title"},"PAGE NOT FOUND",-1)),$l=re(()=>r("div",{class:"divider"},null,-1)),Pl=re(()=>r("blockquote",{class:"quote"}," But if you don't change your direction, and if you keep looking, you may end up where you are heading. ",-1)),Vl={class:"action"},wl=["href"],xl=g({__name:"NotFound",setup(e){const{site:t}=V(),{localeLinks:n}=te({removeCurrent:!1}),s=x("/");return F(()=>{var l;const o=window.location.pathname.replace(t.value.base,"").replace(/(^.*?\/).*$/,"/$1");n.value.length&&(s.value=((l=n.value.find(({link:d})=>d.startsWith(o)))==null?void 0:l.link)||n.value[0].link)}),(o,l)=>(a(),i("div",yl,[bl,kl,$l,Pl,r("div",Vl,[r("a",{class:"link",href:c(he)(s.value),"aria-label":"go to home"}," Take me home ",8,wl)])]))}});const Sl=p(xl,[["__scopeId","data-v-c70503b8"]]),Ll=g({__name:"VPContent",setup(e){const{page:t,frontmatter:n}=V(),{hasSidebar:s}=z();return(o,l)=>(a(),i("div",{class:M(["VPContent",{"has-sidebar":c(s),"is-home":c(n).layout==="home"}]),id:"VPContent"},[c(t).isNotFound?u(o.$slots,"not-found",{key:0},()=>[h(Sl)],!0):c(n).layout==="page"?(a(),y(Ia,{key:1},{"page-top":v(()=>[u(o.$slots,"page-top",{},void 0,!0)]),"page-bottom":v(()=>[u(o.$slots,"page-bottom",{},void 0,!0)]),_:3})):c(n).layout==="home"?(a(),y(mc,{key:2},{"home-hero-before":v(()=>[u(o.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":v(()=>[u(o.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[u(o.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":v(()=>[u(o.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":v(()=>[u(o.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":v(()=>[u(o.$slots,"home-features-after",{},void 0,!0)]),_:3})):(a(),y(gl,{key:3},{"doc-top":v(()=>[u(o.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":v(()=>[u(o.$slots,"doc-bottom",{},void 0,!0)]),"doc-footer-before":v(()=>[u(o.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":v(()=>[u(o.$slots,"doc-before",{},void 0,!0)]),"doc-after":v(()=>[u(o.$slots,"doc-after",{},void 0,!0)]),"aside-top":v(()=>[u(o.$slots,"aside-top",{},void 0,!0)]),"aside-outline-before":v(()=>[u(o.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[u(o.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[u(o.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[u(o.$slots,"aside-ads-after",{},void 0,!0)]),"aside-bottom":v(()=>[u(o.$slots,"aside-bottom",{},void 0,!0)]),_:3}))],2))}});const Ml=p(Ll,[["__scopeId","data-v-a494bd1d"]]),Cl={class:"container"},Bl=["innerHTML"],Il=["innerHTML"],Nl=g({__name:"VPFooter",setup(e){const{theme:t}=V(),{hasSidebar:n}=z();return(s,o)=>c(t).footer?(a(),i("footer",{key:0,class:M(["VPFooter",{"has-sidebar":c(n)}])},[r("div",Cl,[c(t).footer.message?(a(),i("p",{key:0,class:"message",innerHTML:c(t).footer.message},null,8,Bl)):m("",!0),c(t).footer.copyright?(a(),i("p",{key:1,class:"copyright",innerHTML:c(t).footer.copyright},null,8,Il)):m("",!0)])],2)):m("",!0)}});const Tl=p(Nl,[["__scopeId","data-v-2f86ebd2"]]),Al={key:0,class:"Layout"},Hl=g({__name:"Layout",setup(e){const{isOpen:t,open:n,close:s}=z(),o=ee();q(()=>o.path,s),xt(t,s),oe("close-sidebar",s),oe("is-sidebar-open",t);const{frontmatter:l}=V(),d=vt(),f=k(()=>!!d["home-hero-image"]);return oe("hero-image-slot-exists",f),(_,b)=>{const L=R("Content");return c(l).layout!==!1?(a(),i("div",Al,[u(_.$slots,"layout-top",{},void 0,!0),h(Mt),h(It,{class:"backdrop",show:c(t),onClick:c(s)},null,8,["show","onClick"]),h(Oo,null,{"nav-bar-title-before":v(()=>[u(_.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[u(_.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":v(()=>[u(_.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":v(()=>[u(_.$slots,"nav-bar-content-after",{},void 0,!0)]),"nav-screen-content-before":v(()=>[u(_.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":v(()=>[u(_.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3}),h(pa,{open:c(t),onOpenMenu:c(n)},null,8,["open","onOpenMenu"]),h(La,{open:c(t)},{"sidebar-nav-before":v(()=>[u(_.$slots,"sidebar-nav-before",{},void 0,!0)]),"sidebar-nav-after":v(()=>[u(_.$slots,"sidebar-nav-after",{},void 0,!0)]),_:3},8,["open"]),h(Ml,null,{"page-top":v(()=>[u(_.$slots,"page-top",{},void 0,!0)]),"page-bottom":v(()=>[u(_.$slots,"page-bottom",{},void 0,!0)]),"not-found":v(()=>[u(_.$slots,"not-found",{},void 0,!0)]),"home-hero-before":v(()=>[u(_.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":v(()=>[u(_.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[u(_.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":v(()=>[u(_.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":v(()=>[u(_.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":v(()=>[u(_.$slots,"home-features-after",{},void 0,!0)]),"doc-footer-before":v(()=>[u(_.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":v(()=>[u(_.$slots,"doc-before",{},void 0,!0)]),"doc-after":v(()=>[u(_.$slots,"doc-after",{},void 0,!0)]),"doc-top":v(()=>[u(_.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":v(()=>[u(_.$slots,"doc-bottom",{},void 0,!0)]),"aside-top":v(()=>[u(_.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":v(()=>[u(_.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":v(()=>[u(_.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[u(_.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[u(_.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[u(_.$slots,"aside-ads-after",{},void 0,!0)]),_:3}),h(Tl),u(_.$slots,"layout-bottom",{},void 0,!0)])):(a(),y(L,{key:1}))}}});const El=p(Hl,[["__scopeId","data-v-b2cf3e0b"]]);const Dl={Layout:El,enhanceApp:({app:e})=>{e.component("Badge",ft)}};export{Dl as t}; diff --git a/assets/index.md.a7ecfcb0.js b/assets/index.md.a7ecfcb0.js new file mode 100644 index 0000000..42091bd --- /dev/null +++ b/assets/index.md.a7ecfcb0.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as n}from"./chunks/framework.41379913.js";const _=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"编程导引","text":"Introduction to Programming","tagline":"从零开始你的计算机编程之旅","actions":[{"text":"开始阅读 →","link":"/intro","theme":"brand"}]}},"headers":[],"relativePath":"index.md"}'),o={name:"index.md"};function a(r,i,c,s,d,m){return n(),t("div")}const l=e(o,[["render",a]]);export{_ as __pageData,l as default}; diff --git a/assets/index.md.a7ecfcb0.lean.js b/assets/index.md.a7ecfcb0.lean.js new file mode 100644 index 0000000..42091bd --- /dev/null +++ b/assets/index.md.a7ecfcb0.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as n}from"./chunks/framework.41379913.js";const _=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"编程导引","text":"Introduction to Programming","tagline":"从零开始你的计算机编程之旅","actions":[{"text":"开始阅读 →","link":"/intro","theme":"brand"}]}},"headers":[],"relativePath":"index.md"}'),o={name:"index.md"};function a(r,i,c,s,d,m){return n(),t("div")}const l=e(o,[["render",a]]);export{_ as __pageData,l as default}; diff --git a/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2 b/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2 new file mode 100644 index 0000000..2a68729 Binary files /dev/null and b/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2 differ diff --git a/assets/inter-italic-cyrillic.ea42a392.woff2 b/assets/inter-italic-cyrillic.ea42a392.woff2 new file mode 100644 index 0000000..f640351 Binary files /dev/null and b/assets/inter-italic-cyrillic.ea42a392.woff2 differ diff --git a/assets/inter-italic-greek-ext.4fbe9427.woff2 b/assets/inter-italic-greek-ext.4fbe9427.woff2 new file mode 100644 index 0000000..0021896 Binary files /dev/null and b/assets/inter-italic-greek-ext.4fbe9427.woff2 differ diff --git a/assets/inter-italic-greek.8f4463c4.woff2 b/assets/inter-italic-greek.8f4463c4.woff2 new file mode 100644 index 0000000..71c265f Binary files /dev/null and b/assets/inter-italic-greek.8f4463c4.woff2 differ diff --git a/assets/inter-italic-latin-ext.bd8920cc.woff2 b/assets/inter-italic-latin-ext.bd8920cc.woff2 new file mode 100644 index 0000000..9c1b944 Binary files /dev/null and b/assets/inter-italic-latin-ext.bd8920cc.woff2 differ diff --git a/assets/inter-italic-latin.bd3b6f56.woff2 b/assets/inter-italic-latin.bd3b6f56.woff2 new file mode 100644 index 0000000..01fcf20 Binary files /dev/null and b/assets/inter-italic-latin.bd3b6f56.woff2 differ diff --git a/assets/inter-italic-vietnamese.6ce511fb.woff2 b/assets/inter-italic-vietnamese.6ce511fb.woff2 new file mode 100644 index 0000000..e4f788e Binary files /dev/null and b/assets/inter-italic-vietnamese.6ce511fb.woff2 differ diff --git a/assets/inter-roman-cyrillic-ext.e75737ce.woff2 b/assets/inter-roman-cyrillic-ext.e75737ce.woff2 new file mode 100644 index 0000000..28593cc Binary files /dev/null and b/assets/inter-roman-cyrillic-ext.e75737ce.woff2 differ diff --git a/assets/inter-roman-cyrillic.5f2c6c8c.woff2 b/assets/inter-roman-cyrillic.5f2c6c8c.woff2 new file mode 100644 index 0000000..a20adc1 Binary files /dev/null and b/assets/inter-roman-cyrillic.5f2c6c8c.woff2 differ diff --git a/assets/inter-roman-greek-ext.ab0619bc.woff2 b/assets/inter-roman-greek-ext.ab0619bc.woff2 new file mode 100644 index 0000000..e3b0be7 Binary files /dev/null and b/assets/inter-roman-greek-ext.ab0619bc.woff2 differ diff --git a/assets/inter-roman-greek.d5a6d92a.woff2 b/assets/inter-roman-greek.d5a6d92a.woff2 new file mode 100644 index 0000000..f790e04 Binary files /dev/null and b/assets/inter-roman-greek.d5a6d92a.woff2 differ diff --git a/assets/inter-roman-latin-ext.0030eebd.woff2 b/assets/inter-roman-latin-ext.0030eebd.woff2 new file mode 100644 index 0000000..715bd90 Binary files /dev/null and b/assets/inter-roman-latin-ext.0030eebd.woff2 differ diff --git a/assets/inter-roman-latin.2ed14f66.woff2 b/assets/inter-roman-latin.2ed14f66.woff2 new file mode 100644 index 0000000..a540b7a Binary files /dev/null and b/assets/inter-roman-latin.2ed14f66.woff2 differ diff --git a/assets/inter-roman-vietnamese.14ce25a6.woff2 b/assets/inter-roman-vietnamese.14ce25a6.woff2 new file mode 100644 index 0000000..5a9f9cb Binary files /dev/null and b/assets/inter-roman-vietnamese.14ce25a6.woff2 differ diff --git a/assets/intro.md.ca4ae51e.js b/assets/intro.md.ca4ae51e.js new file mode 100644 index 0000000..79f3cb2 --- /dev/null +++ b/assets/intro.md.ca4ae51e.js @@ -0,0 +1 @@ +import{_ as e,c as a,o as r,O as t}from"./chunks/framework.41379913.js";const l="/assets/wechat-mp-qrcode.f64c0e73.jpg",i="/assets/wechat-pay-qrcode.87383472.jpg",_=JSON.parse('{"title":"引言","description":"","frontmatter":{},"headers":[],"relativePath":"intro.md"}'),o={name:"intro.md"},n=t('

引言

编程

编程(Programming)是一项创造性的活动,如同创作(Composing)、绘画(Painting)等一样。我们常常会通过一系列的步骤把所需要的部件进行组合,得到另一个期望的结构,或者是解决一个实际性的问题。表达和构建这些步骤的过程,就是编程(当然,在脱离程序语言以后,也可以把它叫做规划(Programme))。

我们会简单地把一件件小事按顺序做,这样一件件小事的组合以后就可以完成一件更大的事情。比如我们会切菜,生火,炒菜,煮饭,这样我们就能够做出一顿午餐。

当然在这个过程中可能会设计一些计算。我们会考虑,需要放几勺盐和几把米,以适合最后吃午餐的人的营养均衡。在特殊的时候,甚至需要精确到毫克级别的营养规划,以及不同人的营养结构的差异等。我们会在第二节探讨关于计算,以及计算的组合等相关问题。

在规划和解决问题的时候可能并不是一帆风顺的,有时候我们会面临各种抉择。比如,考虑明天下午吃素还是吃荤,吃鸡肉和还是鱼肉,吃宫保鸡丁还是鱼香肉丝等等。有比如,在我们准备吃鱼香肉丝的时候发现没有鱼了,又该怎么办。

在任何需要做选择的时候,作为一个程序员(规划师),你不应该出现选择困难症。因为任何一个涵待解决的问题都必须得到解决之后才能让这个程序正常地执行。所以,我们在第三章中会探讨关于过程设计,以及如何利用循环和选择等手法来设计一个完善的程序的相关问题。

但是做饭的时候你是怎么区分盐和砂糖、醋和酱油的呢?当然你会说每个都尝一遍,但是这样既有可能浪费,又不卫生。所以习惯性地我们会关注这些材料瓶上面的标签,从上面知道我们用的是什么东西。当然对于读不懂标签的计算机来说,这些材料作为商品的时候都会有固定的条形码,这个条形码也能够让计算机知道一个被它看到的东西代表着什么。因为它知道这些条码是如何编码(Encoding)的。

当你遇到大量具有相同组件的时候,可以考虑用序列(Sequence)或者列表(List)来安排这些组件。比如做一道菜的每一个步骤对应在菜谱上的每一项。或者是在处理具有层级结构以及更复杂的关联的结构的时候采用(Tree)和(Graph)来组织。比如六度关系理论,实际上就是一张非常大的(Graph)。

有时候会考虑到一部分组件是可变的。比如时间总是流逝的,人总会长大的。当我们尝试描述一个组件改变的时候,就引入了状态(State)。这种时候我们更希望能够把改变的数据和相应的操作做一个绑定,这样子绑定了组件的操作就可以看成一个闭包(Closure)。简单地比如一个计数器。当在组件上面绑定更多操作的时候,就可以把它看作一个对象(Object)。

可变的状态可能会带来的一个问题就是,如果有多个人尝试同时去修改这个状态的时候,会造成结果的不一致性。比如,一个没有加入事务处理的银行账户,两个人同时在进行操作,账户中原有1000元,甲存入了200块,但在甲存入200块还未完成的同时乙取出了200块。这种时候会有几种可能的结果,第一种就是甲先于乙完成操作,结果就是账户余额变成了800块,凭空损失了200块;另外一种就是乙先于甲完成了操作,结果账户余额就变成了甲操作的结果,1200块,凭空多出来400块。这种问题就属于并发(Concurrency)。

JavaScript

本书选择使用JavaScript来讲述编程中的各种思想和概念。

我们并不是在讲述JavaScript这门编程语言,只是通过JavaScript和其提供的丰富的方便特性,来讲述关于“程序设计”的内容。任何与JavaScript细节相关的东西,可以参考Mozilla Developer Network,对于特定的主题,我也会专门列出可以参阅的链接。StackOverflow上也有丰富的资源来解答你的疑惑。

作为入门书籍,为了直观地表述各种概念或者原理,本书可能会使用一些非专业词汇,或者与准确定义有偏差的理解。JavaScript作为一种生产力工具,其准确定义和行为以ECMA 262国际标准为准。对专业人员如有任何困惑或者争议的点,请参考标准文档的描述。同样,如果你通过本书对编程和相关的东西产生了兴趣,希望深入了解和学习,也请使用专业的书籍和资料。本书只是作为入门的引导,不对专业内容做解释和定义。

更新状态

这本书从2015年8月开始编写,中间经过了多次的重写过程,包括把编程语言从偏硬核且专业的C++转向为JavaScript、引入更多资料和练习、增补和调整章节顺序等,目前内容仍处于不稳定的状态,请参考下面的状态来确定这一章是否值得学习:

  • 第一章 环境 (rewriting)
  • 第二章 计算 Computation (outdated)
  • 第三章 过程 Procedural (outdated)
  • 第四章 编码 Encoding (outdated)
  • 第五章 序列 Sequence (outdated)
  • 第六章 数据 Data (outdated)
  • 第七章 状态 State (prepared)
  • 第八章 引用 Reference
  • 第九章 闭包 Closure
  • 第十章 对象 Object (prepared)
  • 第十一章
  • 第十二章
  • more...

目录

参与贡献

本书的源码托管在github:kenpusney/i2p-the-book仓库,你可以提交issue或者pull request贡献和改进本书。

支持我

关注我的公众号获取更新:

微信公众号

资助我创作:

微信二维码:

Buy me a coffee

或点击我的 Paypal 链接给予帮助。

特别感谢以下朋友的资助
',31),h=[n];function s(c,p,d,g,u,f){return r(),a("div",null,h)}const b=e(o,[["render",s]]);export{_ as __pageData,b as default}; diff --git a/assets/intro.md.ca4ae51e.lean.js b/assets/intro.md.ca4ae51e.lean.js new file mode 100644 index 0000000..d74ea20 --- /dev/null +++ b/assets/intro.md.ca4ae51e.lean.js @@ -0,0 +1 @@ +import{_ as e,c as a,o as r,O as t}from"./chunks/framework.41379913.js";const l="/assets/wechat-mp-qrcode.f64c0e73.jpg",i="/assets/wechat-pay-qrcode.87383472.jpg",_=JSON.parse('{"title":"引言","description":"","frontmatter":{},"headers":[],"relativePath":"intro.md"}'),o={name:"intro.md"},n=t("",31),h=[n];function s(c,p,d,g,u,f){return r(),a("div",null,h)}const b=e(o,[["render",s]]);export{_ as __pageData,b as default}; diff --git a/assets/stream.md.a4405d7c.js b/assets/stream.md.a4405d7c.js new file mode 100644 index 0000000..bb66f55 --- /dev/null +++ b/assets/stream.md.a4405d7c.js @@ -0,0 +1 @@ +import{_ as a,c as t,o as r,z as e,a as s}from"./chunks/framework.41379913.js";const u=JSON.parse('{"title":"流","description":"","frontmatter":{},"headers":[],"relativePath":"stream.md"}'),o={name:"stream.md"},c=e("h1",{id:"流",tabindex:"-1"},[s("流 "),e("a",{class:"header-anchor",href:"#流","aria-label":'Permalink to "流"'},"​")],-1),n=[c];function d(i,_,l,m,p,h){return r(),t("div",null,n)}const x=a(o,[["render",d]]);export{u as __pageData,x as default}; diff --git a/assets/stream.md.a4405d7c.lean.js b/assets/stream.md.a4405d7c.lean.js new file mode 100644 index 0000000..bb66f55 --- /dev/null +++ b/assets/stream.md.a4405d7c.lean.js @@ -0,0 +1 @@ +import{_ as a,c as t,o as r,z as e,a as s}from"./chunks/framework.41379913.js";const u=JSON.parse('{"title":"流","description":"","frontmatter":{},"headers":[],"relativePath":"stream.md"}'),o={name:"stream.md"},c=e("h1",{id:"流",tabindex:"-1"},[s("流 "),e("a",{class:"header-anchor",href:"#流","aria-label":'Permalink to "流"'},"​")],-1),n=[c];function d(i,_,l,m,p,h){return r(),t("div",null,n)}const x=a(o,[["render",d]]);export{u as __pageData,x as default}; diff --git a/assets/style.c68003ee.css b/assets/style.c68003ee.css new file mode 100644 index 0000000..759d1b2 --- /dev/null +++ b/assets/style.c68003ee.css @@ -0,0 +1 @@ +@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-cyrillic.5f2c6c8c.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-cyrillic-ext.e75737ce.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-greek.d5a6d92a.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-greek-ext.ab0619bc.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-latin.2ed14f66.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-latin-ext.0030eebd.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-vietnamese.14ce25a6.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-cyrillic.ea42a392.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-greek.8f4463c4.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-greek-ext.4fbe9427.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-latin.bd3b6f56.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-latin-ext.bd8920cc.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-vietnamese.6ce511fb.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Chinese Quotes;src:local("PingFang SC Regular"),local("PingFang SC"),local("SimHei"),local("Source Han Sans SC");unicode-range:U+2018,U+2019,U+201C,U+201D}:root{--vp-c-white: #ffffff;--vp-c-black: #000000;--vp-c-gray: #8e8e93;--vp-c-text-light-1: rgba(60, 60, 67);--vp-c-text-light-2: rgba(60, 60, 67, .75);--vp-c-text-light-3: rgba(60, 60, 67, .33);--vp-c-text-dark-1: rgba(255, 255, 245, .86);--vp-c-text-dark-2: rgba(235, 235, 245, .6);--vp-c-text-dark-3: rgba(235, 235, 245, .38);--vp-c-green: #10b981;--vp-c-green-light: #34d399;--vp-c-green-lighter: #6ee7b7;--vp-c-green-dark: #059669;--vp-c-green-darker: #047857;--vp-c-green-dimm-1: rgba(16, 185, 129, .05);--vp-c-green-dimm-2: rgba(16, 185, 129, .2);--vp-c-green-dimm-3: rgba(16, 185, 129, .5);--vp-c-yellow: #d97706;--vp-c-yellow-light: #f59e0b;--vp-c-yellow-lighter: #fbbf24;--vp-c-yellow-dark: #b45309;--vp-c-yellow-darker: #92400e;--vp-c-yellow-dimm-1: rgba(234, 179, 8, .05);--vp-c-yellow-dimm-2: rgba(234, 179, 8, .2);--vp-c-yellow-dimm-3: rgba(234, 179, 8, .5);--vp-c-red: #f43f5e;--vp-c-red-light: #fb7185;--vp-c-red-lighter: #fda4af;--vp-c-red-dark: #e11d48;--vp-c-red-darker: #be123c;--vp-c-red-dimm-1: rgba(244, 63, 94, .05);--vp-c-red-dimm-2: rgba(244, 63, 94, .2);--vp-c-red-dimm-3: rgba(244, 63, 94, .5);--vp-c-sponsor: #db2777}:root{--vp-c-bg: #ffffff;--vp-c-bg-elv: #ffffff;--vp-c-bg-elv-up: #ffffff;--vp-c-bg-elv-down: #f6f6f7;--vp-c-bg-elv-mute: #f6f6f7;--vp-c-bg-soft: #f6f6f7;--vp-c-bg-soft-up: #f9f9fa;--vp-c-bg-soft-down: #e3e3e5;--vp-c-bg-soft-mute: #e3e3e5;--vp-c-bg-alt: #f6f6f7;--vp-c-border: rgba(60, 60, 67, .29);--vp-c-divider: rgba(60, 60, 67, .12);--vp-c-gutter: rgba(60, 60, 67, .12);--vp-c-neutral: var(--vp-c-black);--vp-c-neutral-inverse: var(--vp-c-white);--vp-c-text-1: var(--vp-c-text-light-1);--vp-c-text-2: var(--vp-c-text-light-2);--vp-c-text-3: var(--vp-c-text-light-3);--vp-c-text-inverse-1: var(--vp-c-text-dark-1);--vp-c-text-inverse-2: var(--vp-c-text-dark-2);--vp-c-text-inverse-3: var(--vp-c-text-dark-3);--vp-c-text-code: #476582;--vp-c-brand: var(--vp-c-green);--vp-c-brand-light: var(--vp-c-green-light);--vp-c-brand-lighter: var(--vp-c-green-lighter);--vp-c-brand-dark: var(--vp-c-green-dark);--vp-c-brand-darker: var(--vp-c-green-darker);--vp-c-mute: #f6f6f7;--vp-c-mute-light: #f9f9fc;--vp-c-mute-lighter: #ffffff;--vp-c-mute-dark: #e3e3e5;--vp-c-mute-darker: #d7d7d9}.dark{--vp-c-bg: #1e1e20;--vp-c-bg-elv: #252529;--vp-c-bg-elv-up: #313136;--vp-c-bg-elv-down: #1e1e20;--vp-c-bg-elv-mute: #313136;--vp-c-bg-soft: #252529;--vp-c-bg-soft-up: #313136;--vp-c-bg-soft-down: #1e1e20;--vp-c-bg-soft-mute: #313136;--vp-c-bg-alt: #161618;--vp-c-border: rgba(82, 82, 89, .68);--vp-c-divider: rgba(82, 82, 89, .32);--vp-c-gutter: #000000;--vp-c-neutral: var(--vp-c-white);--vp-c-neutral-inverse: var(--vp-c-black);--vp-c-text-1: var(--vp-c-text-dark-1);--vp-c-text-2: var(--vp-c-text-dark-2);--vp-c-text-3: var(--vp-c-text-dark-3);--vp-c-text-inverse-1: var(--vp-c-text-light-1);--vp-c-text-inverse-2: var(--vp-c-text-light-2);--vp-c-text-inverse-3: var(--vp-c-text-light-3);--vp-c-text-code: #c9def1;--vp-c-mute: #313136;--vp-c-mute-light: #3a3a3c;--vp-c-mute-lighter: #505053;--vp-c-mute-dark: #2c2c30;--vp-c-mute-darker: #252529}:root{--vp-font-family-base: "Chinese Quotes", "Inter var", "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--vp-font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}:root{--vp-shadow-1: 0 1px 2px rgba(0, 0, 0, .04), 0 1px 2px rgba(0, 0, 0, .06);--vp-shadow-2: 0 3px 12px rgba(0, 0, 0, .07), 0 1px 4px rgba(0, 0, 0, .07);--vp-shadow-3: 0 12px 32px rgba(0, 0, 0, .1), 0 2px 6px rgba(0, 0, 0, .08);--vp-shadow-4: 0 14px 44px rgba(0, 0, 0, .12), 0 3px 9px rgba(0, 0, 0, .12);--vp-shadow-5: 0 18px 56px rgba(0, 0, 0, .16), 0 4px 12px rgba(0, 0, 0, .16)}:root{--vp-z-index-local-nav: 10;--vp-z-index-nav: 20;--vp-z-index-layout-top: 30;--vp-z-index-backdrop: 40;--vp-z-index-sidebar: 50;--vp-z-index-footer: 60}:root{--vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E")}:root{--vp-layout-max-width: 1440px}:root{--vp-header-anchor-symbol: "#"}:root{--vp-code-line-height: 1.7;--vp-code-font-size: .875em;--vp-code-block-color: var(--vp-c-text-dark-1);--vp-code-block-bg: #292b30;--vp-code-block-bg-light: #1e1e20;--vp-code-block-divider-color: #000000;--vp-code-line-highlight-color: rgba(0, 0, 0, .5);--vp-code-line-number-color: var(--vp-c-text-dark-3);--vp-code-line-diff-add-color: var(--vp-c-green-dimm-2);--vp-code-line-diff-add-symbol-color: var(--vp-c-green);--vp-code-line-diff-remove-color: var(--vp-c-red-dimm-2);--vp-code-line-diff-remove-symbol-color: var(--vp-c-red);--vp-code-line-warning-color: var(--vp-c-yellow-dimm-2);--vp-code-line-error-color: var(--vp-c-red-dimm-2);--vp-code-copy-code-border-color: transparent;--vp-code-copy-code-bg: var(--vp-code-block-bg-light);--vp-code-copy-code-hover-border-color: var(--vp-c-divider);--vp-code-copy-code-hover-bg: var(--vp-code-block-bg-light);--vp-code-copy-code-active-text: var(--vp-c-text-dark-2);--vp-code-tab-divider: var(--vp-code-block-divider-color);--vp-code-tab-text-color: var(--vp-c-text-dark-2);--vp-code-tab-bg: var(--vp-code-block-bg);--vp-code-tab-hover-text-color: var(--vp-c-text-dark-1);--vp-code-tab-active-text-color: var(--vp-c-text-dark-1);--vp-code-tab-active-bar-color: var(--vp-c-brand)}.dark{--vp-code-block-bg: #161618}:root{--vp-button-brand-border: var(--vp-c-brand-lighter);--vp-button-brand-text: var(--vp-c-white);--vp-button-brand-bg: var(--vp-c-brand);--vp-button-brand-hover-border: var(--vp-c-brand-lighter);--vp-button-brand-hover-text: var(--vp-c-white);--vp-button-brand-hover-bg: var(--vp-c-brand-dark);--vp-button-brand-active-border: var(--vp-c-brand-lighter);--vp-button-brand-active-text: var(--vp-c-white);--vp-button-brand-active-bg: var(--vp-c-brand-darker);--vp-button-alt-border: var(--vp-c-border);--vp-button-alt-text: var(--vp-c-neutral);--vp-button-alt-bg: var(--vp-c-mute);--vp-button-alt-hover-border: var(--vp-c-border);--vp-button-alt-hover-text: var(--vp-c-neutral);--vp-button-alt-hover-bg: var(--vp-c-mute-dark);--vp-button-alt-active-border: var(--vp-c-border);--vp-button-alt-active-text: var(--vp-c-neutral);--vp-button-alt-active-bg: var(--vp-c-mute-darker);--vp-button-sponsor-border: var(--vp-c-gray-light-3);--vp-button-sponsor-text: var(--vp-c-text-light-2);--vp-button-sponsor-bg: transparent;--vp-button-sponsor-hover-border: var(--vp-c-sponsor);--vp-button-sponsor-hover-text: var(--vp-c-sponsor);--vp-button-sponsor-hover-bg: transparent;--vp-button-sponsor-active-border: var(--vp-c-sponsor);--vp-button-sponsor-active-text: var(--vp-c-sponsor);--vp-button-sponsor-active-bg: transparent}.dark{--vp-button-sponsor-border: var(--vp-c-gray-dark-1);--vp-button-sponsor-text: var(--vp-c-text-dark-2)}:root{--vp-custom-block-font-size: 14px;--vp-custom-block-code-font-size: 13px;--vp-custom-block-info-border: var(--vp-c-border);--vp-custom-block-info-text: var(--vp-c-text-2);--vp-custom-block-info-bg: var(--vp-c-bg-soft-up);--vp-custom-block-info-code-bg: var(--vp-c-bg-soft);--vp-custom-block-tip-border: var(--vp-c-green);--vp-custom-block-tip-text: var(--vp-c-green-dark);--vp-custom-block-tip-bg: var(--vp-c-bg-soft-up);--vp-custom-block-tip-code-bg: var(--vp-c-bg-soft);--vp-custom-block-warning-border: var(--vp-c-yellow);--vp-custom-block-warning-text: var(--vp-c-yellow);--vp-custom-block-warning-bg: var(--vp-c-bg-soft-up);--vp-custom-block-warning-code-bg: var(--vp-c-bg-soft);--vp-custom-block-danger-border: var(--vp-c-red);--vp-custom-block-danger-text: var(--vp-c-red);--vp-custom-block-danger-bg: var(--vp-c-bg-soft-up);--vp-custom-block-danger-code-bg: var(--vp-c-bg-soft);--vp-custom-block-details-border: var(--vp-custom-block-info-border);--vp-custom-block-details-text: var(--vp-custom-block-info-text);--vp-custom-block-details-bg: var(--vp-custom-block-info-bg);--vp-custom-block-details-code-bg: var(--vp-custom-block-details-bg)}:root{--vp-input-border-color: var(--vp-c-border);--vp-input-bg-color: var(--vp-c-bg-alt);--vp-input-hover-border-color: var(--vp-c-gray);--vp-input-switch-bg-color: var(--vp-c-mute)}:root{--vp-nav-height: 64px;--vp-nav-bg-color: var(--vp-c-bg);--vp-nav-screen-bg-color: var(--vp-c-bg)}:root{--vp-local-nav-bg-color: var(--vp-c-bg)}:root{--vp-sidebar-width: 272px;--vp-sidebar-bg-color: var(--vp-c-bg-alt)}:root{--vp-backdrop-bg-color: rgba(0, 0, 0, .6)}:root{--vp-home-hero-name-color: var(--vp-c-brand);--vp-home-hero-name-background: transparent;--vp-home-hero-image-background-image: none;--vp-home-hero-image-filter: none}:root{--vp-badge-info-border: var(--vp-c-border);--vp-badge-info-text: var(--vp-c-text-2);--vp-badge-info-bg: var(--vp-c-bg-soft-up);--vp-badge-tip-border: var(--vp-c-green-dark);--vp-badge-tip-text: var(--vp-c-green);--vp-badge-tip-bg: var(--vp-c-green-dimm-1);--vp-badge-warning-border: var(--vp-c-yellow-dark);--vp-badge-warning-text: var(--vp-c-yellow);--vp-badge-warning-bg: var(--vp-c-yellow-dimm-1);--vp-badge-danger-border: var(--vp-c-red-dark);--vp-badge-danger-text: var(--vp-c-red);--vp-badge-danger-bg: var(--vp-c-red-dimm-1)}:root{--vp-carbon-ads-text-color: var(--vp-c-text-1);--vp-carbon-ads-poweredby-color: var(--vp-c-text-2);--vp-carbon-ads-bg-color: var(--vp-c-bg-soft);--vp-carbon-ads-hover-text-color: var(--vp-c-brand);--vp-carbon-ads-hover-poweredby-color: var(--vp-c-text-1)}:root{--vp-local-search-bg: var(--vp-c-bg);--vp-local-search-result-bg: var(--vp-c-bg);--vp-local-search-result-border: var(--vp-c-divider);--vp-local-search-result-selected-bg: var(--vp-c-bg);--vp-local-search-result-selected-border: var(--vp-c-brand);--vp-local-search-highlight-bg: var(--vp-c-green-lighter);--vp-local-search-highlight-text: var(--vp-c-black)}*,:before,:after{box-sizing:border-box}html{line-height:1.4;font-size:16px;-webkit-text-size-adjust:100%}html.dark{color-scheme:dark}body{margin:0;width:100%;min-width:320px;min-height:100vh;line-height:24px;font-family:var(--vp-font-family-base);font-size:16px;font-weight:400;color:var(--vp-c-text-1);background-color:var(--vp-c-bg);direction:ltr;font-synthesis:style;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}main{display:block}h1,h2,h3,h4,h5,h6{margin:0;line-height:24px;font-size:16px;font-weight:400}p{margin:0}strong,b{font-weight:600}a,area,button,[role=button],input,label,select,summary,textarea{touch-action:manipulation}a{color:inherit;text-decoration:inherit}ol,ul{list-style:none;margin:0;padding:0}blockquote{margin:0}pre,code,kbd,samp{font-family:var(--vp-font-family-mono)}img,svg,video,canvas,audio,iframe,embed,object{display:block}figure{margin:0}img,video{max-width:100%;height:auto}button,input,optgroup,select,textarea{border:0;padding:0;line-height:inherit;color:inherit}button{padding:0;font-family:inherit;background-color:transparent;background-image:none}button:enabled,[role=button]:enabled{cursor:pointer}button:focus,button:focus-visible{outline:1px dotted;outline:4px auto -webkit-focus-ring-color}button:focus:not(:focus-visible){outline:none!important}input:focus,textarea:focus,select:focus{outline:none}table{border-collapse:collapse}input{background-color:transparent}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--vp-c-text-3)}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--vp-c-text-3)}input::placeholder,textarea::placeholder{color:var(--vp-c-text-3)}input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=number]{-moz-appearance:textfield}textarea{resize:vertical}select{-webkit-appearance:none}fieldset{margin:0;padding:0}h1,h2,h3,h4,h5,h6,li,p{overflow-wrap:break-word}vite-error-overlay{z-index:9999}.visually-hidden{position:absolute;width:1px;height:1px;white-space:nowrap;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden}.custom-block{border:1px solid transparent;border-radius:8px;padding:16px 16px 8px;line-height:24px;font-size:var(--vp-custom-block-font-size);color:var(--vp-c-text-2)}.custom-block.info{border-color:var(--vp-custom-block-info-border);color:var(--vp-custom-block-info-text);background-color:var(--vp-custom-block-info-bg)}.custom-block.custom-block th,.custom-block.custom-block blockquote>p{font-size:var(--vp-custom-block-font-size);color:inherit}.custom-block.info code{background-color:var(--vp-custom-block-info-code-bg)}.custom-block.tip{border-color:var(--vp-custom-block-tip-border);color:var(--vp-custom-block-tip-text);background-color:var(--vp-custom-block-tip-bg)}.custom-block.tip code{background-color:var(--vp-custom-block-tip-code-bg)}.custom-block.warning{border-color:var(--vp-custom-block-warning-border);color:var(--vp-custom-block-warning-text);background-color:var(--vp-custom-block-warning-bg)}.custom-block.warning code{background-color:var(--vp-custom-block-warning-code-bg)}.custom-block.danger{border-color:var(--vp-custom-block-danger-border);color:var(--vp-custom-block-danger-text);background-color:var(--vp-custom-block-danger-bg)}.custom-block.danger code{background-color:var(--vp-custom-block-danger-code-bg)}.custom-block.details{border-color:var(--vp-custom-block-details-border);color:var(--vp-custom-block-details-text);background-color:var(--vp-custom-block-details-bg)}.custom-block.details code{background-color:var(--vp-custom-block-details-code-bg)}.custom-block-title{font-weight:600}.custom-block p+p{margin:8px 0}.custom-block.details summary{margin:0 0 8px;font-weight:700;cursor:pointer}.custom-block.details summary+p{margin:8px 0}.custom-block a{color:inherit;font-weight:600}.custom-block a:hover{text-decoration:underline}.custom-block code{font-size:var(--vp-custom-block-code-font-size)}.dark .vp-code-light{display:none}html:not(.dark) .vp-code-dark{display:none}.vp-code-group{margin-top:16px}.vp-code-group .tabs{position:relative;display:flex;margin-right:-24px;margin-left:-24px;padding:0 12px;background-color:var(--vp-code-tab-bg);overflow-x:auto;overflow-y:hidden}.vp-code-group .tabs:after{position:absolute;right:0;bottom:0;left:0;height:1px;background-color:var(--vp-code-tab-divider);content:""}@media (min-width: 640px){.vp-code-group .tabs{margin-right:0;margin-left:0;border-radius:8px 8px 0 0}}.vp-code-group .tabs input{position:absolute;opacity:0;pointer-events:none}.vp-code-group .tabs label{position:relative;display:inline-block;border-bottom:1px solid transparent;padding:0 12px;line-height:48px;font-size:14px;font-weight:500;color:var(--vp-code-tab-text-color);white-space:nowrap;cursor:pointer;transition:color .25s}.vp-code-group .tabs label:after{position:absolute;right:8px;bottom:-1px;left:8px;z-index:10;height:1px;content:"";background-color:transparent;transition:background-color .25s}.vp-code-group label:hover{color:var(--vp-code-tab-hover-text-color)}.vp-code-group input:checked+label{color:var(--vp-code-tab-active-text-color)}.vp-code-group input:checked+label:after{background-color:var(--vp-code-tab-active-bar-color)}.vp-code-group div[class*=language-]{display:none;margin-top:0!important;border-top-left-radius:0!important;border-top-right-radius:0!important}.vp-code-group div[class*=language-].active{display:block}.vp-doc h1,.vp-doc h2,.vp-doc h3,.vp-doc h4,.vp-doc h5,.vp-doc h6{position:relative;font-weight:600;outline:none}.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:28px}.vp-doc h2{margin:48px 0 16px;border-top:1px solid var(--vp-c-divider);padding-top:24px;letter-spacing:-.02em;line-height:32px;font-size:24px}.vp-doc h3{margin:32px 0 0;letter-spacing:-.01em;line-height:28px;font-size:20px}.vp-doc .header-anchor{float:left;margin-left:-.87em;padding-right:.23em;font-weight:500;user-select:none;opacity:0;transition:color .25s,opacity .25s}.vp-doc .header-anchor:before{content:var(--vp-header-anchor-symbol)}.vp-doc h1:hover .header-anchor,.vp-doc h1 .header-anchor:focus,.vp-doc h2:hover .header-anchor,.vp-doc h2 .header-anchor:focus,.vp-doc h3:hover .header-anchor,.vp-doc h3 .header-anchor:focus,.vp-doc h4:hover .header-anchor,.vp-doc h4 .header-anchor:focus,.vp-doc h5:hover .header-anchor,.vp-doc h5 .header-anchor:focus,.vp-doc h6:hover .header-anchor,.vp-doc h6 .header-anchor:focus{opacity:1}@media (min-width: 768px){.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:32px}}.vp-doc p,.vp-doc summary{margin:16px 0}.vp-doc p{line-height:28px}.vp-doc blockquote{margin:16px 0;border-left:2px solid var(--vp-c-divider);padding-left:16px;transition:border-color .5s}.vp-doc blockquote>p{margin:0;font-size:16px;color:var(--vp-c-text-2);transition:color .5s}.vp-doc a{font-weight:500;color:var(--vp-c-brand);text-decoration-style:dotted;transition:color .25s}.vp-doc a:hover{text-decoration:underline}.vp-doc strong{font-weight:600}.vp-doc ul,.vp-doc ol{padding-left:1.25rem;margin:16px 0}.vp-doc ul{list-style:disc}.vp-doc ol{list-style:decimal}.vp-doc li+li{margin-top:8px}.vp-doc li>ol,.vp-doc li>ul{margin:8px 0 0}.vp-doc table{display:block;border-collapse:collapse;margin:20px 0;overflow-x:auto}.vp-doc tr{border-top:1px solid var(--vp-c-divider);transition:background-color .5s}.vp-doc tr:nth-child(2n){background-color:var(--vp-c-bg-soft)}.vp-doc th,.vp-doc td{border:1px solid var(--vp-c-divider);padding:8px 16px}.vp-doc th{text-align:left;font-size:14px;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-doc td{font-size:14px}.vp-doc hr{margin:16px 0;border:none;border-top:1px solid var(--vp-c-divider)}.vp-doc .custom-block{margin:16px 0}.vp-doc .custom-block p{margin:8px 0;line-height:24px}.vp-doc .custom-block p:first-child{margin:0}.vp-doc .custom-block a{color:inherit;font-weight:600}.vp-doc .custom-block a:hover{text-decoration:underline}.vp-doc .custom-block code{font-size:var(--vp-custom-block-code-font-size);font-weight:700;color:inherit}.vp-doc .custom-block div[class*=language-]{margin:8px 0}.vp-doc .custom-block div[class*=language-] code{font-weight:400;background-color:transparent}.vp-doc :not(pre,h1,h2,h3,h4,h5,h6)>code{font-size:var(--vp-code-font-size)}.vp-doc :not(pre)>code{border-radius:4px;padding:3px 6px;color:var(--vp-c-text-code);background-color:var(--vp-c-mute);transition:color .5s,background-color .5s}.vp-doc h1>code,.vp-doc h2>code,.vp-doc h3>code{font-size:.9em}.vp-doc a>code{color:var(--vp-c-brand);transition:color .25s}.vp-doc a:hover>code{color:var(--vp-c-brand-dark)}.vp-doc div[class*=language-]{position:relative;margin:16px -24px;background-color:var(--vp-code-block-bg);overflow-x:auto;transition:background-color .5s}@media (min-width: 640px){.vp-doc div[class*=language-]{border-radius:8px;margin:16px 0}}@media (max-width: 639px){.vp-doc li div[class*=language-]{border-radius:8px 0 0 8px}}.vp-doc div[class*=language-]+div[class*=language-],.vp-doc div[class$=-api]+div[class*=language-],.vp-doc div[class*=language-]+div[class$=-api]>div[class*=language-]{margin-top:-8px}.vp-doc [class*=language-] pre,.vp-doc [class*=language-] code{direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}.vp-doc [class*=language-] pre{position:relative;z-index:1;margin:0;padding:20px 0;background:transparent;overflow-x:auto}.vp-doc [class*=language-] code{display:block;padding:0 24px;width:fit-content;min-width:100%;line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-block-color);transition:color .5s}.vp-doc [class*=language-] code .highlighted{background-color:var(--vp-code-line-highlight-color);transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .highlighted.error{background-color:var(--vp-code-line-error-color)}.vp-doc [class*=language-] code .highlighted.warning{background-color:var(--vp-code-line-warning-color)}.vp-doc [class*=language-] code .diff{transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .diff:before{position:absolute;left:10px}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){filter:blur(.095rem);opacity:.4;transition:filter .35s,opacity .35s}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){opacity:.7;transition:filter .35s,opacity .35s}.vp-doc [class*=language-]:hover .has-focused-lines .line:not(.has-focus){filter:blur(0);opacity:1}.vp-doc [class*=language-] code .diff.remove{background-color:var(--vp-code-line-diff-remove-color);opacity:.7}.vp-doc [class*=language-] code .diff.remove:before{content:"-";color:var(--vp-code-line-diff-remove-symbol-color)}.vp-doc [class*=language-] code .diff.add{background-color:var(--vp-code-line-diff-add-color)}.vp-doc [class*=language-] code .diff.add:before{content:"+";color:var(--vp-code-line-diff-add-symbol-color)}.vp-doc div[class*=language-].line-numbers-mode{padding-left:32px}.vp-doc .line-numbers-wrapper{position:absolute;top:0;bottom:0;left:0;z-index:3;border-right:1px solid var(--vp-code-block-divider-color);padding-top:20px;width:32px;text-align:center;font-family:var(--vp-font-family-mono);line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-line-number-color);transition:border-color .5s,color .5s}.vp-doc [class*=language-]>button.copy{direction:ltr;position:absolute;top:12px;right:12px;z-index:3;border:1px solid var(--vp-code-copy-code-border-color);border-radius:4px;width:40px;height:40px;background-color:var(--vp-code-copy-code-bg);opacity:0;cursor:pointer;background-image:var(--vp-icon-copy);background-position:50%;background-size:20px;background-repeat:no-repeat;transition:border-color .25s,background-color .25s,opacity .25s}.vp-doc [class*=language-]:hover>button.copy,.vp-doc [class*=language-]>button.copy:focus{opacity:1}.vp-doc [class*=language-]>button.copy:hover,.vp-doc [class*=language-]>button.copy.copied{border-color:var(--vp-code-copy-code-hover-border-color);background-color:var(--vp-code-copy-code-hover-bg)}.vp-doc [class*=language-]>button.copy.copied,.vp-doc [class*=language-]>button.copy:hover.copied{border-radius:0 4px 4px 0;background-color:var(--vp-code-copy-code-hover-bg);background-image:var(--vp-icon-copied)}.vp-doc [class*=language-]>button.copy.copied:before,.vp-doc [class*=language-]>button.copy:hover.copied:before{position:relative;top:-1px;left:-65px;display:flex;justify-content:center;align-items:center;border:1px solid var(--vp-code-copy-code-hover-border-color);border-right:0;border-radius:4px 0 0 4px;width:64px;height:40px;text-align:center;font-size:12px;font-weight:500;color:var(--vp-code-copy-code-active-text);background-color:var(--vp-code-copy-code-hover-bg);white-space:nowrap;content:"Copied"}.vp-doc [class*=language-]>span.lang{position:absolute;top:2px;right:8px;z-index:2;font-size:12px;font-weight:500;color:var(--vp-c-text-dark-3);transition:color .4s,opacity .4s}.vp-doc [class*=language-]:hover>button.copy+span.lang,.vp-doc [class*=language-]>button.copy:focus+span.lang{opacity:0}.vp-doc .VPTeamMembers{margin-top:24px}.vp-doc .VPTeamMembers.small.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}.vp-doc .VPTeamMembers.small.count-2 .container,.vp-doc .VPTeamMembers.small.count-3 .container{max-width:100%!important}.vp-doc .VPTeamMembers.medium.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}.vp-sponsor{border-radius:16px;overflow:hidden}.vp-sponsor.aside{border-radius:12px}.vp-sponsor-section+.vp-sponsor-section{margin-top:4px}.vp-sponsor-tier{margin-bottom:4px;text-align:center;letter-spacing:1px;line-height:24px;width:100%;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-sponsor.normal .vp-sponsor-tier{padding:13px 0 11px;font-size:14px}.vp-sponsor.aside .vp-sponsor-tier{padding:9px 0 7px;font-size:12px}.vp-sponsor-grid+.vp-sponsor-tier{margin-top:4px}.vp-sponsor-grid{display:flex;flex-wrap:wrap;gap:4px}.vp-sponsor-grid.xmini .vp-sponsor-grid-link{height:64px}.vp-sponsor-grid.xmini .vp-sponsor-grid-image{max-width:64px;max-height:22px}.vp-sponsor-grid.mini .vp-sponsor-grid-link{height:72px}.vp-sponsor-grid.mini .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.small .vp-sponsor-grid-link{height:96px}.vp-sponsor-grid.small .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.medium .vp-sponsor-grid-link{height:112px}.vp-sponsor-grid.medium .vp-sponsor-grid-image{max-width:120px;max-height:36px}.vp-sponsor-grid.big .vp-sponsor-grid-link{height:184px}.vp-sponsor-grid.big .vp-sponsor-grid-image{max-width:192px;max-height:56px}.vp-sponsor-grid[data-vp-grid="2"] .vp-sponsor-grid-item{width:calc((100% - 4px)/2)}.vp-sponsor-grid[data-vp-grid="3"] .vp-sponsor-grid-item{width:calc((100% - 4px * 2) / 3)}.vp-sponsor-grid[data-vp-grid="4"] .vp-sponsor-grid-item{width:calc((100% - 12px)/4)}.vp-sponsor-grid[data-vp-grid="5"] .vp-sponsor-grid-item{width:calc((100% - 16px)/5)}.vp-sponsor-grid[data-vp-grid="6"] .vp-sponsor-grid-item{width:calc((100% - 4px * 5) / 6)}.vp-sponsor-grid-item{flex-shrink:0;width:100%;background-color:var(--vp-c-bg-soft);transition:background-color .25s}.vp-sponsor-grid-item:hover{background-color:var(--vp-c-bg-soft-down)}.vp-sponsor-grid-item:hover .vp-sponsor-grid-image{filter:grayscale(0) invert(0)}.vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.dark .vp-sponsor-grid-item:hover{background-color:var(--vp-c-white)}.dark .vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.vp-sponsor-grid-link{display:flex}.vp-sponsor-grid-box{display:flex;justify-content:center;align-items:center;width:100%}.vp-sponsor-grid-image{max-width:100%;filter:grayscale(1);transition:filter .25s}.dark .vp-sponsor-grid-image{filter:grayscale(1) invert(1)}.VPBadge[data-v-350d3852]{display:inline-block;margin-left:2px;border:1px solid transparent;border-radius:10px;padding:0 8px;line-height:18px;font-size:12px;font-weight:600;transform:translateY(-2px)}h1 .VPBadge[data-v-350d3852],h2 .VPBadge[data-v-350d3852],h3 .VPBadge[data-v-350d3852],h4 .VPBadge[data-v-350d3852],h5 .VPBadge[data-v-350d3852],h6 .VPBadge[data-v-350d3852]{vertical-align:top}h2 .VPBadge[data-v-350d3852]{border-radius:11px;line-height:20px}.VPBadge.info[data-v-350d3852]{border-color:var(--vp-badge-info-border);color:var(--vp-badge-info-text);background-color:var(--vp-badge-info-bg)}.VPBadge.tip[data-v-350d3852]{border-color:var(--vp-badge-tip-border);color:var(--vp-badge-tip-text);background-color:var(--vp-badge-tip-bg)}.VPBadge.warning[data-v-350d3852]{border-color:var(--vp-badge-warning-border);color:var(--vp-badge-warning-text);background-color:var(--vp-badge-warning-bg)}.VPBadge.danger[data-v-350d3852]{border-color:var(--vp-badge-danger-border);color:var(--vp-badge-danger-text);background-color:var(--vp-badge-danger-bg)}.VPSkipLink[data-v-c8616af1]{top:8px;left:8px;padding:8px 16px;z-index:999;border-radius:8px;font-size:12px;font-weight:700;text-decoration:none;color:var(--vp-c-brand);box-shadow:var(--vp-shadow-3);background-color:var(--vp-c-bg)}.VPSkipLink[data-v-c8616af1]:focus{height:auto;width:auto;clip:auto;clip-path:none}.dark .VPSkipLink[data-v-c8616af1]{color:var(--vp-c-green)}@media (min-width: 1280px){.VPSkipLink[data-v-c8616af1]{top:14px;left:16px}}.VPBackdrop[data-v-c79a1216]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--vp-z-index-backdrop);background:var(--vp-backdrop-bg-color);transition:opacity .5s}.VPBackdrop.fade-enter-from[data-v-c79a1216],.VPBackdrop.fade-leave-to[data-v-c79a1216]{opacity:0}.VPBackdrop.fade-leave-active[data-v-c79a1216]{transition-duration:.25s}@media (min-width: 1280px){.VPBackdrop[data-v-c79a1216]{display:none}}html:not(.dark) .VPImage.dark[data-v-6db2186b]{display:none}.dark .VPImage.light[data-v-6db2186b]{display:none}.title[data-v-f4ef19a3]{display:flex;align-items:center;border-bottom:1px solid transparent;width:100%;height:var(--vp-nav-height);font-size:16px;font-weight:600;color:var(--vp-c-text-1);transition:opacity .25s}.title[data-v-f4ef19a3]:hover{opacity:.6}@media (min-width: 960px){.title[data-v-f4ef19a3]{flex-shrink:0}.VPNavBarTitle.has-sidebar .title[data-v-f4ef19a3]{border-bottom-color:var(--vp-c-divider)}}[data-v-f4ef19a3] .logo{margin-right:8px;height:24px}/*! @docsearch/css 3.3.3 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */:root{--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12)}html[data-theme=dark]{--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}.DocSearch-Button{align-items:center;background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;display:flex;font-weight:500;height:36px;justify-content:space-between;margin:0 0 0 16px;padding:0 8px;user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:none}.DocSearch-Button-Container{align-items:center;display:flex}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;position:relative;padding:0 0 2px;border:0;top:-1px;width:20px}@media (max-width:768px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder{display:none}}.DocSearch--active{overflow:hidden!important}.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;font:inherit;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{appearance:none;background:transparent;border:0;color:var(--docsearch-text-color);flex:1;font:inherit;font-size:1.2em;height:100%;outline:none;padding:0 0 0 8px;width:80%}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator{display:none}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{animation:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0;stroke-width:var(--docsearch-icon-stroke-width)}}.DocSearch-Reset{animation:fade-in .1s ease-in forwards;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0;stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Cancel{display:none}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:transparent}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Help{font-size:.9em;margin:0;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{color:var(--docsearch-muted-color);display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--deleting{transition:none}}.DocSearch-Hit--deleting{opacity:0;transition:all .25s linear}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--favoriting{transition:none}}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:all .25s linear;transition-delay:.25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;stroke-width:var(--docsearch-icon-stroke-width);width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color);stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{appearance:none;background:none;border:0;border-radius:50%;color:inherit;cursor:pointer;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{transition:none}}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:none;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;user-select:none;width:100%;z-index:300}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li{align-items:center;display:flex}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;color:var(--docsearch-muted-color);border:0;width:20px}@media (max-width:768px){:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Dropdown{max-height:calc(var(--docsearch-vh, 1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Cancel{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:none;overflow:hidden;padding:0;user-select:none;white-space:nowrap}.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.DocSearch{--docsearch-primary-color: var(--vp-c-brand);--docsearch-highlight-color: var(--docsearch-primary-color);--docsearch-text-color: var(--vp-c-text-1);--docsearch-muted-color: var(--vp-c-text-2);--docsearch-searchbox-shadow: none;--docsearch-searchbox-focus-background: transparent;--docsearch-key-gradient: transparent;--docsearch-key-shadow: none;--docsearch-modal-background: var(--vp-c-bg-soft);--docsearch-footer-background: var(--vp-c-bg)}.dark .DocSearch{--docsearch-modal-shadow: none;--docsearch-footer-shadow: none;--docsearch-logo-color: var(--vp-c-text-2);--docsearch-hit-background: var(--vp-c-bg-soft-mute);--docsearch-hit-color: var(--vp-c-text-2);--docsearch-hit-shadow: none}.DocSearch-Button{display:flex;justify-content:center;align-items:center;margin:0;padding:0;width:32px;height:55px;background:transparent;transition:border-color .25s}.DocSearch-Button:hover{background:transparent}.DocSearch-Button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}.DocSearch-Button:focus:not(:focus-visible){outline:none!important}@media (min-width: 768px){.DocSearch-Button{justify-content:flex-start;border:1px solid transparent;border-radius:8px;padding:0 10px 0 12px;width:100%;height:40px;background-color:var(--vp-c-bg-alt)}.DocSearch-Button:hover{border-color:var(--vp-c-brand);background:var(--vp-c-bg-alt)}}.DocSearch-Button .DocSearch-Button-Container{display:flex;align-items:center}.DocSearch-Button .DocSearch-Search-Icon{position:relative;width:16px;height:16px;color:var(--vp-c-text-1);fill:currentColor;transition:color .5s}.DocSearch-Button:hover .DocSearch-Search-Icon{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Search-Icon{top:1px;margin-right:8px;width:14px;height:14px;color:var(--vp-c-text-2)}}.DocSearch-Button .DocSearch-Button-Placeholder{display:none;margin-top:2px;padding:0 16px 0 0;font-size:13px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.DocSearch-Button:hover .DocSearch-Button-Placeholder{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Placeholder{display:inline-block}}.DocSearch-Button .DocSearch-Button-Keys{direction:ltr;display:none;min-width:auto}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Keys{display:flex;align-items:center}}.DocSearch-Button .DocSearch-Button-Key{display:block;margin:2px 0 0;border:1px solid var(--vp-c-divider);border-right:none;border-radius:4px 0 0 4px;padding-left:6px;min-width:0;width:auto;height:22px;line-height:22px;font-family:var(--vp-font-family-base);font-size:12px;font-weight:500;transition:color .5s,border-color .5s}.DocSearch-Button .DocSearch-Button-Key+.DocSearch-Button-Key{border-right:1px solid var(--vp-c-divider);border-left:none;border-radius:0 4px 4px 0;padding-left:2px;padding-right:6px}.DocSearch-Button .DocSearch-Button-Key:first-child{font-size:1px;letter-spacing:-12px;color:transparent}.DocSearch-Button .DocSearch-Button-Key:first-child:after{content:var(--vp-meta-key);font-size:12px;letter-spacing:normal;color:var(--docsearch-muted-color)}.DocSearch-Button .DocSearch-Button-Key:first-child>*{display:none}.VPNavBarSearch{display:flex;align-items:center}@media (min-width: 768px){.VPNavBarSearch{flex-grow:1;padding-left:24px}}@media (min-width: 960px){.VPNavBarSearch{padding-left:32px}}.dark .DocSearch-Footer{border-top:1px solid var(--vp-c-divider)}.DocSearch-Form{border:1px solid var(--vp-c-brand);background-color:var(--vp-c-white)}.dark .DocSearch-Form{background-color:var(--vp-c-bg-soft-mute)}.DocSearch-Screen-Icon>svg{margin:auto}.icon[data-v-8f4dc553]{display:inline-block;margin-top:-1px;margin-left:4px;width:11px;height:11px;fill:var(--vp-c-text-3);transition:fill .25s;flex-shrink:0}.VPNavBarMenuLink[data-v-37adc828]{display:flex;align-items:center;padding:0 12px;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.VPNavBarMenuLink.active[data-v-37adc828],.VPNavBarMenuLink[data-v-37adc828]:hover{color:var(--vp-c-brand)}.VPMenuGroup+.VPMenuLink[data-v-d2c93bab]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.link[data-v-d2c93bab]{display:block;border-radius:6px;padding:0 12px;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);white-space:nowrap;transition:background-color .25s,color .25s}.link[data-v-d2c93bab]:hover{color:var(--vp-c-brand);background-color:var(--vp-c-bg-elv-mute)}.link.active[data-v-d2c93bab]{color:var(--vp-c-brand)}.VPMenuGroup[data-v-69e747b5]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.VPMenuGroup[data-v-69e747b5]:first-child{margin-top:0;border-top:0;padding-top:0}.VPMenuGroup+.VPMenuGroup[data-v-69e747b5]{margin-top:12px;border-top:1px solid var(--vp-c-divider)}.title[data-v-69e747b5]{padding:0 12px;line-height:32px;font-size:14px;font-weight:600;color:var(--vp-c-text-2);white-space:nowrap;transition:color .25s}.VPMenu[data-v-e7ea1737]{border-radius:12px;padding:12px;min-width:128px;border:1px solid var(--vp-c-divider);background-color:var(--vp-c-bg-elv);box-shadow:var(--vp-shadow-3);transition:background-color .5s;max-height:calc(100vh - var(--vp-nav-height));overflow-y:auto}.VPMenu[data-v-e7ea1737] .group{margin:0 -12px;padding:0 12px 12px}.VPMenu[data-v-e7ea1737] .group+.group{border-top:1px solid var(--vp-c-divider);padding:11px 12px 12px}.VPMenu[data-v-e7ea1737] .group:last-child{padding-bottom:0}.VPMenu[data-v-e7ea1737] .group+.item{border-top:1px solid var(--vp-c-divider);padding:11px 16px 0}.VPMenu[data-v-e7ea1737] .item{padding:0 16px;white-space:nowrap}.VPMenu[data-v-e7ea1737] .label{flex-grow:1;line-height:28px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.VPMenu[data-v-e7ea1737] .action{padding-left:24px}.VPFlyout[data-v-764effdf]{position:relative}.VPFlyout[data-v-764effdf]:hover{color:var(--vp-c-brand);transition:color .25s}.VPFlyout:hover .text[data-v-764effdf]{color:var(--vp-c-text-2)}.VPFlyout:hover .icon[data-v-764effdf]{fill:var(--vp-c-text-2)}.VPFlyout.active .text[data-v-764effdf]{color:var(--vp-c-brand)}.VPFlyout.active:hover .text[data-v-764effdf]{color:var(--vp-c-brand-dark)}.VPFlyout:hover .menu[data-v-764effdf],.button[aria-expanded=true]+.menu[data-v-764effdf]{opacity:1;visibility:visible;transform:translateY(0)}.button[data-v-764effdf]{display:flex;align-items:center;padding:0 12px;height:var(--vp-nav-height);color:var(--vp-c-text-1);transition:color .5s}.text[data-v-764effdf]{display:flex;align-items:center;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.option-icon[data-v-764effdf]{margin-right:0;width:16px;height:16px;fill:currentColor}.text-icon[data-v-764effdf]{margin-left:4px;width:14px;height:14px;fill:currentColor}.icon[data-v-764effdf]{width:20px;height:20px;fill:currentColor;transition:fill .25s}.menu[data-v-764effdf]{position:absolute;top:calc(var(--vp-nav-height) / 2 + 20px);right:0;opacity:0;visibility:hidden;transition:opacity .25s,visibility .25s,transform .25s}.VPNavBarMenu[data-v-7f418b0f]{display:none}@media (min-width: 768px){.VPNavBarMenu[data-v-7f418b0f]{display:flex}}.VPNavBarTranslations[data-v-74abcbb9]{display:none}@media (min-width: 1280px){.VPNavBarTranslations[data-v-74abcbb9]{display:flex;align-items:center}}.title[data-v-74abcbb9]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.VPSwitch[data-v-f3c41672]{position:relative;border-radius:11px;display:block;width:40px;height:22px;flex-shrink:0;border:1px solid var(--vp-input-border-color);background-color:var(--vp-input-switch-bg-color);transition:border-color .25s}.VPSwitch[data-v-f3c41672]:hover{border-color:var(--vp-input-hover-border-color)}.check[data-v-f3c41672]{position:absolute;top:1px;left:1px;width:18px;height:18px;border-radius:50%;background-color:var(--vp-c-neutral-inverse);box-shadow:var(--vp-shadow-1);transition:transform .25s}.icon[data-v-f3c41672]{position:relative;display:block;width:18px;height:18px;border-radius:50%;overflow:hidden}.icon[data-v-f3c41672] svg{position:absolute;top:3px;left:3px;width:12px;height:12px;fill:var(--vp-c-text-2)}.dark .icon[data-v-f3c41672] svg{fill:var(--vp-c-text-1);transition:opacity .25s}.sun[data-v-a9c8afb8]{opacity:1}.moon[data-v-a9c8afb8],.dark .sun[data-v-a9c8afb8]{opacity:0}.dark .moon[data-v-a9c8afb8]{opacity:1}.dark .VPSwitchAppearance[data-v-a9c8afb8] .check{transform:translate(18px)}.VPNavBarAppearance[data-v-f6a63727]{display:none}@media (min-width: 1280px){.VPNavBarAppearance[data-v-f6a63727]{display:flex;align-items:center}}.VPSocialLink[data-v-c530cc0a]{display:flex;justify-content:center;align-items:center;width:36px;height:36px;color:var(--vp-c-text-2);transition:color .5s}.VPSocialLink[data-v-c530cc0a]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPSocialLink[data-v-c530cc0a]>svg{width:20px;height:20px;fill:currentColor}.VPSocialLinks[data-v-f6988cfb]{display:flex;flex-wrap:wrap;justify-content:center}.VPNavBarSocialLinks[data-v-0394ad82]{display:none}@media (min-width: 1280px){.VPNavBarSocialLinks[data-v-0394ad82]{display:flex;align-items:center}}.VPNavBarExtra[data-v-40855f84]{display:none;margin-right:-12px}@media (min-width: 768px){.VPNavBarExtra[data-v-40855f84]{display:block}}@media (min-width: 1280px){.VPNavBarExtra[data-v-40855f84]{display:none}}.trans-title[data-v-40855f84]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.item.appearance[data-v-40855f84],.item.social-links[data-v-40855f84]{display:flex;align-items:center;padding:0 12px}.item.appearance[data-v-40855f84]{min-width:176px}.appearance-action[data-v-40855f84]{margin-right:-2px}.social-links-list[data-v-40855f84]{margin:-4px -8px}.VPNavBarHamburger[data-v-e5dd9c1c]{display:flex;justify-content:center;align-items:center;width:48px;height:var(--vp-nav-height)}@media (min-width: 768px){.VPNavBarHamburger[data-v-e5dd9c1c]{display:none}}.container[data-v-e5dd9c1c]{position:relative;width:16px;height:14px;overflow:hidden}.VPNavBarHamburger:hover .top[data-v-e5dd9c1c]{top:0;left:0;transform:translate(4px)}.VPNavBarHamburger:hover .middle[data-v-e5dd9c1c]{top:6px;left:0;transform:translate(0)}.VPNavBarHamburger:hover .bottom[data-v-e5dd9c1c]{top:12px;left:0;transform:translate(8px)}.VPNavBarHamburger.active .top[data-v-e5dd9c1c]{top:6px;transform:translate(0) rotate(225deg)}.VPNavBarHamburger.active .middle[data-v-e5dd9c1c]{top:6px;transform:translate(16px)}.VPNavBarHamburger.active .bottom[data-v-e5dd9c1c]{top:6px;transform:translate(0) rotate(135deg)}.VPNavBarHamburger.active:hover .top[data-v-e5dd9c1c],.VPNavBarHamburger.active:hover .middle[data-v-e5dd9c1c],.VPNavBarHamburger.active:hover .bottom[data-v-e5dd9c1c]{background-color:var(--vp-c-text-2);transition:top .25s,background-color .25s,transform .25s}.top[data-v-e5dd9c1c],.middle[data-v-e5dd9c1c],.bottom[data-v-e5dd9c1c]{position:absolute;width:16px;height:2px;background-color:var(--vp-c-text-1);transition:top .25s,background-color .5s,transform .25s}.top[data-v-e5dd9c1c]{top:0;left:0;transform:translate(0)}.middle[data-v-e5dd9c1c]{top:6px;left:0;transform:translate(8px)}.bottom[data-v-e5dd9c1c]{top:12px;left:0;transform:translate(4px)}.VPNavBar[data-v-94c81dcc]{position:relative;border-bottom:1px solid transparent;padding:0 8px 0 24px;height:var(--vp-nav-height);transition:border-color .5s,background-color .5s;pointer-events:none;white-space:nowrap}.VPNavBar.has-sidebar[data-v-94c81dcc]{border-bottom-color:var(--vp-c-gutter)}@media (min-width: 768px){.VPNavBar[data-v-94c81dcc]{padding:0 32px}}@media (min-width: 960px){.VPNavBar.has-sidebar[data-v-94c81dcc]{border-bottom-color:transparent;padding:0}.VPNavBar.fill[data-v-94c81dcc]:not(.has-sidebar){border-bottom-color:var(--vp-c-gutter);background-color:var(--vp-nav-bg-color)}}.container[data-v-94c81dcc]{display:flex;justify-content:space-between;margin:0 auto;max-width:calc(var(--vp-layout-max-width) - 64px);height:var(--vp-nav-height);pointer-events:none}.container>.title[data-v-94c81dcc],.container>.content[data-v-94c81dcc]{pointer-events:none}.container[data-v-94c81dcc] *{pointer-events:auto}@media (min-width: 960px){.VPNavBar.has-sidebar .container[data-v-94c81dcc]{max-width:100%}}.title[data-v-94c81dcc]{flex-shrink:0;height:calc(var(--vp-nav-height) - 1px);transition:background-color .5s}@media (min-width: 960px){.VPNavBar.has-sidebar .title[data-v-94c81dcc]{position:absolute;top:0;left:0;z-index:2;padding:0 32px;width:var(--vp-sidebar-width);height:var(--vp-nav-height);background-color:transparent}}@media (min-width: 1440px){.VPNavBar.has-sidebar .title[data-v-94c81dcc]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}.content[data-v-94c81dcc]{flex-grow:1}@media (min-width: 960px){.VPNavBar.has-sidebar .content[data-v-94c81dcc]{position:relative;z-index:1;padding-right:32px;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPNavBar.has-sidebar .content[data-v-94c81dcc]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2 + 32px);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.content-body[data-v-94c81dcc]{display:flex;justify-content:flex-end;align-items:center;height:calc(var(--vp-nav-height) - 1px);transition:background-color .5s}@media (min-width: 960px){.VPNavBar.has-sidebar .content-body[data-v-94c81dcc],.VPNavBar.fill .content-body[data-v-94c81dcc]{position:relative;background-color:var(--vp-nav-bg-color)}}.menu+.translations[data-v-94c81dcc]:before,.menu+.appearance[data-v-94c81dcc]:before,.menu+.social-links[data-v-94c81dcc]:before,.translations+.appearance[data-v-94c81dcc]:before,.appearance+.social-links[data-v-94c81dcc]:before{margin-right:8px;margin-left:8px;width:1px;height:24px;background-color:var(--vp-c-divider);content:""}.menu+.appearance[data-v-94c81dcc]:before,.translations+.appearance[data-v-94c81dcc]:before{margin-right:16px}.appearance+.social-links[data-v-94c81dcc]:before{margin-left:16px}.social-links[data-v-94c81dcc]{margin-right:-8px}@media (min-width: 960px){.VPNavBar.has-sidebar .curtain[data-v-94c81dcc]{position:absolute;right:0;bottom:-31px;width:calc(100% - var(--vp-sidebar-width));height:32px}.VPNavBar.has-sidebar .curtain[data-v-94c81dcc]:before{display:block;width:100%;height:32px;background:linear-gradient(var(--vp-c-bg),transparent 70%);content:""}}@media (min-width: 1440px){.VPNavBar.has-sidebar .curtain[data-v-94c81dcc]{width:calc(100% - ((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)))}}.VPNavScreenMenuLink[data-v-c328f34f]{display:block;border-bottom:1px solid var(--vp-c-divider);padding:12px 0 11px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:border-color .25s,color .25s}.VPNavScreenMenuLink[data-v-c328f34f]:hover{color:var(--vp-c-brand)}.VPNavScreenMenuGroupLink[data-v-3d20956d]{display:block;margin-left:12px;line-height:32px;font-size:14px;font-weight:400;color:var(--vp-c-text-1);transition:color .25s}.VPNavScreenMenuGroupLink[data-v-3d20956d]:hover{color:var(--vp-c-brand)}.VPNavScreenMenuGroupSection[data-v-7478538b]{display:block}.title[data-v-7478538b]{line-height:32px;font-size:13px;font-weight:700;color:var(--vp-c-text-2);transition:color .25s}.VPNavScreenMenuGroup[data-v-a9a19324]{border-bottom:1px solid var(--vp-c-divider);height:48px;overflow:hidden;transition:border-color .5s}.VPNavScreenMenuGroup .items[data-v-a9a19324]{visibility:hidden}.VPNavScreenMenuGroup.open .items[data-v-a9a19324]{visibility:visible}.VPNavScreenMenuGroup.open[data-v-a9a19324]{padding-bottom:10px;height:auto}.VPNavScreenMenuGroup.open .button[data-v-a9a19324]{padding-bottom:6px;color:var(--vp-c-brand)}.VPNavScreenMenuGroup.open .button-icon[data-v-a9a19324]{transform:rotate(45deg)}.button[data-v-a9a19324]{display:flex;justify-content:space-between;align-items:center;padding:12px 4px 11px 0;width:100%;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.button[data-v-a9a19324]:hover{color:var(--vp-c-brand)}.button-icon[data-v-a9a19324]{width:14px;height:14px;fill:var(--vp-c-text-2);transition:fill .5s,transform .25s}.group[data-v-a9a19324]:first-child{padding-top:0}.group+.group[data-v-a9a19324],.group+.item[data-v-a9a19324]{padding-top:4px}.VPNavScreenAppearance[data-v-add8f686]{display:flex;justify-content:space-between;align-items:center;border-radius:8px;padding:12px 14px 12px 16px;background-color:var(--vp-c-bg-soft)}.text[data-v-add8f686]{line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.VPNavScreenTranslations[data-v-d72aa483]{height:24px;overflow:hidden}.VPNavScreenTranslations.open[data-v-d72aa483]{height:auto}.title[data-v-d72aa483]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-text-1)}.icon[data-v-d72aa483]{width:16px;height:16px;fill:currentColor}.icon.lang[data-v-d72aa483]{margin-right:8px}.icon.chevron[data-v-d72aa483]{margin-left:4px}.list[data-v-d72aa483]{padding:4px 0 0 24px}.link[data-v-d72aa483]{line-height:32px;font-size:13px;color:var(--vp-c-text-1)}.VPNavScreen[data-v-724636ae]{position:fixed;top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 1px);right:0;bottom:0;left:0;padding:0 32px;width:100%;background-color:var(--vp-nav-screen-bg-color);overflow-y:auto;transition:background-color .5s;pointer-events:auto}.VPNavScreen.fade-enter-active[data-v-724636ae],.VPNavScreen.fade-leave-active[data-v-724636ae]{transition:opacity .25s}.VPNavScreen.fade-enter-active .container[data-v-724636ae],.VPNavScreen.fade-leave-active .container[data-v-724636ae]{transition:transform .25s ease}.VPNavScreen.fade-enter-from[data-v-724636ae],.VPNavScreen.fade-leave-to[data-v-724636ae]{opacity:0}.VPNavScreen.fade-enter-from .container[data-v-724636ae],.VPNavScreen.fade-leave-to .container[data-v-724636ae]{transform:translateY(-8px)}@media (min-width: 768px){.VPNavScreen[data-v-724636ae]{display:none}}.container[data-v-724636ae]{margin:0 auto;padding:24px 0 96px;max-width:288px}.menu+.translations[data-v-724636ae],.menu+.appearance[data-v-724636ae],.translations+.appearance[data-v-724636ae]{margin-top:24px}.menu+.social-links[data-v-724636ae]{margin-top:16px}.appearance+.social-links[data-v-724636ae]{margin-top:16px}.VPNav[data-v-7e5bc4a5]{position:relative;top:var(--vp-layout-top-height, 0px);left:0;z-index:var(--vp-z-index-nav);width:100%;pointer-events:none;transition:background-color .5s}@media (min-width: 960px){.VPNav[data-v-7e5bc4a5]{position:fixed}}.root[data-v-9a431c33]{position:relative;z-index:1}.nested[data-v-9a431c33]{padding-left:13px}.outline-link[data-v-9a431c33]{display:block;line-height:28px;color:var(--vp-c-text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .5s;font-weight:500}.outline-link[data-v-9a431c33]:hover,.outline-link.active[data-v-9a431c33]{color:var(--vp-c-text-1);transition:color .25s}.outline-link.nested[data-v-9a431c33]{padding-left:13px}.VPLocalNavOutlineDropdown[data-v-079b16a8]{padding:12px 20px 11px}.VPLocalNavOutlineDropdown button[data-v-079b16a8]{display:block;font-size:12px;font-weight:500;line-height:24px;color:var(--vp-c-text-2);transition:color .5s;position:relative}.VPLocalNavOutlineDropdown button[data-v-079b16a8]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPLocalNavOutlineDropdown button.open[data-v-079b16a8]{color:var(--vp-c-text-1)}.icon[data-v-079b16a8]{display:inline-block;vertical-align:middle;margin-left:2px;width:14px;height:14px;fill:currentColor}[data-v-079b16a8] .outline-link{font-size:14px;padding:2px 0}.open>.icon[data-v-079b16a8]{transform:rotate(90deg)}.items[data-v-079b16a8]{position:absolute;left:20px;right:20px;top:64px;background-color:var(--vp-local-nav-bg-color);padding:4px 10px 16px;border:1px solid var(--vp-c-divider);border-radius:8px;max-height:calc(var(--vp-vh, 100vh) - 86px);overflow:hidden auto;box-shadow:var(--vp-shadow-3)}.top-link[data-v-079b16a8]{display:block;color:var(--vp-c-brand);font-size:13px;font-weight:500;padding:6px 0;margin:0 13px 10px;border-bottom:1px solid var(--vp-c-divider)}.flyout-enter-active[data-v-079b16a8]{transition:all .2s ease-out}.flyout-leave-active[data-v-079b16a8]{transition:all .15s ease-in}.flyout-enter-from[data-v-079b16a8],.flyout-leave-to[data-v-079b16a8]{opacity:0;transform:translateY(-16px)}.VPLocalNav[data-v-f5a2ca58]{position:sticky;top:0;left:0;z-index:var(--vp-z-index-local-nav);display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--vp-c-gutter);padding-top:var(--vp-layout-top-height, 0px);width:100%;background-color:var(--vp-local-nav-bg-color);transition:border-color .5s,background-color .5s}@media (min-width: 960px){.VPLocalNav[data-v-f5a2ca58]{display:none}}.menu[data-v-f5a2ca58]{display:flex;align-items:center;padding:12px 24px 11px;line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.menu[data-v-f5a2ca58]:hover{color:var(--vp-c-text-1);transition:color .25s}@media (min-width: 768px){.menu[data-v-f5a2ca58]{padding:0 32px}}.menu-icon[data-v-f5a2ca58]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPOutlineDropdown[data-v-f5a2ca58]{padding:12px 24px 11px}@media (min-width: 768px){.VPOutlineDropdown[data-v-f5a2ca58]{padding:12px 32px 11px}}.VPSidebarItem.level-0[data-v-c4656e6d]{padding-bottom:24px}.VPSidebarItem.collapsed.level-0[data-v-c4656e6d]{padding-bottom:10px}.item[data-v-c4656e6d]{position:relative;display:flex;width:100%}.VPSidebarItem.collapsible>.item[data-v-c4656e6d]{cursor:pointer}.indicator[data-v-c4656e6d]{position:absolute;top:6px;bottom:6px;left:-17px;width:1px;transition:background-color .25s}.VPSidebarItem.level-2.is-active>.item>.indicator[data-v-c4656e6d],.VPSidebarItem.level-3.is-active>.item>.indicator[data-v-c4656e6d],.VPSidebarItem.level-4.is-active>.item>.indicator[data-v-c4656e6d],.VPSidebarItem.level-5.is-active>.item>.indicator[data-v-c4656e6d]{background-color:var(--vp-c-brand)}.link[data-v-c4656e6d]{display:flex;align-items:center;flex-grow:1}.text[data-v-c4656e6d]{flex-grow:1;padding:4px 0;line-height:24px;font-size:14px;transition:color .25s}.VPSidebarItem.level-0 .text[data-v-c4656e6d]{font-weight:700;color:var(--vp-c-text-1)}.VPSidebarItem.level-1 .text[data-v-c4656e6d],.VPSidebarItem.level-2 .text[data-v-c4656e6d],.VPSidebarItem.level-3 .text[data-v-c4656e6d],.VPSidebarItem.level-4 .text[data-v-c4656e6d],.VPSidebarItem.level-5 .text[data-v-c4656e6d]{font-weight:500;color:var(--vp-c-text-2)}.VPSidebarItem.level-0.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-1.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-2.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-3.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-4.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-5.is-link>.item>.link:hover .text[data-v-c4656e6d]{color:var(--vp-c-brand)}.VPSidebarItem.level-0.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-1.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-2.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-3.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-4.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-5.has-active>.item>.link>.text[data-v-c4656e6d]{color:var(--vp-c-text-1)}.VPSidebarItem.level-0.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-1.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-2.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-3.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-4.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-5.is-active>.item .link>.text[data-v-c4656e6d]{color:var(--vp-c-brand)}.caret[data-v-c4656e6d]{display:flex;justify-content:center;align-items:center;margin-right:-7px;width:32px;height:32px;color:var(--vp-c-text-3);cursor:pointer;transition:color .25s}.item:hover .caret[data-v-c4656e6d]{color:var(--vp-c-text-2)}.item:hover .caret[data-v-c4656e6d]:hover{color:var(--vp-c-text-1)}.caret-icon[data-v-c4656e6d]{width:18px;height:18px;fill:currentColor;transform:rotate(90deg);transition:transform .25s}.VPSidebarItem.collapsed .caret-icon[data-v-c4656e6d]{transform:rotate(0)}.VPSidebarItem.level-1 .items[data-v-c4656e6d],.VPSidebarItem.level-2 .items[data-v-c4656e6d],.VPSidebarItem.level-3 .items[data-v-c4656e6d],.VPSidebarItem.level-4 .items[data-v-c4656e6d],.VPSidebarItem.level-5 .items[data-v-c4656e6d]{border-left:1px solid var(--vp-c-divider);padding-left:16px}.VPSidebarItem.collapsed .items[data-v-c4656e6d]{display:none}.VPSidebar[data-v-af16598e]{position:fixed;top:var(--vp-layout-top-height, 0px);bottom:0;left:0;z-index:var(--vp-z-index-sidebar);padding:32px 32px 96px;width:calc(100vw - 64px);max-width:320px;background-color:var(--vp-sidebar-bg-color);opacity:0;box-shadow:var(--vp-c-shadow-3);overflow-x:hidden;overflow-y:auto;transform:translate(-100%);transition:opacity .5s,transform .25s ease;overscroll-behavior:contain}.VPSidebar.open[data-v-af16598e]{opacity:1;visibility:visible;transform:translate(0);transition:opacity .25s,transform .5s cubic-bezier(.19,1,.22,1)}.dark .VPSidebar[data-v-af16598e]{box-shadow:var(--vp-shadow-1)}@media (min-width: 960px){.VPSidebar[data-v-af16598e]{z-index:1;padding-top:var(--vp-nav-height);padding-bottom:128px;width:var(--vp-sidebar-width);max-width:100%;background-color:var(--vp-sidebar-bg-color);opacity:1;visibility:visible;box-shadow:none;transform:translate(0)}}@media (min-width: 1440px){.VPSidebar[data-v-af16598e]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}@media (min-width: 960px){.curtain[data-v-af16598e]{position:sticky;top:-64px;left:0;z-index:1;margin-top:calc(var(--vp-nav-height) * -1);margin-right:-32px;margin-left:-32px;height:var(--vp-nav-height);background-color:var(--vp-sidebar-bg-color)}}.nav[data-v-af16598e]{outline:0}.group+.group[data-v-af16598e]{border-top:1px solid var(--vp-c-divider);padding-top:10px}@media (min-width: 960px){.group[data-v-af16598e]{padding-top:10px;width:calc(var(--vp-sidebar-width) - 64px)}}.VPButton[data-v-567ba664]{display:inline-block;border:1px solid transparent;text-align:center;font-weight:600;white-space:nowrap;transition:color .25s,border-color .25s,background-color .25s}.VPButton[data-v-567ba664]:active{transition:color .1s,border-color .1s,background-color .1s}.VPButton.medium[data-v-567ba664]{border-radius:20px;padding:0 20px;line-height:38px;font-size:14px}.VPButton.big[data-v-567ba664]{border-radius:24px;padding:0 24px;line-height:46px;font-size:16px}.VPButton.brand[data-v-567ba664]{border-color:var(--vp-button-brand-border);color:var(--vp-button-brand-text);background-color:var(--vp-button-brand-bg)}.VPButton.brand[data-v-567ba664]:hover{border-color:var(--vp-button-brand-hover-border);color:var(--vp-button-brand-hover-text);background-color:var(--vp-button-brand-hover-bg)}.VPButton.brand[data-v-567ba664]:active{border-color:var(--vp-button-brand-active-border);color:var(--vp-button-brand-active-text);background-color:var(--vp-button-brand-active-bg)}.VPButton.alt[data-v-567ba664]{border-color:var(--vp-button-alt-border);color:var(--vp-button-alt-text);background-color:var(--vp-button-alt-bg)}.VPButton.alt[data-v-567ba664]:hover{border-color:var(--vp-button-alt-hover-border);color:var(--vp-button-alt-hover-text);background-color:var(--vp-button-alt-hover-bg)}.VPButton.alt[data-v-567ba664]:active{border-color:var(--vp-button-alt-active-border);color:var(--vp-button-alt-active-text);background-color:var(--vp-button-alt-active-bg)}.VPButton.sponsor[data-v-567ba664]{border-color:var(--vp-button-sponsor-border);color:var(--vp-button-sponsor-text);background-color:var(--vp-button-sponsor-bg)}.VPButton.sponsor[data-v-567ba664]:hover{border-color:var(--vp-button-sponsor-hover-border);color:var(--vp-button-sponsor-hover-text);background-color:var(--vp-button-sponsor-hover-bg)}.VPButton.sponsor[data-v-567ba664]:active{border-color:var(--vp-button-sponsor-active-border);color:var(--vp-button-sponsor-active-text);background-color:var(--vp-button-sponsor-active-bg)}.VPHero[data-v-fd2650d5]{margin-top:calc((var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1);padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px) 24px 48px}@media (min-width: 640px){.VPHero[data-v-fd2650d5]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 48px 64px}}@media (min-width: 960px){.VPHero[data-v-fd2650d5]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 64px 64px}}.container[data-v-fd2650d5]{display:flex;flex-direction:column;margin:0 auto;max-width:1152px}@media (min-width: 960px){.container[data-v-fd2650d5]{flex-direction:row}}.main[data-v-fd2650d5]{position:relative;z-index:10;order:2;flex-grow:1;flex-shrink:0}.VPHero.has-image .container[data-v-fd2650d5]{text-align:center}@media (min-width: 960px){.VPHero.has-image .container[data-v-fd2650d5]{text-align:left}}@media (min-width: 960px){.main[data-v-fd2650d5]{order:1;width:calc((100% / 3) * 2)}.VPHero.has-image .main[data-v-fd2650d5]{max-width:592px}}.name[data-v-fd2650d5],.text[data-v-fd2650d5]{max-width:392px;letter-spacing:-.4px;line-height:40px;font-size:32px;font-weight:700;white-space:pre-wrap}.VPHero.has-image .name[data-v-fd2650d5],.VPHero.has-image .text[data-v-fd2650d5]{margin:0 auto}.name[data-v-fd2650d5]{color:var(--vp-home-hero-name-color)}.clip[data-v-fd2650d5]{background:var(--vp-home-hero-name-background);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:var(--vp-home-hero-name-color)}@media (min-width: 640px){.name[data-v-fd2650d5],.text[data-v-fd2650d5]{max-width:576px;line-height:56px;font-size:48px}}@media (min-width: 960px){.name[data-v-fd2650d5],.text[data-v-fd2650d5]{line-height:64px;font-size:56px}.VPHero.has-image .name[data-v-fd2650d5],.VPHero.has-image .text[data-v-fd2650d5]{margin:0}}.tagline[data-v-fd2650d5]{padding-top:8px;max-width:392px;line-height:28px;font-size:18px;font-weight:500;white-space:pre-wrap;color:var(--vp-c-text-2)}.VPHero.has-image .tagline[data-v-fd2650d5]{margin:0 auto}@media (min-width: 640px){.tagline[data-v-fd2650d5]{padding-top:12px;max-width:576px;line-height:32px;font-size:20px}}@media (min-width: 960px){.tagline[data-v-fd2650d5]{line-height:36px;font-size:24px}.VPHero.has-image .tagline[data-v-fd2650d5]{margin:0}}.actions[data-v-fd2650d5]{display:flex;flex-wrap:wrap;margin:-6px;padding-top:24px}.VPHero.has-image .actions[data-v-fd2650d5]{justify-content:center}@media (min-width: 640px){.actions[data-v-fd2650d5]{padding-top:32px}}@media (min-width: 960px){.VPHero.has-image .actions[data-v-fd2650d5]{justify-content:flex-start}}.action[data-v-fd2650d5]{flex-shrink:0;padding:6px}.image[data-v-fd2650d5]{order:1;margin:-76px -24px -48px}@media (min-width: 640px){.image[data-v-fd2650d5]{margin:-108px -24px -48px}}@media (min-width: 960px){.image[data-v-fd2650d5]{flex-grow:1;order:2;margin:0;min-height:100%}}.image-container[data-v-fd2650d5]{position:relative;margin:0 auto;width:320px;height:320px}@media (min-width: 640px){.image-container[data-v-fd2650d5]{width:392px;height:392px}}@media (min-width: 960px){.image-container[data-v-fd2650d5]{display:flex;justify-content:center;align-items:center;width:100%;height:100%;transform:translate(-32px,-32px)}}.image-bg[data-v-fd2650d5]{position:absolute;top:50%;left:50%;border-radius:50%;width:192px;height:192px;background-image:var(--vp-home-hero-image-background-image);filter:var(--vp-home-hero-image-filter);transform:translate(-50%,-50%)}@media (min-width: 640px){.image-bg[data-v-fd2650d5]{width:256px;height:256px}}@media (min-width: 960px){.image-bg[data-v-fd2650d5]{width:320px;height:320px}}[data-v-fd2650d5] .image-src{position:absolute;top:50%;left:50%;max-width:192px;max-height:192px;transform:translate(-50%,-50%)}@media (min-width: 640px){[data-v-fd2650d5] .image-src{max-width:256px;max-height:256px}}@media (min-width: 960px){[data-v-fd2650d5] .image-src{max-width:320px;max-height:320px}}.VPFeature[data-v-837f6cca]{display:block;border:1px solid var(--vp-c-bg-soft);border-radius:12px;height:100%;background-color:var(--vp-c-bg-soft);transition:border-color .25s,background-color .25s}.VPFeature.link[data-v-837f6cca]:hover{border-color:var(--vp-c-brand);background-color:var(--vp-c-bg-soft-up)}.box[data-v-837f6cca]{display:flex;flex-direction:column;padding:24px;height:100%}.VPFeature[data-v-837f6cca] .VPImage{width:48px;height:48px;margin-bottom:20px}.icon[data-v-837f6cca]{display:flex;justify-content:center;align-items:center;margin-bottom:20px;border-radius:6px;background-color:var(--vp-c-bg-soft-down);width:48px;height:48px;font-size:24px;transition:background-color .25s}.title[data-v-837f6cca]{line-height:24px;font-size:16px;font-weight:600}.details[data-v-837f6cca]{flex-grow:1;padding-top:8px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.link-text[data-v-837f6cca]{padding-top:8px}.link-text-value[data-v-837f6cca]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-brand)}.link-text-icon[data-v-837f6cca]{display:inline-block;margin-left:6px;width:14px;height:14px;fill:currentColor}.VPFeatures[data-v-6816157f]{position:relative;padding:0 24px}@media (min-width: 640px){.VPFeatures[data-v-6816157f]{padding:0 48px}}@media (min-width: 960px){.VPFeatures[data-v-6816157f]{padding:0 64px}}.container[data-v-6816157f]{margin:0 auto;max-width:1152px}.items[data-v-6816157f]{display:flex;flex-wrap:wrap;margin:-8px}.item[data-v-6816157f]{padding:8px;width:100%}@media (min-width: 640px){.item.grid-2[data-v-6816157f],.item.grid-4[data-v-6816157f],.item.grid-6[data-v-6816157f]{width:50%}}@media (min-width: 768px){.item.grid-2[data-v-6816157f],.item.grid-4[data-v-6816157f]{width:50%}.item.grid-3[data-v-6816157f],.item.grid-6[data-v-6816157f]{width:calc(100% / 3)}}@media (min-width: 960px){.item.grid-4[data-v-6816157f]{width:25%}}.VPHome[data-v-d82743a8]{padding-bottom:96px}.VPHome[data-v-d82743a8] .VPHomeSponsors{margin-top:112px;margin-bottom:-128px}@media (min-width: 768px){.VPHome[data-v-d82743a8]{padding-bottom:128px}}.VPDocAsideOutline[data-v-ff0f39c8]{display:none}.VPDocAsideOutline.has-outline[data-v-ff0f39c8]{display:block}.content[data-v-ff0f39c8]{position:relative;border-left:1px solid var(--vp-c-divider);padding-left:16px;font-size:13px;font-weight:500}.outline-marker[data-v-ff0f39c8]{position:absolute;top:32px;left:-1px;z-index:0;opacity:0;width:1px;height:18px;background-color:var(--vp-c-brand);transition:top .25s cubic-bezier(0,1,.5,1),background-color .5s,opacity .25s}.outline-title[data-v-ff0f39c8]{letter-spacing:.4px;line-height:28px;font-size:13px;font-weight:600}.VPDocAside[data-v-3f215769]{display:flex;flex-direction:column;flex-grow:1}.spacer[data-v-3f215769]{flex-grow:1}.VPDocAside[data-v-3f215769] .spacer+.VPDocAsideSponsors,.VPDocAside[data-v-3f215769] .spacer+.VPDocAsideCarbonAds{margin-top:24px}.VPDocAside[data-v-3f215769] .VPDocAsideSponsors+.VPDocAsideCarbonAds{margin-top:16px}.VPLastUpdated[data-v-7b3ebfe1]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 640px){.VPLastUpdated[data-v-7b3ebfe1]{line-height:32px;font-size:14px;font-weight:500}}.VPDocFooter[data-v-face870a]{margin-top:64px}.edit-info[data-v-face870a]{padding-bottom:18px}@media (min-width: 640px){.edit-info[data-v-face870a]{display:flex;justify-content:space-between;align-items:center;padding-bottom:14px}}.edit-link-button[data-v-face870a]{display:flex;align-items:center;border:0;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-brand);transition:color .25s}.edit-link-button[data-v-face870a]:hover{color:var(--vp-c-brand-dark)}.edit-link-icon[data-v-face870a]{margin-right:8px;width:14px;height:14px;fill:currentColor}.prev-next[data-v-face870a]{border-top:1px solid var(--vp-c-divider);padding-top:24px}@media (min-width: 640px){.prev-next[data-v-face870a]{display:flex}}.pager.has-prev[data-v-face870a]{padding-top:8px}@media (min-width: 640px){.pager[data-v-face870a]{display:flex;flex-direction:column;flex-shrink:0;width:50%}.pager.has-prev[data-v-face870a]{padding-top:0;padding-left:16px}}.pager-link[data-v-face870a]{display:block;border:1px solid var(--vp-c-divider);border-radius:8px;padding:11px 16px 13px;width:100%;height:100%;transition:border-color .25s}.pager-link[data-v-face870a]:hover{border-color:var(--vp-c-brand)}.pager-link.next[data-v-face870a]{margin-left:auto;text-align:right}.desc[data-v-face870a]{display:block;line-height:20px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.title[data-v-face870a]{display:block;line-height:20px;font-size:14px;font-weight:500;color:var(--vp-c-brand);transition:color .25s}.VPDocOutlineDropdown[data-v-2edece88]{margin-bottom:42px}.VPDocOutlineDropdown button[data-v-2edece88]{display:block;font-size:14px;font-weight:500;line-height:24px;color:var(--vp-c-text-2);transition:color .5s;border:1px solid var(--vp-c-border);padding:4px 12px;border-radius:8px}.VPDocOutlineDropdown button[data-v-2edece88]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPDocOutlineDropdown button.open[data-v-2edece88]{color:var(--vp-c-text-1)}.icon[data-v-2edece88]{display:inline-block;vertical-align:middle;margin-left:2px;width:14px;height:14px;fill:currentColor}[data-v-2edece88] .outline-link{font-size:13px}.open>.icon[data-v-2edece88]{transform:rotate(90deg)}.items[data-v-2edece88]{margin-top:10px;border-left:1px solid var(--vp-c-divider)}.VPDoc[data-v-c4b0d3cf]{padding:32px 24px 96px;width:100%}.VPDoc .VPDocOutlineDropdown[data-v-c4b0d3cf]{display:none}@media (min-width: 960px) and (max-width: 1280px){.VPDoc .VPDocOutlineDropdown[data-v-c4b0d3cf]{display:block}}@media (min-width: 768px){.VPDoc[data-v-c4b0d3cf]{padding:48px 32px 128px}}@media (min-width: 960px){.VPDoc[data-v-c4b0d3cf]{padding:32px 32px 0}.VPDoc:not(.has-sidebar) .container[data-v-c4b0d3cf]{display:flex;justify-content:center;max-width:992px}.VPDoc:not(.has-sidebar) .content[data-v-c4b0d3cf]{max-width:752px}}@media (min-width: 1280px){.VPDoc .container[data-v-c4b0d3cf]{display:flex;justify-content:center}.VPDoc .aside[data-v-c4b0d3cf]{display:block}}@media (min-width: 1440px){.VPDoc:not(.has-sidebar) .content[data-v-c4b0d3cf]{max-width:784px}.VPDoc:not(.has-sidebar) .container[data-v-c4b0d3cf]{max-width:1104px}}.container[data-v-c4b0d3cf]{margin:0 auto;width:100%}.aside[data-v-c4b0d3cf]{position:relative;display:none;order:2;flex-grow:1;padding-left:32px;width:100%;max-width:256px}.left-aside[data-v-c4b0d3cf]{order:1;padding-left:unset;padding-right:32px}.aside-container[data-v-c4b0d3cf]{position:fixed;top:0;padding-top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-doc-top-height, 0px) + 32px);width:224px;height:100vh;overflow-x:hidden;overflow-y:auto;scrollbar-width:none}.aside-container[data-v-c4b0d3cf]::-webkit-scrollbar{display:none}.aside-curtain[data-v-c4b0d3cf]{position:fixed;bottom:0;z-index:10;width:224px;height:32px;background:linear-gradient(transparent,var(--vp-c-bg) 70%)}.aside-content[data-v-c4b0d3cf]{display:flex;flex-direction:column;min-height:calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px));padding-bottom:32px}.content[data-v-c4b0d3cf]{position:relative;margin:0 auto;width:100%}@media (min-width: 960px){.content[data-v-c4b0d3cf]{padding:0 32px 128px}}@media (min-width: 1280px){.content[data-v-c4b0d3cf]{order:1;margin:0;min-width:640px}}.content-container[data-v-c4b0d3cf]{margin:0 auto}.VPDoc.has-aside .content-container[data-v-c4b0d3cf]{max-width:688px}.NotFound[data-v-c70503b8]{padding:64px 24px 96px;text-align:center}@media (min-width: 768px){.NotFound[data-v-c70503b8]{padding:96px 32px 168px}}.code[data-v-c70503b8]{line-height:64px;font-size:64px;font-weight:600}.title[data-v-c70503b8]{padding-top:12px;letter-spacing:2px;line-height:20px;font-size:20px;font-weight:700}.divider[data-v-c70503b8]{margin:24px auto 18px;width:64px;height:1px;background-color:var(--vp-c-divider)}.quote[data-v-c70503b8]{margin:0 auto;max-width:256px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.action[data-v-c70503b8]{padding-top:20px}.link[data-v-c70503b8]{display:inline-block;border:1px solid var(--vp-c-brand);border-radius:16px;padding:3px 16px;font-size:14px;font-weight:500;color:var(--vp-c-brand);transition:border-color .25s,color .25s}.link[data-v-c70503b8]:hover{border-color:var(--vp-c-brand-dark);color:var(--vp-c-brand-dark)}.VPContent[data-v-a494bd1d]{flex-grow:1;flex-shrink:0;margin:var(--vp-layout-top-height, 0px) auto 0;width:100%}.VPContent.is-home[data-v-a494bd1d]{width:100%;max-width:100%}.VPContent.has-sidebar[data-v-a494bd1d]{margin:0}@media (min-width: 960px){.VPContent[data-v-a494bd1d]{padding-top:var(--vp-nav-height)}.VPContent.has-sidebar[data-v-a494bd1d]{margin:var(--vp-layout-top-height, 0px) 0 0;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPContent.has-sidebar[data-v-a494bd1d]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.VPFooter[data-v-2f86ebd2]{position:relative;z-index:var(--vp-z-index-footer);border-top:1px solid var(--vp-c-gutter);padding:32px 24px;background-color:var(--vp-c-bg)}.VPFooter.has-sidebar[data-v-2f86ebd2]{display:none}@media (min-width: 768px){.VPFooter[data-v-2f86ebd2]{padding:32px}}.container[data-v-2f86ebd2]{margin:0 auto;max-width:var(--vp-layout-max-width);text-align:center}.message[data-v-2f86ebd2],.copyright[data-v-2f86ebd2]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.message[data-v-2f86ebd2]{order:2}.copyright[data-v-2f86ebd2]{order:1}.Layout[data-v-b2cf3e0b]{display:flex;flex-direction:column;min-height:100vh}.VPHomeSponsors[data-v-3c6e61c2]{border-top:1px solid var(--vp-c-gutter);padding:88px 24px 96px;background-color:var(--vp-c-bg)}.container[data-v-3c6e61c2]{margin:0 auto;max-width:1152px}.love[data-v-3c6e61c2]{margin:0 auto;width:28px;height:28px;color:var(--vp-c-text-3)}.icon[data-v-3c6e61c2]{width:28px;height:28px;fill:currentColor}.message[data-v-3c6e61c2]{margin:0 auto;padding-top:10px;max-width:320px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.sponsors[data-v-3c6e61c2]{padding-top:32px}.action[data-v-3c6e61c2]{padding-top:40px;text-align:center}.VPTeamPage[data-v-10b00018]{padding-bottom:96px}@media (min-width: 768px){.VPTeamPage[data-v-10b00018]{padding-bottom:128px}}.VPTeamPageSection+.VPTeamPageSection[data-v-10b00018-s],.VPTeamMembers+.VPTeamPageSection[data-v-10b00018-s]{margin-top:64px}.VPTeamMembers+.VPTeamMembers[data-v-10b00018-s]{margin-top:24px}@media (min-width: 768px){.VPTeamPageTitle+.VPTeamPageSection[data-v-10b00018-s]{margin-top:16px}.VPTeamPageSection+.VPTeamPageSection[data-v-10b00018-s],.VPTeamMembers+.VPTeamPageSection[data-v-10b00018-s]{margin-top:96px}}.VPTeamMembers[data-v-10b00018-s]{padding:0 24px}@media (min-width: 768px){.VPTeamMembers[data-v-10b00018-s]{padding:0 48px}}@media (min-width: 960px){.VPTeamMembers[data-v-10b00018-s]{padding:0 64px}}.VPTeamPageTitle[data-v-bf2cbdac]{padding:48px 32px;text-align:center}@media (min-width: 768px){.VPTeamPageTitle[data-v-bf2cbdac]{padding:64px 48px 48px}}@media (min-width: 960px){.VPTeamPageTitle[data-v-bf2cbdac]{padding:80px 64px 48px}}.title[data-v-bf2cbdac]{letter-spacing:0;line-height:44px;font-size:36px;font-weight:500}@media (min-width: 768px){.title[data-v-bf2cbdac]{letter-spacing:-.5px;line-height:56px;font-size:48px}}.lead[data-v-bf2cbdac]{margin:0 auto;max-width:512px;padding-top:12px;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 768px){.lead[data-v-bf2cbdac]{max-width:592px;letter-spacing:.15px;line-height:28px;font-size:20px}}.VPTeamPageSection[data-v-b1a88750]{padding:0 32px}@media (min-width: 768px){.VPTeamPageSection[data-v-b1a88750]{padding:0 48px}}@media (min-width: 960px){.VPTeamPageSection[data-v-b1a88750]{padding:0 64px}}.title[data-v-b1a88750]{position:relative;margin:0 auto;max-width:1152px;text-align:center;color:var(--vp-c-text-2)}.title-line[data-v-b1a88750]{position:absolute;top:16px;left:0;width:100%;height:1px;background-color:var(--vp-c-divider)}.title-text[data-v-b1a88750]{position:relative;display:inline-block;padding:0 24px;letter-spacing:0;line-height:32px;font-size:20px;font-weight:500;background-color:var(--vp-c-bg)}.lead[data-v-b1a88750]{margin:0 auto;max-width:480px;padding-top:12px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.members[data-v-b1a88750]{padding-top:40px}.VPTeamMembersItem[data-v-1739ab5f]{display:flex;flex-direction:column;gap:2px;border-radius:12px;width:100%;height:100%;overflow:hidden}.VPTeamMembersItem.small .profile[data-v-1739ab5f]{padding:32px}.VPTeamMembersItem.small .data[data-v-1739ab5f]{padding-top:20px}.VPTeamMembersItem.small .avatar[data-v-1739ab5f]{width:64px;height:64px}.VPTeamMembersItem.small .name[data-v-1739ab5f]{line-height:24px;font-size:16px}.VPTeamMembersItem.small .affiliation[data-v-1739ab5f]{padding-top:4px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .desc[data-v-1739ab5f]{padding-top:12px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .links[data-v-1739ab5f]{margin:0 -16px -20px;padding:10px 0 0}.VPTeamMembersItem.medium .profile[data-v-1739ab5f]{padding:48px 32px}.VPTeamMembersItem.medium .data[data-v-1739ab5f]{padding-top:24px;text-align:center}.VPTeamMembersItem.medium .avatar[data-v-1739ab5f]{width:96px;height:96px}.VPTeamMembersItem.medium .name[data-v-1739ab5f]{letter-spacing:.15px;line-height:28px;font-size:20px}.VPTeamMembersItem.medium .affiliation[data-v-1739ab5f]{padding-top:4px;font-size:16px}.VPTeamMembersItem.medium .desc[data-v-1739ab5f]{padding-top:16px;max-width:288px;font-size:16px}.VPTeamMembersItem.medium .links[data-v-1739ab5f]{margin:0 -16px -12px;padding:16px 12px 0}.profile[data-v-1739ab5f]{flex-grow:1;background-color:var(--vp-c-bg-soft)}.data[data-v-1739ab5f]{text-align:center}.avatar[data-v-1739ab5f]{position:relative;flex-shrink:0;margin:0 auto;border-radius:50%;box-shadow:var(--vp-shadow-3)}.avatar-img[data-v-1739ab5f]{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:50%;object-fit:cover}.name[data-v-1739ab5f]{margin:0;font-weight:600}.affiliation[data-v-1739ab5f]{margin:0;font-weight:500;color:var(--vp-c-text-2)}.org.link[data-v-1739ab5f]{color:var(--vp-c-text-2);transition:color .25s}.org.link[data-v-1739ab5f]:hover{color:var(--vp-c-brand)}.desc[data-v-1739ab5f]{margin:0 auto}.links[data-v-1739ab5f]{display:flex;justify-content:center;height:56px}.sp-link[data-v-1739ab5f]{display:flex;justify-content:center;align-items:center;text-align:center;padding:16px;font-size:14px;font-weight:500;color:var(--vp-c-sponsor);background-color:var(--vp-c-bg-soft);transition:color .25s,background-color .25s}.sp .sp-link.link[data-v-1739ab5f]:hover,.sp .sp-link.link[data-v-1739ab5f]:focus{outline:none;color:var(--vp-c-white);background-color:var(--vp-c-sponsor)}.sp-icon[data-v-1739ab5f]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPTeamMembers.small .container[data-v-04685dce]{grid-template-columns:repeat(auto-fit,minmax(224px,1fr))}.VPTeamMembers.small.count-1 .container[data-v-04685dce]{max-width:276px}.VPTeamMembers.small.count-2 .container[data-v-04685dce]{max-width:576px}.VPTeamMembers.small.count-3 .container[data-v-04685dce]{max-width:876px}.VPTeamMembers.medium .container[data-v-04685dce]{grid-template-columns:repeat(auto-fit,minmax(256px,1fr))}@media (min-width: 375px){.VPTeamMembers.medium .container[data-v-04685dce]{grid-template-columns:repeat(auto-fit,minmax(288px,1fr))}}.VPTeamMembers.medium.count-1 .container[data-v-04685dce]{max-width:368px}.VPTeamMembers.medium.count-2 .container[data-v-04685dce]{max-width:760px}.container[data-v-04685dce]{display:grid;gap:24px;margin:0 auto;max-width:1152px} diff --git a/assets/wechat-mp-qrcode.f64c0e73.jpg b/assets/wechat-mp-qrcode.f64c0e73.jpg new file mode 100644 index 0000000..4801bc4 Binary files /dev/null and b/assets/wechat-mp-qrcode.f64c0e73.jpg differ diff --git a/assets/wechat-pay-qrcode.87383472.jpg b/assets/wechat-pay-qrcode.87383472.jpg new file mode 100644 index 0000000..f7063f1 Binary files /dev/null and b/assets/wechat-pay-qrcode.87383472.jpg differ diff --git a/book.json b/book.json deleted file mode 100644 index 70c08cc..0000000 --- a/book.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "gitbook": ">=2.0.0", - "plugins": [ - "mathjax-pro", - "todo", - "betterchinese", - "ga" - ], - "pluginsConfig": { - "ga": { - "token": "UA-51073473-3" - } - } -} \ No newline at end of file diff --git a/ch01_environment.html b/ch01_environment.html new file mode 100644 index 0000000..e9ccaa2 --- /dev/null +++ b/ch01_environment.html @@ -0,0 +1,35 @@ + + + + + + 环境 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page

环境

JavaScript

JavaScript是目前使用最广泛的编程语言之一。作为互联网和移动计算时代的基础支撑语言,从基本的生产力工具,到日常的泛娱乐平台,它活跃在每一次你浏览网站、使用App的过程中,提供基本的运算和流程保障。

JavaScript的诞生和发展也颇具传奇色彩,Brendan Eich在几天的时间内设计并实现了这门编程语言,影响了后面几十年的软件和技术的发展。如今几乎每一个设备都接入了JavaScript,深入到了我们生活的方方面面。作为一门工业级的编程语言,JavaScript由欧洲计算机制造商协会(ECMA)制定标准化的行为和定义,来规范这个语言的使用和发展方向。TC39技术委员会也一直在更新JavaScript的功能,让这个有二十多年历史的旧技术不断焕发新的活力。

JavaScript的环境触手可及。在电脑端你可以随时打开一个现代的浏览器,页面上右键菜单中选择“Inspect”或者“查看/检查元素”,选择“控制台(Console)”标签。如果你是在移动端或者浏览器不支持,也可以通过一些工具比如移动端Console来体验JavaScript控制台。

编写代码

当需要写大量代码组合使用的时候,你可以通过本地编辑Web页面的方式来编写JavaScript代码。JavaScript是Web端的基本编程语言,可以稳定地运行在大多数现代浏览器中。

通过文件编辑器创建下面的代码,命名为index.html

html
<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+    <meta charset="UTF-8">
+    <title>Introduction to Programming</title>
+</head>
+<body>
+    <script type="text/javascript">
+        console.log("hello world");
+    </script>
+</body>
+</html>

<script ...></script>之间,就是你编写JavaScript代码的地方。编写完成后,用浏览器打开(或者刷新)这个文件,就能看到对应的效果和执行内容。

比如上面的代码,就是在控制台打印“hello world”。

本书推荐使用Visual Studio Code作为基本的代码编辑工具。你可以在官网进行下载和安装。官网也有详细的使用教程。

MDN 中对浏览器开发者工具基本的工具软件有更为完整的介绍,可以参考。如果你想了解更多关于Web设计和开发的知识,MDN也有完备的文档供参考。

一切准备就绪的话,我们就开始吧。

+ + + + \ No newline at end of file diff --git a/ch02_computation.html b/ch02_computation.html new file mode 100644 index 0000000..1ed5300 --- /dev/null +++ b/ch02_computation.html @@ -0,0 +1,36 @@ + + + + + + 计算 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page

计算

编程语言是人与计算机打交道的一个重要工具。

计算机(Computer)是干嘛的?当然是计算(Computing)啦。

在浏览器的控制台输入以下代码:

javascript
1 + 1

是不是有看到浏览器在回应你:2。这里基本上是能够当成一个计算器用的。

没错,这就是计算嘛。

然而,有什么用呢?

计算

简(pian)单(mian)地讲,一切问题都能抽出来一套数学模型,同样能够抽出来的数学模型都可以转化成计算问题,然后让计算机来解决。

所以计算可以看成是整个程序设计的基础。同样像我们之前提到的,计算机,本身也就是拿来做计算的。

那么我们都可以做哪些计算呢?

当然普通的不用说,你还可以把一些复杂的计算规则定义成函数:

javascript
function pow5(num) {
+    return num * num * num * num * num;
+}

比如pow5的意思就是求一个数的5次方。我们定义了一个函数,怎么用呢?

然后尝试下面的代码:

javascript
pow5(1)

嗯,没错,输出了我们期望的结果(1)。

组合

什么是组合呢?

想想我们学习函数和多项式的时候,

然后我们要定义一个,那么定义出来的长什么样呢?

我们首先要把代入

好吧我编不下去了。总之展开来以后很丑的。

而且如果以后遇到组合的,或者是更多参数的函数组合,结果更是可想而知。

但是在JavaScript里面我们并不用担心这个问题。

比如我们有两个函数

javascript
function f(x) {
+    return x*x + 2*x + 1;
+}
+
+function g(x) {
+    return (x+3) * (x-5);
+}

那么我们的可以很简单的写成这个样子:

javascript
function h(x) {
+    return f(g(x));
+}

或者你也可以不用专门去定义,在用到的时候直接用就可以:

javascript
f(g(5))
+// or
+h(5)

组合的意义

比如我们可以考虑一个更复杂的函数,

分别为 的时候。你可以试着自己去展开一下这个算式:

总之,无论他有多长,都会在算式里面充斥着,而且,计算机并没有那么聪明,所以只好按照特定的顺序一次次的计算。

于是被重复计算了三次,被重复计算了两次。对于pow5那种情况,更是会被求值5次。

令人欣慰的是,组合帮我们保证了这个问题不会有这些重复。

当你计算pow5(3+1)的时候,JavaScript并不会去展开成(3+1)*(3+1)...*(3+1)这种形式,而是直接变成了pow5(4),最后的结果是1024。

也就是说,函数的每个自变量(编程语言里面我们会把他们叫做参数(argument))会先被求值,得到他们的值以后才会带入计算。

*请记住我们现在讨论的这个问题,后面会展开并且深入地探讨。

练习

  • 请思考,如果函数的参数在代入函数之前并不进行强制求值,pow5(pow5(pow5(pow5(5))))展开后共计算多少次乘法。
  • 强制求值可能会造成的问题有哪些。
+ + + + \ No newline at end of file diff --git a/ch03_procedure.html b/ch03_procedure.html new file mode 100644 index 0000000..89b0c96 --- /dev/null +++ b/ch03_procedure.html @@ -0,0 +1,83 @@ + + + + + + 过程 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page

过程

什么是过程呢?

凭经验我们就能知道,实际的问题并不是像理想中那么简单,不是一个表达式就能描述的清楚的,而且也并不一定能通过组合来直接解决。

甚至是简单的数学问题,也不能用他们来解决。比如:

求绝对值,或者

求积。

其中一个需要我们根据参数的值,来选择不同的计算方式,返回不同的结果;另外一个则是通过对特定的表达式做一个累乘(没错其实行为跟乘方是一样的,所以我把它叫 pow)。

我们先看累乘。

累乘

如果我们已知指数的话,要写起来是很简单的:

javascript
function pow1(x) { return x; }
+function pow2(x) { return pow1(x) * x; }
+function pow3(x) { return pow2(x) * x; }
+function pow4(x) { return pow3(x) * x; }
+function pow5(x) { return pow4(x) * x; }

这样写其实是符合pow的定义的。

然后其实我们可以把它变换一下:

javascript
// 伪代码,无法正确运行
+function pow(x, 1) { return x; }
+function pow(x, 2) { return pow(x, 1) * x; }
+function pow(x, 3) { return pow(x, 2) * x; }
+function pow(x, 4) { return pow(x, 3) * x; }
+function pow(x, 5) { return pow(x, 4) * x; }

所以我们得到了pow函数的另一个形式:

没错我们又归结到了选择结构上。后面我们会拿这个来讨论。现在只是简单提一提。

哦对了,现在pow的这个形式叫做递归定义,数学上叫递推关系式(好像又叫归纳定义?)。

好的,我们回到运算符上来。

根据前面的分析,我们可以知道,如果能够拿到前面一个乘积,然后和第y个相乘,,就可以得到我们要的结果。

于是我们假设那第y-1个乘积是prod,最终的结果就是prod*x。再往前推prod,这个关系是基本上不变的。

那么,我们只要有一个东西来保存前面的结果,再跟后面的相乘之后更新保存的结果就可以了。

这个能保存数据的东西我们把它叫做变量(Variable)。

比如最开始prod = 1,然后prod = prod * x,会把prodx相乘的结果计算出来,再保存进prod里面。

所以我们可以简单地把这个计算看成

javascript
let prod=1
+prod=prod*x;
+prod=prod*x;
+prod=prod*x;
+...
+prod=prod*x; // 执行y次

那么怎么让他能够只执行y次呢?

循环

JavaScript提供了一种东西叫做循环语句。

大概是长这个样子的

javascript
let prod=1;
+for(let i = 1; i <= y; i = i+1) {
+    prod = prod*x;
+}

这个叫循环语句,意思就是,在满足指定条件的情况下,会一直去执行语句体(花括号之间)的内容。

上面那段代码的意思是,让prod初值为1,让i从1开始,每次增加1,如果i小于等于y的话,执行prod = prod*x,否则跳出循环。

确实很复杂,特别是比起上面那个递归定义来。

我们对它进行一个简单的改写。

一般来说,程序员是从 开始计数的。于是从 ,就变成了从 ,进一步地i <= y-1又可以进一步地写成i < y(反正都是整数,证明我就不给了),这样条件就简化掉了一个符号。

另外,但凡类似x=x+y这种的,都可以写成x+=y,特别地,当运算符为+-,同时y为1的时候,可以直接写成x++或者x--

于是一个典型的循环就出来了:

javascript
let prod=1;
+for(let i = 0; i < y; i++) {
+    prod *= x;
+}

那么我们的pow函数也有了:

javascript
function pow(x, y) {
+    let prod=1;
+    for(let i = 0; i < y; i++) {
+        prod *= x;
+    }
+    return prod;
+}

选择

好的,回头来看我们的abs函数。

我们要根据x的符号来决定时返回x还是他的相反数。

JavaScript让我们能通过选择语句if...else...做这件事。

javascript
function abs(x) {
+    if(x >= 0) {
+        return x;
+    } else {
+        return -x;
+    }
+}

当然if/else还有一种简单的形式,可以让我们写起来更省力。

javascript
function abs(x) {
+    return (x >= 0) ? x : -x;
+}

?:运算符共有三部分,问好前面是判断条件,问号后面由冒号分成两部分,如果判断条件为真,那么返回冒号与问号之间的,否则返回冒号之后的。

递归

好的,这样子我们就能完善我们前面写的递归定义的pow了。

javascript
function pow(x, y) {
+    return (y == 1) ? x : pow(x, y-1) * x;
+}

不能比循环的明白更多。

尾递归

尾递归这个概念可以简单做个了解。主要是因为如果有了尾递归,编译器就可以更好的优化程序的执行过程。而且更重要的一点是,尾递归跟循环几乎是一一对应的关系。

我们先简单的把这个实现写出来吧。

javascript
function pow(x, y, prod = 1) {
+    return (y == 0) ? prod : pow(x, y-1, prod*x);
+}

怎么转换到循环的呢?

首先我们提取出变量来。

javascript
function pow(x, y, prod = 1) {
+    if (y == 0) {
+        return prod;
+    }
+    y = y - 1;
+    prod*=x;
+    return prow(x, y, prod);
+}

正式写成循环

javascript
function pow(x, y, prod = 1) {
+    for(;y != 0;) { // or while(y != 0)
+        y = y - 1;
+        prod*=x;
+    }
+    return prod;
+}

改写成递增的变量

javascript
function pow(x, y) {
+    let prod = 1;
+    for(let i = 0; i < y; i++) {
+        prod *= x;
+    }
+    return prod;
+}

短路求值

试想一下我们的递归版pow代码

javascript
function pow(x, y) {
+    return (y == 1) ? x : pow(x, y-1) * x;
+}

如果这里你把函数自身的调用写成了pow(x, y) * x,结果会是怎么样的呢?

如果这样写,y作为一个计数器并没有递减,那么每回调用都会跟当前调用一样传递相同的参数进去。

于是会一在重复递归地调用pow(x, y),直到不能再调用导致栈溢出。

然而一定是这样吗?你可以调用一下pow(4, 1)试试,会正常输出4

为什么会有这种不同呢?

上一讲,我们提到了组合的概念,说,通过组合会强制求值每一个传递给函数的参数,但是当我们把这个东西忘?:操作符上套的时候,并没有效果。

这种特性叫做短路求值(short-circuit evaluation),对于?:来说,只有当条件为真的时候才去求值真分支,为假的时候才去求值假分支。

所以如果写错的那个pow,当y == 1的时候,还是能够正常执行的。

过程

一个再复杂的流程,最终都会由这样的三部分组成:

  • 顺序
  • 分支(选择)
  • 循环

根据我们前面提到的组合的思想,再复杂的过程都能通过这些来组合解决。

练习

  • 试写出求阶乘、求斐波那契数等函数的循环、递归和尾递归版本的实现。
  • 用以上代码解释短路求值
  • 请使用顺序、分支、循环来描述现实中的一些过程。
+ + + + \ No newline at end of file diff --git a/ch04_encoding.html b/ch04_encoding.html new file mode 100644 index 0000000..2c775d5 --- /dev/null +++ b/ch04_encoding.html @@ -0,0 +1,27 @@ + + + + + + 编码 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page

编码

这里的编码(Encoding)当然不是指简单的编写代码。

为什么会有这个概念呢。

想想古老的印度人是怎么表示数字的吧:用十个不同的符号和位序组合来表示一个确切的数字,演化成今天我们的用阿拉伯数字(Arabic Numerials)表示的十进制系统。

没错就是你身边到处都存在的数字。这套系统只需要简单的十个符号和并不怎么复杂的规则就能从0表示到任意大的确切数字。

我们在第二讲中提到,代码中的表示的“整数”,跟数学意义上的整数,是有所差距的。这个差距其宗之一就在于表示数字的能力上。

比如,我们来计算

javascript
pow(2, 70);

嗯,结果是什么鬼(1.1805916207174113e+21,特别是后面这段)!

也就是说,我们能直观表示的整数也就21位(十万亿亿级),再之后就只能用科学计数法表示了。

实际你会发现,其实还没到这个数量级的时候,数字就已经无法精确表示了。

为什么会这样呢?

二进制

跟阿拉伯数字系统一样,计算机内部也是利用位序来表示数值,但犹豫数字电路只有通和断两种状态,所以,相当于每位上只能有两种状态,作为传统,我们将用0和1来表示这两种状态。

然后我们就会看到计算机努力的表示数字: 等你都觉得脑袋大了,计算机才数到10,不过其实关于这个问题并不需要你操心太多,反正跟阿拉伯数字一样,只要足够长,任意大的数都可以表示。

可是我们之前使用的过程告诉我们,其实并不是这样的。为什么呢?

定长

既然计算机表示数字就是靠0和1,那么如果我要表示多个数字,怎么来区分呢?

比如,你知道下面究竟是1,2,3,4还是13, 14?

javascript
11011100

于是其实我们可以思考一下,出了数值,还有什么可以利用的。

长度。(以及其实你也可以用霍夫曼编码。)

当我规定用于表示数据的长度必须是一定(比如4位)的时候,表示数据就特别地清晰了:

javascript
0001 0010 0011 0100

就可以表示1,2,3,4这四个数。而上面那个我们理解不了的序列,也可以解释成13,14这两个数。

当你去规定一种解释二进制序列的方法的时候,其实你就是在定义一种编码

二进制序列中,每个0或者1,叫一个bit(位),特定长度(一般是8)的基本解释单位,叫做byte(字节),这个特定长度,据说是依据当前系统中表示基本的字符编码的长度来确定。

换句话说,编码不仅仅是能用来表示数字的。

ASCII

现在提到字节大家都会反应是8位,就是因为这个东西。

全名叫American Standard Code for Information Interchange(美国标准信息交换码)。

这套代码从上世纪60年代一致用到现在,足以说明有多么经典。然而我们先不说他是什么,简单的从我们自己的角度出发先设计一套东西。

字符集

要知道美国人民的生活很是单调的,每天就面临大小写加起来四十多个字母,十个阿拉伯数字,以及十多个常用的标点符号。对比起来,深深地为自己的母语感到自豪。

当然,美国人民日常使用的字符量少,在信息化的时代,其实是有优势的,比如,把他们常用的字符加起来也就100个左右,而,所以我们只要有长度为7位的二进制串长度就能把美国人民用的那些字符给完全表示出来。

比如我们可以定义,0000000 ~ 0001001 表示数字字符0-9, 0100001 ~ 0111010 表示大写字母字符的A-Z,1000001 ~ 1011010表示小写字母字符的a-z,于是我们就能对把对应的字母变成相应的编码了。

但是老是这样看着满屏的0和1,总是会感觉眼花。既然都是表示数据,并不是说相同的东西,计算机看到长什么样子,我们就要看一样的。只要是能够表示相同的数据,完全可以换一种我们更习惯的方式来表示。

十六进制

比如我们可以使用十六进制(Hexadecimal)代替二进制(Binary),也就是说,同一个位上面共有16个状态,习惯上分别用0-9加上a-f来表示,同时,为了与十进制表示进行区分,一般会加上0x前缀或者h后缀。对于刚刚的字符编码来说,就可以用0x00 ~ 0x09 表示 数字字符,0x21 ~ 0x3A 表示大写字母A-Z,0x40 ~ 0x5A表示小写字母a-z。

为什么是十六进制而不是其他的十二进制或者十八、二十四进制呢?

我们可以简单的做一个对比:

Bin 0001 0010 0011 .... 1010 1011 1100 1101 1110 1111
+Hex    1    2    3         A    B    C    D    E    F
+Dec    1    2    3        10   11   12   13   14   15

十六进制的一位刚好一一对应二进制的每四个位。

于是本来用二进制表示起来很冗长的东西,用十六进制可以比较轻松的表述出来。而且相互之间的转换也很容易。

相反生活中常用的十进制跟二进制对应的就不那么协调,所以一般也比较少用,只要知道对应的位上的值就好。

也就是说,只要能够数

嗯,我觉得如果你玩过2048的话一定不会对前面的那些数字感到陌生。

ASCII

嗯,ASCII跟我们想的一样,选择了8位作为实际的长度,同时加入了一些必须要用到的特殊字符(比如制表符tab 0x09,回车记号carriage return 0x0D,换行符line feed 0x0A..),以及一些控制字符(比如空字符NUL 0x00,文件结束符EOF等)。于是整体的设计相对于我们的想法有一些偏移:

  • \x30\x39 表示0-9
  • \x41\x5a 表示A-Z
  • \x61\x7a 表示a-z

好的我们来试试吧:

javascript
'\x41'

我们可以直接用引号'括起来单个字符,在程序中直接拿来使用。同时也可以反过来看:

javascript
let a_char = 'a';
+a_char.charCodeAt(0).toString(16)

CJK字符集

CJK(有时候也会带上V),是China,Japan,Korea(和Vietnam)的统称,在这些地区,因为同遗传自古汉语,字符的数量可是非常之多,8位的ASCII根本就没办法保存这么多东西。

于是随着各种语言诞生了各种编码,比如GB2312 简体中文,Big-5 繁体中文,Shift-JIS 日文等。这些编码并不是相互兼容的,于是一般对于特定的内容,需要专门指出其编码。同样地,当你尝试去一种编码来解释另外一种编码的文本时,就会出现乱码的情况。注意这个时候并不是你电脑中毒了。

Unicode

所以就有人想,不如把所有的编码都统一了吧,于是就出现了Unicode。

现行版本的Unicode加入了世界上能够找到的绝大多数语言的符号(嗯,包括emoji),然后将这些符号分类整理,统一编码。于是,在理想情况下,大家都使用Unicode字符集的话,就不会出现乱码的错误了。

Unicode包含的字符量足够的多,于是就要设计比ASCII更充分大的表示方式才能够容纳所有的符号。最初选择的是每个字符使用16位的二进制串来表示,这样的话一个中文字符虽然会超出8位范围之外,还是能够表示得出来,这种方法被称为UTF-16。

然而不久就发现,符号的数量(就算仅仅是中文字符)远远多于65536个,于是又进一步地扩充到了32位,可以表示到上亿个字符了,目前为止还要很久才会资源枯竭,这种方法类似地,被称为UTF-32。

然而UTF-32有个很致命的缺点。

对于西方人来说,并没有CJKV地区的人的需求,1个字节就能足够表示他们的符号,而UTF-32硬生生地为了统一,要求字母字符也要用4个字节来表示,这样就让空间整整大了4倍,而且另外3倍的空间完全是浪费掉的,没有任何作用。

但是,为了兼容,没办法。

UTF-8

于是为了解决这个浪费空间的问题,UTF-8出现了。这是来自Ken Thompson的设计,他给整个IT界贡献了不少东西,Unix、C语言、UTF-8。

简单来说就是,在保持与ASCII兼容的情况下,通过添加一些标记来支持更长的编码。

具体的做法当然维基百科介绍的比我更清楚,这里提起来只是要说这么一个思路:变长编码(Variable-Width Encoding)。因为比较重要的就是在设计的过程中考虑变量和不变量,以及设置各种标志和不同的数据区块解释方式,这样就可以把简单的二进制序列变成我们想要的任意数据。

当然,这里的思想我们会在第6章详细地展开。

数系的扩充

你有没有尝试计算JavaScript里面1/3的结果?

当我们需要小数的时候又该如何表示呢?

这个问题留作练习。

作为参考答案,你可以搜索IEEE754。

+ + + + \ No newline at end of file diff --git a/ch05_sequence.html b/ch05_sequence.html new file mode 100644 index 0000000..ca2bb4c --- /dev/null +++ b/ch05_sequence.html @@ -0,0 +1,45 @@ + + + + + + 序列 | 编程导引 + + + + + + + + + + + + + +
Skip to content
On this page

序列

你应该记得,我们在编码那一张提过如何表示多个数字的,没错,定长编码可以帮我们解决这个问题,帮我们界定数字之间的边界。但是,当我们要表示的数据不只是一组的时候,又该怎么来界定边界?

比如,我们有两组数,分别是,假设以一个字节(十六进制)表示的话,就是下面这个样子的:

cpp
0x01030507 0x090B0002 0x0406080A

好的,接下来请完成以下任务:

  • 找到第二个序列中的第三个数。

嗯,是不是感觉自己懵了,如果不知道第一组有几个的话你是不可能准确找到这个数字的。

于是我们得到了一个简单的界定序列边界的方法,就是要知道各序列对应的长度。

比如我们知道这两个序列是连续放在一起的,第一个序列长度是,同时也知道要找的是第二个序列的第三个数,那么这个数就应该是第个数字。

当然,这个数字要从数过去才行,否则从任意其他的位置数,都不能得到我们想要的结果。

长度

我们可以看到,界定一个序列,必须要知道的是两点,第一个是他的起始元素,第二个就是这个序列的长度。

一般来说,当这个序列的长度固定不变,并且每个元素是挨在一起的时候,我们常把它叫做array。

关于array,我非常不喜欢“数组”这个翻译,因为这里面并不一定是数,也有可能是符,所以我会尽可能地使用array来表示它。

在JavaScript中,定义一个array如下:

javascript
let nums = [1, 2, 3, 4, 5]

对于array来讲,初始元素是方括号中的取值为0的时候的值。在方括号中的值我们称之为index(索引,奇葩翻译一般叫做下标)。这些索引以数字的形式出现,作为序号来表示数据在序列中的位置。

有个重点是,这里的索引是从开始,最大是序列长度-1,这其实也就是为什么我们在第三章讲过程的时候提到循环的写法的一个原因。

不过对于遍历序列来说还支持另外一种写法。

javascript
for(let item of nums) {
+    console.log(item);
+}

不需要你去管什么索引了,这样子写就像是在说

javascript
对于 nums 里面的 item
+    输出 item 的值

当然,在Python中看起来更自然:

python
for item in nums:
+    print(item)

JavaScript中的字符串与array类似,自己维护了长度。但在早期的一些编程语言(如C语言)中,是没有做这种记录的,取而代之的是使用一个特定的标志来表示,这里是最后一个值为空。这种字符串也有另外一个名字,叫空结尾串(Null-terminated String)。

这种特殊标记就可以让我们界定一个序列的边界的另外一种方式。

我们可以来简单模拟一下这个行为:

javascript
let content = ['h', 'e', 'l', 'l', 'o', null];
+
+// 求长度:
+
+function length(nts) {
+    let length = 0;
+    for (let item of nts) {
+        if (item != null) {
+            length ++;
+        } else {
+            break;
+        }
+    }
+    return length;
+}
+
+length(content) // 5

而实际存放这个串的空间大小:

javascript
content.length // 6

可变长序列

试想你在一个笔记本上记录内容,每一行算是一个条目,当一页记录满以后,并不能连续地往下面记了,就需要翻到下一页纸。

连续不连续并不重要,重要的是我们能从这一页翻到下一页。

对应到我们的序列表示上,只要当前元素能够记录下一个元素的位置,是不是就表示我们的序列就能从第一个元素全部找出来了呢?

就像这张图片描述的:

list

这种结构被称为链接列表(也叫链表,Linked List)在上世纪五十年代就已经设计用于实际的程序。链表这种结构非常的灵活,可以很轻松的在任意位置添加或者删除数据。因为并不需要内存结构上的连续,所有的位序关系都是靠前一个元素和后一个元素之间的链接关系来确定,而添加或者删除数据只要改变一下这种链接关系就可以了。

但是对于链表来说一个很大的问题就是访问数据。比如我们要访问链表的第n个元素的时候,对于array和串这种结构来说,因为内存空间上是连续的,直接做一次加法就能找到,但是对于链表来说每次都要从第一个元素开始找起。而且,这种形式的链表又称作单链表(Singly Linked List),是没有办法从尾部往头部走的。

所以,在单链表的基础上,又构造了双链表(Double Linked List),每一个元素除了纪录自己对应的下一个元素之外,还要保存自己对应的上一个元素。这种时候如果做一些相邻元素的访问,就比单链表高效了一些。

比如,当我访问列表中第7个元素,然后再去访问第6个的时候,只要从第7个元素向前查找一次就可以了。

也许以后你可能还会听说到循环链表(Circular Linked List),或者是其他的形式,这些都离不开最初链表的思想:把数据和位置做关联。

线性结构

我们简单的做一个总结。

对于array,string和list我们都称为序列,原因其实是显而易见的。

首先,他们满足这样一点:所有的都是从头到尾连续地组织在一起,从任何地方出发都能找到下一个元素或者是到序列尾部。就像是在头和尾之间有一条线把他们串起来一样,而且在逻辑表示上,我们也是把他们放成一串。

另外,对于每一个元素,他们在序列中的位置是固定的,换句话说,序列中的元素是有前后顺序的。当我任意交换两个元素的位置,得到的新序列就跟原来的序列是不同的。

第三点就是,当我们从第一个元素开始找下去的时候,总是能找到最后一个元素(对于没有最后一个元素的情况,我们会在以后进行探讨)。

总结下来就是,序列是线性的(Linear)、有序的(Ordered,类比“排序过的(Sorted)”)和有穷的(Finite)。

生成器

很多时候我们的序列是通过某种规则产生的,而这种规则

练习:最长公共子序列(Longest Common Subsequence)

通常我们会对两个序列的内容进行对比,比较这两个序列有什么地方不同。这就是典型的LCS问题。LCS的应用很多,大部分版本控制系统的核心逻辑中就有它的存在。

请参阅Wikipedia对LCS问题的分析和讨论,尝试实现求LCS的算法,并试分析该算法要求迭代器至少要属于哪个category。

+ + + + \ No newline at end of file diff --git a/ch06_data.html b/ch06_data.html new file mode 100644 index 0000000..a41239d --- /dev/null +++ b/ch06_data.html @@ -0,0 +1,136 @@ + + + + + + 数据 | 编程导引 + + + + + + + + + + + + + +
Skip to content
On this page

数据

很多时候我们要处理的东西并不只是一个简单的数据,或者是一个由多个数据组成的简单序列。比如,当我们要考虑一间房子,关注的是户型、面积和位置等,考虑一辆汽车,关注的是性价比、能耗和易用性等,考虑一个学生,关注的是他的学号、姓名、各科成绩等。

很多时候我们也需要考虑一系列的符号,比如房屋的户型、汽车的品牌,这些在一定程度上固定的内容,并不需要专门去用字符串处理。

这种时候,序列、引用和各种标量(Scalar)类型都不能很好的满足我们的需要。

我们来做一个简单的示范吧。

结构

比如,我们要表示一辆车,其中包括他的品牌、价格和最高时速。在当前的情况下,最合适的方式就是采用长度为3的array,约定好第0位表示品牌,第1位表示价格,第2位表示最高时速。于是我们可以得到以下的设计:

javascript
let car = [1, 500000, 180]

于是我们就能够很轻松地(并丑陋地)表示汽车了。

这种写法虽然算是比较方便,但是还是有很多问题的。

比如,每次我们都需要多写很多个函数,比如所有的元素都必须是同一个类型的。

所以大多数编程语言都提供结构(Structure)或者记录(Record)类型,来简化我们实现这个的过程。

比如,在JavaScript中我们可以很简单的把Car表示如下:

javascript
let car = {
+    brand: 'Nissan',
+    price: 500000, // USD
+    speed: 180 // MPH
+}

这是一个结构(struct),其中包含了三个域(field,也叫字段)。比起还要反复地写各种函数来,要简单和方便很多。 而且,各个字段的类型可以不同。

这样子我们就能通过Car来表示一辆汽车了:

javascript
car.brand

很简单吧。

那么,结构的本质到底是什么呢?

回到本章开始我们的讨论。房子、汽车、学生,这些概念性的实体(Entity)都有一些自己本身的属性(Property),以及与其他实体之间的关系(Relation)。 我们可以给这些实体的属性和关系贴上一个个标签,把他们与所属的实体之间关联起来。这样就组成了我们现在看到的结构。

当然,他还是数据,但是这些数据通过我们贴上不同的标签以及把他们关联组合,就变成了更加具有实际意义的内容。换句话说,我们给一块二进制内存区赋予了语义(Semantics)。

来让我们看一下其他的许多结构吧。

复数,

cpp
let complex = {
+    real: 1,
+    image: 1
+};

分数,

cpp
let frac = {
+    numerator: 1,
+    denominator: 2
+};

甚至是,用数据代替表达式,

cpp
let arith = {
+    optr: '+'
+    leftOperand: 1,
+    rightOperand: 2
+};

以及,更多复杂的结构。

递归数据类型

看到这里也许你开始担忧了。什么是递归数据结构(Recursive Data Type)?

这里我们要拿前面提到的一个例子来说事儿了。

list

看这张图,去掉这个list的第一个节点,结果得到的还是一个list。同样地,在这个节点上面再加一个节点,得到的也依然还是一个list。

也就是说,这个list的每一个节点大概对应到下面的结构:

cpp
struct ListNode{
+    int value;
+    ListNode* next;
+};

我们试着用这个结构建立一个列表。

cpp
ListNode a{1, nullptr};
+ListNode b{2, &a};
+ListNode c{3, &b};

c出发,我们就能够依次遍历完ba,而且对于每一个节点,处理的方式基本一致。

cpp
void printList(ListNode* list) {
+    if(list == nullptr) return;
+    std::cout << list->value;
+    printList(list->next);
+}

没错,list就是递归的。每一个list节点都包含一个list的引用。这样我们就能够利用递归来对其进行操作了。

比如我们想要构造一个新的list,也可以把他做成递归的。

cpp
struct List{
+    int value;
+    shared_ptr<List> next;
+}
+
+shared_ptr<List> make_cell(int value, shared_ptr<List> next) {
+    auto cell = make_shared<List>();
+    cell->value = value;
+    cell->next = next;
+    return cell;
+}
+
+template<class ...Args>
+shared_ptr<List> make_list(int value, Args... args) {
+    return make_cell(value, make_list(args...));
+}
+
+template<>
+shared_ptr<List> make_list(int value) {
+    return make_cell(value, nullptr);
+}

构造器(Constructor)

看我们的make_cell函数,其实就是创建了一个shared_ptr,然后再利用传入的参数来对cell中的数据进行初始化。当然这意思其实已经很明确了,make_cell作为创建一个新的cell结构的函数而存在。

但是这样未免太罗嗦,每次我创建一个struct的时候都要再去考虑怎么设计一个用来专门创建实例的函数。JavaScript提供了一种叫做构造器的函数,可以简化这个过程。加入了构造器的List定义如下:

cpp
struct {
+    int value;
+    shared_ptr<List> next;
+
+    List(int _value, shared_ptr<List> _next):
+        value(_value), next(_next) {}
+}

这里的构造器是说,我接受两个参数(_next, _value),然后用这两个参数去初始化JavaScript结构(struct)中的字段。

然后我们可以把整个make_cell函数改成

cpp
make_shared<List>(value, next);

构造器能够让我们以一种很直观的方式来创建和使用数据。 比如:

cpp
struct User {
+    std::string name;
+    int age;
+    double height;
+
+    User(std::string _name, int _age, double _height):
+        name(_name), age(_age), height(_height) {}
+};
+
+auto user = User{"Kimmy", 18, 178.0};

没错,这样看上去跟前面定义的Car类型没有什么区别。但是当结合上默认值参数以及需要在数据初始化的时候做一些其他操作的时候,构造器给提供了很直接的方案。后面我们会进一步地看到这种方案的优点。(当然你也可以去看一看什么是RAII。)

递归结构上的操作

好的我们回到刚刚的List上来。

我们已经可以通过make_list来创建一个整数的列表了,那么,作为一个递归的结构,我们当然也可以通过递归来对齐定义一些操作。

比如把List里面的每一个元素做一个转换。

cpp
shared_ptr<List> 
+transform(shared_ptr<List> list, std::function<int(int)> transformer) {
+    if(list == nullptr) return nullptr;
+    return make_shared<List>(transformer(list->value), transform(list->next, tranformer))
+}
+
+
+// => {-1, -2, -3, -4, -5}
+transform(make_list(1,2,3,4,5), [](int i) -> int { return -i; });

作为数据的过程(Procedure as data)

当然,看到上面那段代码你会是满头雾水。std::function<int(int)>是什么,[](){}放在一起又是什么。

回过头去想想,我们是不是可以定义一些过程,然后对这个过程进行调用,去做一些复杂的计算?同时,当我们需要进一步利用这些过程的时候,可以把它们直接当成数据拿来用的。于是我们可以约束他们的类型,然后根据这些约束再进一步地做抽象。

比如第三章中的pow,类型就是std::function<int(int, int)>,接收两个整数,返回他们的乘方,结果也是整数。

而有些时候我们可能并不只是需要拿已经就有的过程来简单地做抽象,所以更有必要提供一种方式让我们随时都能够创建一个过程。于是JavaScript给我们提供了Lambda表达式(Lambda Expression,在其他语言里面也可能叫做匿名函数/过程,Anonymous Function/Procedure),比如

cpp
auto negate=[](int i) -> int{ return -i; };

我们就地创建了一个lambda表达式,然后把它赋值给了negate。这个lambda表达式所表示的就是一个匿名过程,它会接收一个整数,然后返回这个数的相反数。

于是当我们再去回过头看transform的时候,就不会觉得太突兀了。

cpp
shared_ptr<List> 
+transform(shared_ptr<List> list, std::function<int(int)> transformer) {
+    if(list == nullptr) return nullptr;
+    return make_shared<List>(transformer(list->value), transform(list->next, tranformer))
+}

transform接收一个List和一个int(int)类型的transformer,然后在这个List上递归地对每一个元素执行transformer,得到一个新的结果,然后组成一个新的List。

好的我们来看另外一种递归数据类型,树。更确切地说,是二叉树(Binary Tree)。

C++
struct Tree {
+    int value;
+    shared_ptr<Tree> left;
+    shared_ptr<Tree> right;
+
+    Tree(int _v, shared_ptr<Tree> _l, shared_ptr<Tree> _r): 
+        value(_v), left(_l), right(_r) {}
+};

也就是说,Tree是这种结构,他的左侧是Tree,右侧也是Tree。而且Tree的每个节点都会带有一个value。

当然你还是会怀疑说这种无聊的结构拿来有什么用。那我们看下面的内容:

*
+           /   \
+          +     4
+        /   \ 
+       1     -
+           /   \
+          2     3

这个就是把 作为一种树行结构来表示。

遍历(Traverse)

对于前面那个树形结构,我们可以试着从最顶端的节点写出他的value,然后再分别访问他的左右子树,按照这样的方式把所有的内容写出来,就能得到下面的结果:

* + 1 - 2 3 4

这种表示方式又叫做该二叉树的先序遍历,又叫前序遍历。

然后我们可以把属于同一个树的部分用括号包起来,就得到了:

scheme
(* (+ 1 (- 2 3)) 4)

没错这就是Lisp。

同样地,我们可以选择先一次访问一棵树的左右子树,然后再写出器对应的value,这样一层层递进下去。我们能够得到如下的结果:

1 2 3 - + 4 *

如果你有了解过的话,就能看得出来这跟forth或者PostScript很像,当然他也叫上面那个二叉树的后序遍历。

当然还有一种叫做中序遍历的过程,得到的结果是:

1 + 2 - 3 * 4

跟最初的算术表达式的区别就在于没有了括号。当然,本来表示的意思也丧失了。

对于前序遍历和后序遍历的结果,并不会改变整个表达式的语义,只是重新定义了这个表达式的解析方式。

练习

请尝试用代码实现先序、中序和后序遍历。

深度优先(Depth-First)和广度优先(Breath-First)

前面我们提到的遍历,是对整个树进行访问的过程。如果我们只需要从根节点出发,然后去寻找某个指定元素的话,可以有这样两种选择。

第一种是遇到有分支,我们就选择一个方向继续往前走,直到走到不能再继续,然后再一步步返回,同时在返回的过程中尝试其他的分支。

这种首先走到最底端然后再慢慢回来的过程叫做深度优先查找(Depth-First Search)。

比如前文中的那个表达式树,整个过程就是

cpp
* + 1 <后退> - 2 <后退> 3 <后退> <后退> <后退> 4

也就是说,我们需要访问整个树才能查找到4。

另外一种方式是,遇到分支,我们先把对应的分支的每一个第一层先访问一遍,再一步步走到下一层。

这样的过程叫做广度优先查找(Breath-First Search)。比如

cpp
* <下一层> + 4 <下一层> 1 - <下一层> 2 3

这个时候我们如果要查找4的话,只要在第一层就可以了。

栈(stack)与队列(queue)

我们仔细思考一下怎么实现DFS和BFS。

对于DFS,我们需要考虑如果找不到如何返回,所以我们可以先试着模拟一下DFS查找4的流程。

cpp
path            description
+==========      ==========
+*               //step 1
+* +             //step 2
+* + 1           //step 3 and 1 has no childs
+* +             //get back to +
+* + -           //step 4
+* + - 2         //step 5 and no childs
+* + -           //get back to -
+* + - 3         //step 6 and no childs
+* + -           //get back to - and no more childs to visit
+* +             //get back to + and no more childs to visit
+*               //get back to *
+* 4             //step 7 found!

可以看到,每次当遇到无法继续的节点,都会有一步回退操作,其实就是把path中加入进去内容的再取出来,这种一般最后进入但是最先退出(Last In First Out,LIFO)的结构我们常常称作是栈(stack)。栈通常会用来辅助实现一些拥有LIFO特性的过程,比如过程调用,也有一个对应的调用栈(call stack)。

比如当我们调用递归函数pow(2, 5),对应的调用栈展开如下:

cpp
pow(2, 5)                 => 2 * 2 * 2 * 2 * 2
+    pow(2, 4)             => 2 * 2 * 2 * 2
+        pow(2, 3)         => 2 * 2 * 2
+            pow(2, 2)     => 2 * 2
+                pow(2, 1) => 2

这也就是在第三章我提到“栈溢出”中的“栈”的含义。当一个调用栈太大而超出了当前系统规定的范围以后,就会造成这种问题。

我们接下来看BFS,模拟一下它查找3的流程:

cpp
path            description
+==========      ========== 
+*               //step 1
+* + 4           //push childs and move to next
++ 4             //step 2
++ 4 1 -         //push childs and move to next
+4 1 -           //step 3 no childs move to next
+1 -             //step 4 no childs move to next
+-               //step 5
+- 2 3           //push childs and move to next
+2 3             //step 6 no childs move to next
+3               //step 7 found!

与DFS的后进先出不同的是,BFS会把先放入的东西给拿出去,也就是先进先出(First In First Out,FIFO)。用于实现FIFO的结构我们一般叫做队列(queue)。当然队列的应用也很广泛,特别是需要考虑进入的先后顺序,通常都是作为一种特殊的队列来处理。

树反应了具有某种特定结构的层级关系。当然这种结构并不一定是只有两个分支的,也并不一定非得是考虑顺序的。当我们在建模的时候,遇到层级结构,就可以考虑设计成一棵树。另外,递归。

练习

尝试用代码实现DFS和BFS。

+ + + + \ No newline at end of file diff --git a/ch07_state.html b/ch07_state.html new file mode 100644 index 0000000..3dbd706 --- /dev/null +++ b/ch07_state.html @@ -0,0 +1,24 @@ + + + + + + 状态 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page

状态

程序中的很多数据往往是在不停地变化的,比如时间,比如与用户的交互,又比如,一些程序运行中产生和保存的数据。当一些可变的数据或者对象是发生改变的时候,我们就可以说它们从一个状态转换到了另一个状态。

一个简单的例子就是迭代器(iterator)。这个JavaScript里面非常重要的概念,就是一个典型的可变对象。每一次需要向前或者向后访问一个序列的元素的时候,都需要改变一下迭代器的状态。

+ + + + \ No newline at end of file diff --git a/ch08_reference.html b/ch08_reference.html new file mode 100644 index 0000000..42a61d3 --- /dev/null +++ b/ch08_reference.html @@ -0,0 +1,24 @@ + + + + + + 引用 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page

引用

注:本文中提及到的引用,是广义的“引用”概念,而不仅仅是JavaScript的引用。

TODO: 重写

+ + + + \ No newline at end of file diff --git a/ch09_closure.html b/ch09_closure.html new file mode 100644 index 0000000..3b05235 --- /dev/null +++ b/ch09_closure.html @@ -0,0 +1,24 @@ + + + + + + 闭包 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page
+ + + + \ No newline at end of file diff --git a/ch10_object.html b/ch10_object.html new file mode 100644 index 0000000..d04ac0f --- /dev/null +++ b/ch10_object.html @@ -0,0 +1,44 @@ + + + + + + 对象 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page

对象

广义地讲,任何一个在程序中出现的元素都被称作对象。于是对象也拥有自己的类型,比如整数对象、类型对象(对象的类型也算是类型,first-class type)以及函数对象等。

但是对象在另外一个编程流派也有着特殊的含义。

面向对象编程(Object-Oriented Programming)

面向对象编程是当前比较热门的一个编程范式(Programming Paradigm)。主要涉及通过抽象(abstraction)和多态(polymorphism)来进行程序建模和设计,以达到组件(Component)复用的目的。

简单地讲,就是面向对象会通过一种方式来定义一些共有的接口和符合接口的操作,然后通过对这些接口的实现来完成一些具有公共特性但细节上略有不一致的行为。

先看一个简单的例子。

不同的车(Vehicle)都可以走(Run),包括汽车(Car)、卡车(Truck)、推车(Wagon)等等。

我们可以简单地描述这种关系:

cpp
// Pseudo code
+interface Vehicle {
+    void Run();
+}
+
+class Car is Vehicle {
+    void Run() override { ... }
+}
+
+class Truck is Vehicle {
+    void Run() override { ... }
+}
+
+class Wagon is Vehicle {
+    void Run() override { ... }
+}
+
+
+Vehicle v = new Car{} // or new Wagon/Truck
+
+v->Run(); // run specific Run prodeure

基于继承(Inheritance)的多态

基于泛型(Generics)的多态

+ + + + \ No newline at end of file diff --git a/ch11_concurrency.html b/ch11_concurrency.html new file mode 100644 index 0000000..0a5442c --- /dev/null +++ b/ch11_concurrency.html @@ -0,0 +1,24 @@ + + + + + + 并发 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page
+ + + + \ No newline at end of file diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js deleted file mode 100644 index 306864e..0000000 --- a/docs/.vitepress/config.js +++ /dev/null @@ -1,111 +0,0 @@ -const chaptersAndGuides = [ - { - text: "目录", - collapsed: false, - items: [ - { text: "引言", link: "/chapters/intro" }, - { text: "环境", link: "/chapters/ch01_environment" }, - { text: "计算", link: "/chapters/ch02_computation" }, - { text: "过程", link: "/chapters/ch03_procedure" }, - { text: "编码", link: "/chapters/ch04_encoding" }, - { text: "序列", link: "/chapters/ch05_sequence" }, - { text: "数据", link: "/chapters/ch06_data" }, - { text: "状态", link: "/chapters/ch07_state" }, - { text: "引用", link: "/chapters/ch08_reference" }, - { text: "闭包", link: "/chapters/ch09_closure" }, - { text: "对象", link: "/chapters/ch10_object" }, - { text: "并发", link: "/chapters/ch11_concurrency" }, - { text: "练习答案与提示", link: "/chapters/answers" }, - ], - }, - { - text: "附录", - collapsed: true, - items: [ - { text: "工具上手", link: "/guides/" }, - { text: "浏览器开发者工具与 Console", link: "/guides/browser-devtools" }, - { text: "安装与上手 VS Code", link: "/guides/vscode" }, - { - text: "安装 Node.js 并在 VS Code 运行 JavaScript", - link: "/guides/nodejs", - }, - { text: "TypeScript 入门:安装、编译与运行", link: "/guides/typescript" }, - ], - }, -]; - -module.exports = { - title: "入门指南", - themeConfig: { - nav: [ - { text: "计算机编程", link: "/chapters/" }, - { text: "Agentic AI", link: "/agentic-ai/" }, - { text: "参考", link: "/reference/glossary" }, - ], - - sidebar: { - "/chapters/": chaptersAndGuides, - "/reference/": [ - { - text: "参考", - collapsed: false, - items: [ - { text: "概念解释", link: "/reference/glossary" }, - { text: "RAG 技术详解", link: "/reference/rag-deep-dive" }, - { - text: "API 调用与 Function Calling", - link: "/reference/api-interaction", - }, - ], - }, - ], - "/guides/": chaptersAndGuides, - "/agentic-ai/": [ - { - text: "Agentic AI", - collapsed: false, - items: [ - { text: "介绍", link: "/agentic-ai/intro" }, - { text: "AI 协同基础", link: "/agentic-ai/ai-essentials" }, - { - text: "多模态创作能力", - link: "/agentic-ai/multimodal-creativity", - }, - { - text: "高级 Prompting 与推理增强", - link: "/agentic-ai/reasoning-logic", - }, - { text: "AI Coding 实践", link: "/agentic-ai/ai-coding" }, - { text: "Agent 机制与构建", link: "/agentic-ai/agentic-system" }, - { text: "Agent 实践与治理", link: "/agentic-ai/agent-practice" }, - ], - }, - ], - }, - footer: { - message: "CC-BY 4.0 Licensed", - copyright: "Copyright © 2015-present Kimmy Liu", - }, - }, - - markdown: { - math: true, - }, - - head: [ - [ - "script", - {}, - - ` -var _hmt = _hmt || []; -(function() { - var hm = document.createElement("script"); - hm.src = "https://hm.baidu.com/hm.js?bc2f9f80827718f41c02c33dc45976cb"; - var s = document.getElementsByTagName("script")[0]; - s.parentNode.insertBefore(hm, s); -})(); - `, - ], - ], -}; diff --git a/docs/agentic-ai/agent-practice.md b/docs/agentic-ai/agent-practice.md deleted file mode 100644 index 3247d0d..0000000 --- a/docs/agentic-ai/agent-practice.md +++ /dev/null @@ -1,432 +0,0 @@ -# Agent 实践与治理(Agent Practice) - -上一章,我们了解了 Agent 的内部机制。这一章,我们来看怎么用好 Agent:配置、治理、以及常见的陷阱。 - -## 配置与治理:Rules 和 Skills - -### Rules:行为准则 - -Rules 是你给 Agent 设定的行为准则。它定义了“什么该做、什么不该做、怎么做”。 - -比如: - -```text -代码风格:使用 2 空格缩进,使用单引号。 -提交规范:commit message 使用英文,格式为 type(scope): description。 -安全要求:不要硬编码任何密钥或密码。 -``` - -Rules 的价值在于一致性。没有 Rules,Agent 每次可能用不同的风格写代码、用不同的格式写提交信息。有了 Rules,输出会稳定得多。 - -你可以先让 Agent 自由发挥,观察它的输出,然后把你不满意的地方写成 Rules。这样 Rules 是从实际问题中长出来的,而不是凭空想象的。 - -### Skills:可复用的能力 - -Skills 是 Agent 的可复用能力模块。一个 Skill 通常是一个“指令 + 工具调用”的组合。 - -比如: - -- “代码审查” Skill:读取代码 → 检查风格 → 检查安全 → 输出报告。 -- “文档生成” Skill:读取代码 → 提取注释 → 生成文档。 -- “测试编写” Skill:读取函数 → 分析边界 → 生成测试用例。 - -Skills 的价值在于复用。你不需要每次都从零开始描述需求,调用一个 Skill 就行。 - -```text -没有 Skills:每次都要写详细的需求描述 -有了 Skills:调用 skill name,Agent 自动执行预定义流程 -``` - -但 Skill 不能写得太抽象。比如“帮我写好文章”很难执行,“先整理主题、再列 5 个小标题、每节写 120 字、最后检查是否有重复观点”就清楚得多。 - -写 Skill 时,可以把它当作一份小型操作手册: - -- 什么时候使用这个 Skill。 -- 输入需要包含哪些信息。 -- 按什么步骤执行。 -- 每一步怎么判断是否完成。 -- 失败时停在哪里,向用户说明什么。 - -具体的 Skill 才能复用。抽象的 Skill 看起来高级,实际执行时往往只是把不确定性藏起来。 - -## 指令层级:SOUL.md → AGENTS.md → Rules → Prompt - -当你的 Agent 使用场景变多时,指令会分层。理解这个层级,能帮你更好地组织和管理 Agent 的行为。 - -从上到下: - -### SOUL.md:系统人格 - -SOUL.md 定义了 Agent 的“人格”:它的角色、价值观、行为风格。 - -```text -你是一个专业的代码审查员。你注重代码质量、安全性和可维护性。 -你不会为了讨好用户而忽略问题。你会直接指出问题,但语气保持尊重。 -``` - -这是最高层级的指令,影响 Agent 的所有行为。 - -### AGENTS.md:Agent 定义 - -AGENTS.md 定义了具体 Agent 的能力和职责。 - -```text -你有以下能力: -- 读取和分析代码 -- 执行测试 -- 生成报告 - -你的职责是: -- 检查代码质量和安全问题 -- 运行测试并报告结果 -- 不要修改代码,只报告问题 -``` - -### Rules:行为规则 - -上一节讲的 Rules,在这个层级生效。它们是具体的、可执行的行为准则。 - -### Prompt:具体任务 - -最底层是你每次给出的具体任务指令。 - -```text -请审查 src/utils.js 这个文件。 -``` - -这个层级结构要记住一件事:上层指令约束下层指令。如果 Prompt 和 Rules 冲突,Rules 优先;如果 Rules 和 SOUL.md 冲突,SOUL.md 优先。 - -```text -SOUL.md:你不会为了讨好用户而忽略问题 - ↓ 约束 -AGENTS.md:你的职责是检查代码质量 - ↓ 约束 -Rules:代码风格使用 2 空格缩进 - ↓ 约束 -Prompt:请审查 src/utils.js -``` - -## Multi-agent:正确打开方式 - -当你熟练使用单个 Agent 之后,很自然会想:能不能用多个 Agent 协作? - -答案是:可以,但不一定是好主意。 - -### Multi-agent 适合的场景 - -- **任务有明确的分工**:比如一个 Agent 负责写代码,另一个负责审查。 -- **需要不同视角**:比如一个 Agent 从用户角度思考,另一个从安全角度思考。 -- **任务可以并行**:比如同时处理多个独立的子任务。 - -### Multi-agent 不适合的场景 - -- **任务本身很简单**:一个 Agent 就能搞定的事,拆成多个 Agent 只会增加协调成本。 -- **Agent 之间需要频繁通信**:如果 Agent A 的输出是 Agent B 的输入,而 B 的输出又是 A 的输入,这种循环依赖会让系统变得脆弱。 -- **你还没有用好单个 Agent**:如果单个 Agent 的 Prompt 都写不好,多个 Agent 只会把问题放大。 - -一个常见的误区:觉得 Multi-agent = 更高的效率。实际上,Multi-agent 往往只是更复杂的系统。它带来了新的问题: - -- **协调成本**:谁先执行?谁后执行?结果怎么合并? -- **错误传播**:一个 Agent 的错误可能影响整个链条。 -- **调试困难**:出了问题,是哪个 Agent 的错? - -正确做法:先用单个 Agent 把事情做好。只有当单个 Agent 确实搞不定时,才考虑拆分。而且拆分时,优先考虑最简单的方案:两个 Agent 串行执行,而不是多 Agent 编排。 - -### Multi-agent 的实现模式 - -如果你确实需要多个 Agent 协作,有几种常见的编排模式: - -**顺序模式(Sequential)** - -最简单的方式:Agent A 做完 → 结果传给 Agent B → Agent B 做完 → 结果传给 Agent C。 - -```text -任务 → [Agent A: 分析] → [Agent B: 执行] → [Agent C: 审查] → 结果 -``` - -适合有明确先后依赖的任务。比如:先让分析 Agent 拆解需求,再让编码 Agent 实现,最后让审查 Agent 检查。 - -**并行模式(Parallel)** - -多个 Agent 同时执行独立的子任务,最后合并结果。 - -```text - ┌→ [Agent A: 查文档] ─┐ -任务 → ├→ [Agent B: 跑测试] ─├→ 合并 → 结果 - └→ [Agent C: 分析日志] ┘ -``` - -适合子任务之间没有依赖的场景。比如同时从三个数据源拉取信息。 - -**辩论模式(Debate)** - -多个 Agent 对同一个问题给出不同观点,由一个裁判 Agent 选择最佳答案。 - -```text - ┌→ [Agent A: 观点 1] ─┐ -问题 → ├→ [Agent B: 观点 2] ─├→ [裁判 Agent: 综合判断] → 结论 - └→ [Agent C: 观点 3] ┘ -``` - -适合需要多角度思考的决策场景。不同 Agent 可以有不同的“人设”(比如乐观派、悲观派、务实派),从不同角度看同一个问题。 - -**分层模式(Hierarchical)** - -一个“管理者” Agent 负责拆解任务和分配,多个“执行者” Agent 负责具体执行。 - -```text -任务 → [管理者 Agent] - ├→ 分配子任务 1 → [执行者 A] - ├→ 分配子任务 2 → [执行者 B] - └→ 汇总结果 → 最终输出 -``` - -适合复杂任务的动态拆分。管理者 Agent 根据任务特点决定派谁去做。 - -### 模式怎么选? - -- 能用顺序就用顺序:最简单,最容易调试。 -- 需要速度且任务独立时用并行。 -- 需要多角度决策时用辩论。 -- 任务无法预先定义流程时用分层。 - -不要为了“看起来高级”而选复杂的模式。能用两个 Agent 串行解决的,就不要搞四个 Agent 并行加辩论。 - -## M×N Gateway:企业级 Agent 集成 - -在企业场景下,你可能面对的是:M 个应用需要调用 N 个模型。如果每个应用都直接对接每个模型,就是 M×N 条连接:维护成本极高。 - -M×N Gateway 的思路是:加一个中间层。 - -```text -没有 Gateway:应用1→模型1, 应用1→模型2, 应用2→模型1, 应用2→模型2 (M×N) -有了 Gateway:应用1→Gateway→模型1/模型2, 应用2→Gateway→模型1/模型2 (M+N) -``` - -Gateway 的价值: - -- **统一管理**:在一个地方控制所有模型的访问权限、用量、计费。 -- **灵活切换**:换了模型提供商,只需要改 Gateway 配置,不需要改每个应用。 -- **负载均衡**:根据任务类型自动选择最合适的模型。 - -对个人学习者来说,这个概念了解即可。但如果你未来在企业环境中使用 Agent,Gateway 会是一个常见的架构模式。 - -## 评估与调试 - -怎么知道你的 Agent 做得好不好?怎么在它出问题时找到原因? - -### 评估结果的有效性 - -AI 的输出看起来“像那么回事”,但不一定对。你需要一套方法来验证。 - -**事实核查**:AI 给出的信息是否准确?最直接的方法是让它给出来源,然后你去核实。如果它给不出来源,那这个信息的可信度就要打折扣。 - -**一致性检查**:用不同的方式问同一个问题,看答案是否一致。如果两次回答矛盾,至少有一个是错的。 - -```text -第一次:用 Python 怎么读取 CSV 文件? -第二次:用 Python 读取 CSV 文件有哪些方法? -``` - -如果第一次它说“用 pandas”,第二次说“用 csv 模块”,两个都对。但如果第一次说“用 open() 直接读”,第二次说“必须用 pandas”,就有问题了。 - -**边界测试**:给 AI 一些极端情况,看它的回答是否合理。 - -```text -如果数组长度是 0 会怎样? -如果用户输入的是空字符串呢? -如果网络请求超时了呢? -``` - -AI 在正常情况下可能回答得很好,但遇到边界情况就可能出错或“编”出不存在的处理逻辑。 - -**对抗性测试**:故意给错误的前提,看 AI 是否会纠正你还是顺着你说。 - -```text -Python 的 list 是不可变的,对吧? -``` - -如果 AI 说“对”,那它的可靠性就值得怀疑。(Python 的 list 是可变的。) - -### 构建任务样本 - -如果你会反复使用同一个 Agent,不要只凭一次感觉判断它好不好。准备一小组固定任务样本,每次改 Rules、Skill、工具或模型后,都用这组样本重新跑一遍。 - -任务样本不需要很大,初学阶段 5 到 10 个就够。关键是覆盖常见情况和容易出错的情况: - -- 正常任务:它应该顺利完成。 -- 边界任务:输入为空、信息不完整、格式不规范。 -- 对抗任务:用户给出错误前提,看看它会不会纠正。 -- 高风险任务:涉及删除、发送、发布、支付等副作用时,必须触发确认。 - -每个样本都要写清楚验收标准。比如“必须列出来源”“不能修改文件”“测试失败时不能声称完成”。标准越清楚,评估越不容易变成主观感觉。 - -这样你就有了一个小型回归检查。它不保证 Agent 永远正确,但能防止你改了一个地方,悄悄弄坏另一个地方。 - -### 避免幻觉的系统性方法 - -前面几章零散提过幻觉问题,这里做一个系统性的总结。 - -**给明确的依据**:不要让 AI “凭记忆”回答,而是给它具体的文档、代码、数据。 - -```text -不要问:Python 的 sort 函数时间复杂度是多少? -而是问:根据 Python 官方文档(附上文档内容),sort 函数的时间复杂度是多少? -``` - -**要求标注不确定性**:在 Prompt 中明确要求 AI 在不确定时说“我不确定”,而不是编一个答案。 - -```text -如果你不确定,请明确说“我不确定”或“需要进一步验证”。不要猜测。 -``` - -**交叉验证**:让 AI 给出结论后,要求它提供支持这个结论的证据或引用。如果它给不出,结论就不可靠。 - -**分步验证**:不要让 AI 一口气做完所有事情。每做完一步,验证一步。这样即使出错,也能立刻发现并纠正。 - -### 追踪 Agent 的执行过程 - -当 Agent 的执行变得复杂时,你需要能“看到”它在做什么。 - -**日志记录**:在 Agent 的每个主要步骤记录日志:它调用了什么工具、传了什么参数、得到了什么结果。 - -```text -[14:23:01] 调用工具: read_file({ path: "config.json" }) -[14:23:01] 返回结果: { "timeout": 30, "retries": 3 } -[14:23:02] 决策: 需要修改 timeout 值 -[14:23:02] 调用工具: write_file({ path: "config.json", content: "..." }) -``` - -**中间结果检查**:在 Agent 的 Loop 中,定期暂停,检查中间结果是否符合预期。这比等它跑完再检查效率高得多。 - -**回放与重放**:记录 Agent 的完整执行过程(包括每一步的输入和输出),出问题时可以回放,定位是哪一步出了错。 - -**人工抽样**:即使 Agent 跑得很顺,也要定期人工抽查结果。AI 可能在大部分情况下是对的,但偶尔的错误如果没人发现,后果可能很严重。 - -### 评估的几个层次 - -1. **单次输出评估**:这一次回答对不对?有没有幻觉? -2. **流程评估**:Agent 的执行路径是否合理?有没有不必要的步骤? -3. **结果评估**:最终产出是否满足用户需求?质量如何? -4. **效率评估**:花了多少 Token?用了多少时间?成本是否可控? - -对学习者来说,先做好第一层(单次输出评估)就够了。后面三层是进阶内容。 - -## 安全与运行检查 - -Agent 一旦能调用工具,就要把它当作会执行操作的系统来看,而不是只会聊天的模型。 - -上线或长期使用前,至少检查几件事: - -- **权限**:它能读什么、写什么、删除什么?默认权限要保守。 -- **确认点**:发送邮件、发布内容、删除文件、付款、改生产数据之前,必须有人确认。 -- **可观察性**:它每一步调用了什么工具、传了什么参数、得到什么结果,应该能被回看。 -- **成本与延迟**:最长跑多久?最多花多少 Token?超过限制后怎么停下来? -- **失败处理**:工具报错、网络超时、信息不足时,是重试、降级,还是停止并说明原因? -- **敏感信息**:密钥、隐私数据、公司机密是否会进入 Prompt、日志或长期记忆? - -可以把 Agent 的安全目标压成三句话:人能控制它,它的权限有限,它做过什么能被看见。 - -## 案例练习 - -以下是几个真实场景,供你练习和探索。每个场景都没有标准答案,练习时重点看你怎么拆解和处理。 - -### 场景一:会议纪要自动化 - -你有一段会议录音,需要整理成结构化的会议纪要,并提取待办事项。 - -思考方向: -- 你需要哪些工具?(语音转文字、文本分析、文件写入) -- 怎么设计 Agent 的 Loop? -- 哪些步骤需要人工确认? - -### 场景二:代码审查流程 - -你希望 Agent 自动审查 Pull Request 中的代码变更。 - -思考方向: -- Agent 需要读取什么信息?(代码差异、项目规范、历史 PR) -- 怎么让 Agent 给出有价值的审查意见,而不是泛泛而谈? -- 审查结果怎么呈现? - -### 场景三:数据报告生成 - -每天需要从数据库提取数据,生成日报,发送到团队群。 - -思考方向: -- 这个任务适合用 Agent 还是用传统的自动化脚本? -- 如果用 Agent,它的 Loop 该怎么设计? -- 出错了怎么办?(数据异常、发送失败) - -### 场景四:设计稿到代码 - -设计师给了一个 Figma 文件,你需要把它变成可运行的前端代码。 - -思考方向: -- Agent 需要理解 Figma 的什么信息?(布局、颜色、字体、间距) -- 生成的代码怎么保证和设计稿一致? -- 响应式适配怎么做? - -## 反模式汇总 - -前面几章分散讲了一些反模式,这里做一个集中回顾。 - -### Prompt 层面 - -| 反模式 | 问题 | 正确做法 | -|---|---|---| -| Context Bloat | 塞太多上下文,AI 注意力被稀释 | 只给必要信息,复杂信息先摘要 | -| Mega-Prompt | 一条 Prompt 做所有事 | 拆成多步,每步只做一件事 | -| 忽视迭代 | 期望一条 Prompt 就完美 | 把第一轮当起点,多轮对齐 | - -### 思维层面 - -| 反模式 | 问题 | 正确做法 | -|---|---|---| -| 确认偏误 | AI 倾向同意你 | 主动要求反驳,换角度提问 | -| 盲目信任 | 不验证 AI 的输出 | 高风险节点必须人工校验 | -| 模型崇拜 | 只看排行榜选工具 | 顺手可用比排名重要 | - -### AI Coding 层面 - -| 反模式 | 问题 | 正确做法 | -|---|---|---| -| 忽略架构 | 能跑就行,不管可维护性 | 先画蓝图,再砌砖 | -| 上下文爆炸 | 项目大了 AI 失去全局视角 | 保持代码结构清晰,用文档记录 | -| 安全后置 | 出事了才考虑安全 | 从一开始就考虑安全 | - -### Agent 层面 - -| 反模式 | 问题 | 正确做法 | -|---|---|---| -| 过早 Multi-agent | 协调成本 > 收益 | 先用好一个 Agent | -| 过度工程化 | 为了使用而使用 | 够用就行,不追复杂 | -| 权限过大 | Agent 能做太多事 | 最小权限,渐进信任 | -| 抽象 Skill | 规则太空泛,执行时各做各的 | 写清输入、步骤、完成标准 | -| 无回归样本 | 改完不知道有没有退化 | 保留一组固定任务样本 | - -## 小结 - -- Rules 和 Skills 是 Agent 治理的基础。Rules 保证一致性,Skills 提升效率。 -- Skill 要具体,不要抽象;它应该像操作手册,而不是口号。 -- 指令层级:SOUL.md → AGENTS.md → Rules → Prompt,上层约束下层。 -- Multi-agent 不一定能提升效率:先用好一个 Agent,再考虑多个。实现模式有顺序、并行、辩论、分层四种。 -- 验证 AI 输出的方法:事实核查、一致性检查、边界测试、对抗性测试。 -- 避免幻觉:给明确依据、要求标注不确定性、交叉验证、分步验证。 -- 评估要留下任务样本,执行要留下日志,高风险操作要留下确认点。 -- 反模式遍布各个层面:知道什么不该做,和知道什么该做同样重要。 - -## 练习 - -1. 为你正在使用的 AI 工具写一份 Rules 文件(3-5 条规则),观察它对输出一致性的影响。 -2. 设计一个指令层级:写一个 SOUL.md(定义角色)、一个 AGENTS.md(定义能力)、3 条 Rules(定义规则),然后用它们指导 Agent 执行一个任务。 -3. 把一个你常做的任务写成 Skill:说明输入、步骤、完成标准和失败时的处理方式。 -4. 从上面的案例练习中选一个场景,画出 Agent 的工作流程图(包括 Loop、工具调用、人工确认节点)。 -5. 为这个场景设计 5 个任务样本,至少包含 1 个正常任务、1 个边界任务和 1 个高风险任务。 - -## 延伸阅读 - -- [RAG 技术详解:向量检索与混合搜索](../reference/rag-deep-dive.md) -- [同步与异步 Sync & Async](../reference/glossary/sync-async.md) -- [进程与线程 Process & Thread](../reference/glossary/process-and-thread.md) -- [事务 Transaction](../reference/glossary/transaction.md) diff --git a/docs/agentic-ai/agentic-system.md b/docs/agentic-ai/agentic-system.md deleted file mode 100644 index eefde16..0000000 --- a/docs/agentic-ai/agentic-system.md +++ /dev/null @@ -1,288 +0,0 @@ -# Agent 机制与构建(Agentic System) - -前面几章,我们一直在和 AI “对话”。这一章,我们要进入一个新层次:让 AI 自己行动。 - -Agent 不是“更聪明的聊天机器人”。差别在于:它能在后台自主执行多步操作,并根据中间结果调整下一步。 - -要理解 Agent,你需要知道它内部是怎么运转的。 - -## 先判断:脚本、Workflow,还是 Agent? - -做自动化时,不要一上来就问“怎么做 Agent”。先问一个更朴素的问题:这件事的执行路径是不是固定的? - -如果路径固定,优先用脚本或传统自动化。比如每天把一个文件复制到备份目录、把 CSV 转成 Excel、按固定格式重命名图片。这类任务不需要 Agent,写清楚规则就能稳定执行。 - -如果路径大体固定,但其中某一步需要 AI 判断,可以用 Workflow(工作流)。比如“先提取会议录音,再生成摘要,再让人工确认待办事项”。每一步是什么很清楚,只是某些步骤用 AI 来完成。 - -如果路径不固定,Agent 才开始有价值。比如“帮我调查这个 bug 的原因”,它可能先读日志,再搜代码,再跑测试,发现方向不对后换一条路径。这种任务需要根据环境反馈不断调整,固定流程很难提前写死。 - -可以用一个简单的判断: - -- **脚本**:规则明确,输入输出稳定。 -- **Workflow**:步骤明确,中间需要 AI 处理内容。 -- **Agent**:目标明确,但路径需要边走边判断。 - -Agent 的灵活性来自不确定性。它能自己调整,也可能自己跑偏。高风险流程里,可确定的部分仍然应该交给代码、测试和审批;让 Agent 负责探索、调度和补全,而不是替代所有确定性执行。 - -## Agent 内部机制:Loop、Run、Snapshot - -### Loop:感知→思考→行动→观察 - -Agent 的基本结构是一个循环(Loop)。每一轮循环包含四个步骤: - -1. **感知(Perceive)**:读取当前状态:用户说了什么、环境是什么样、上一步的结果是什么。 -2. **思考(Think)**:分析当前状态,决定下一步该做什么。 -3. **行动(Act)**:执行一个具体操作:调用工具、写文件、发请求。 -4. **观察(Observe)**:检查行动的结果,更新状态。 - -然后回到第 1 步,开始下一轮循环。直到任务完成或遇到无法继续的情况。 - -用伪代码表示: - -```text -while (任务未完成) { - 当前状态 = 感知() - 下一步 = 思考(当前状态) - 结果 = 行动(下一步) - 更新状态(结果) -} -``` - -这个循环可能执行 3 次,也可能执行 30 次。每次循环都是一次“试错+调整”的过程。 - -你也可以把 Agent 理解成一个非确定性的 REPL。普通 REPL 是“读入、执行、输出、继续”;Agent 多了一步环境观察,并且下一步怎么做由模型判断。它像一个会自己调度工具的工作流执行器,但这个执行器不是完全确定的。 - -### Run:一次完整的任务执行 - -Run 是指从接收任务到完成(或失败)的整个过程。一次 Run 包含多轮 Loop。 - -比如你让 Agent “帮我整理今天的会议纪要”,一次 Run 可能是: - -- Loop 1:读取会议录音文件。 -- Loop 2:调用语音转文字工具。 -- Loop 3:提取重点信息和待办事项。 -- Loop 4:格式化输出。 -- Loop 5:写入文件。 - -每一轮 Loop 都在推进任务,直到最终完成。 - -### Snapshot:上下文快照 - -Agent 在执行过程中,需要记住很多东西:用户的需求、已经做了什么、中间结果是什么。这些信息的集合就是一个快照(Snapshot)。 - -Snapshot 的作用: - -- **上下文恢复**:如果 Agent 中断了(比如网络问题),可以从 Snapshot 恢复,不需要从头开始。 -- **回退**:如果某一步做错了,可以回退到之前的 Snapshot。 -- **调试**:你可以查看每个 Snapshot,理解 Agent 的决策过程。 - -## Memory:Agent 的记忆系统 - -Snapshot 解决了“执行过程中的状态管理”,但 Agent 还面临另一个问题:它怎么记住过去发生的事情? - -这就是记忆系统(Memory)要解决的问题。 - -### 短期记忆:对话上下文 - -每次和 AI 对话,你们的聊天记录就是短期记忆。模型的上下文窗口(Context Window)决定了它能“记住”多少内容。 - -问题在于:上下文窗口是有限的。当对话变长,早期的内容会被截断。你可能遇到过这种情况:聊了几十轮之后,AI 忘了你最开始说的需求。 - -应对方式: - -- **摘要压缩**:让 AI 定期总结之前的对话,用摘要替代完整历史。 -- **重点信息提取**:把事实、决策、约束单独记录下来,每次请求时带上。 -- **滑动窗口**:只保留最近 N 轮对话,更早的自动丢弃或归档。 - -### 长期记忆:跨会话保存的事实与偏好 - -短期记忆在对话结束后就消失了。长期记忆是跨会话的:Agent 下次启动时,还能检索到你上次告诉它的偏好、项目事实或历史决策。 - -这里要和 Rules 文件、SOUL.md 这类配置区分开。它们更像持久化指令或项目治理规则,用来约束 Agent 怎么工作;长期记忆更像可检索的信息记录,用来保存“你是谁、项目做过什么决定、哪些事实下次还要用”。不要把会变化的动态事实长期塞进规则文件里,否则容易过期,也会把隐私和错误信息固定到每次对话中。 - -实现长期记忆的常见方式: - -- **专门的记忆文件**:把用户偏好、项目事实、历史决策写入可维护的记录文件,并定期清理过期内容。 -- **工作区检索**:如果信息已经在项目文件、日志或文档里,优先让 Agent 实时检索,而不是把内容复制进记忆。 -- **向量数据库**:把文本转换成向量(Embedding),存入数据库。查询时通过语义相似度检索相关内容。这是 RAG 的基本原理,后面的附录会详细讲。 -- **结构化数据库**:把用户偏好、历史决策等存入传统数据库。 - -### 记忆系统的实际问题 - -在构建有记忆的 Agent 时,你会遇到几个现实问题: - -**信息过载**:记住所有东西不现实,也不好用。Agent 需要学会“遗忘”:丢弃过时的、不再相关的信息。 - -**信息冲突**:用户在不同时间说了矛盾的话。Agent 需要判断哪个是最新的、最准确的。 - -**隐私风险**:记住的信息越多,泄露的风险越大。敏感信息不该进入长期记忆。 - -**幻觉放大**:如果 Agent 基于错误的记忆做出判断,错误会被放大。记忆的内容也需要验证。 - -> 提醒:记忆不是越多越好。给 Agent 记住太多东西,反而可能让它变得混乱。重点是记住**正确的东西**,而不是**所有东西**。 - -很多时候,最好的“记忆”不是让 Agent 记住一切,而是让项目本身有清晰结构。文档、日志、目录命名、任务记录写清楚了,Agent 可以随时检索最新的一手信息。长期记忆更适合保存少量稳定偏好和关键决策,不适合变成用户人生履历。 - -## Tool Use:让 AI 真的“动手” - -Agent 要能行动,就需要工具使用(Tool Use)。没有工具,Agent 就只是一个聊天机器人。 - -常见工具: - -- **文件系统**:读写文档、整理资料。 -- **终端命令**:跑脚本、执行构建、安装依赖。 -- **外部 API**:拉取数据、调用服务、发送通知。 -- **浏览器**:访问网页、提取信息。 - -当你告诉 Agent “帮我把这段代码跑一下”,它不是在“模拟”运行,而是真的调用了终端执行命令。 - -一个工具调用的过程: - -```text -Agent 决定需要调用什么工具 - → 构造工具调用的参数 - → 执行工具 - → 获取结果 - → 基于结果决定下一步 -``` - -### 工具设计:把确定性交给工具 - -工具不是越大越好。一个好工具,应该把确定性的事情做稳定,把不确定的判断留给 Agent。 - -设计工具时,可以抓住几个原则: - -- **职责单一**:一个工具只做一类事。`read_file`、`write_file`、`run_test` 比“项目管理工具”更容易被正确调用。 -- **参数清楚**:参数名要直接,类型要稳定。不要让 Agent 在一个长字符串里自己猜路径、命令和选项。 -- **返回可判断**:结果里要能看出成功、失败、错误原因和下一步线索。只返回一段模糊文本,会让 Agent 很难修正。 -- **副作用可控**:读取、预览、写入、删除最好分开。危险操作要放到 Ask 或人工确认之后。 -- **失败可恢复**:工具失败时,要给出明确错误,而不是静默失败。Agent 只有看到问题,才可能调整下一步。 - -这就是为什么高风险流程不能完全靠自然语言描述。自然语言适合表达意图,工具负责把确定动作做稳。 - -> 提醒:工具权限要最小化。能不开放的,就先不开放。你不会给实习生无限的公司权限,同理,Agent 的工具权限也需要控制。 - -## MCP:工具的聚合标准 - -当你有多个工具需要接入 Agent 时,逐个适配会很麻烦。MCP(Model Context Protocol)是一个开放协议,它把各种工具统一成标准格式,让 Agent 能自动理解和调用。你不需要为每个工具写专门的适配代码。 - -## 权限模型:Ask、Skip、Deny - -当 Agent 在后台自主执行时,需要先回答一个问题:哪些操作要你确认? - -一个常见的权限模型是三级: - -- **Ask(询问)**:执行前必须得到你的确认。适用于不可逆操作:删除文件、发送邮件、发布代码。 -- **Skip(跳过)**:不需要确认,直接执行。适用于低风险操作:读取文件、运行测试、格式化代码。 -- **Deny(禁止)**:不允许执行。适用于高风险操作:访问敏感目录、执行危险命令。 - -配置权限的原则: - -- **默认保守**:不确定的操作,默认 Ask。 -- **逐步放开**:用熟了之后,把高频低风险操作改为 Skip。 -- **永远禁止**:某些操作永远不该自动执行,保持 Deny。 - -```text -读取文件 → Skip(低风险,直接执行) -运行测试 → Skip(低风险,直接执行) -修改代码 → Ask(中风险,确认后执行) -删除文件 → Ask(高风险,确认后执行) -格式化硬盘 → Deny(永远不允许) -``` - -## 安全意识 - -Agent 能“动手”意味着它也可能“犯错”。安全不是事后补救,而是从一开始就该考虑的事情。 - -### 零信任 - -不要默认 Agent 的输出都是对的。尤其是: - -- 代码:AI 生成的代码可能有 bug,也可能有安全漏洞。 -- 建议:AI 给出的操作建议可能不适合你的具体场景。 -- 信息:AI 可能基于过时或错误的信息做出判断。 - -原则:永远验证,永远不盲信。 - -### 数据脱敏 - -敏感信息不该直接喂给 Agent: - -- 密码、密钥、Token。 -- 个人隐私数据(身份证号、手机号等)。 -- 公司内部机密。 - -如果必须涉及敏感数据,先做脱敏处理:用占位符替代真实值。 - -### 权限边界 - -上一节讲的权限模型,本质上就是在划定安全边界。Agent 能做什么、不能做什么,需要明确。 - -记住这个原则:Agent 的权限应该小于你的权限。它不需要知道所有事情,也不需要能做所有事情。 - -## 动手练习:实现一个简单的 Agent Harness - -现在,我们动手实现一个最简单的 Agent Harness(Agent 运行框架)。不需要复杂的框架,用最基础的代码就能理解 Agent 的基本机制。 - -用你熟悉的语言(JavaScript、Python、或其他),实现以下功能: - -### 第一步:定义工具 - -创建一个工具注册表,至少包含三个工具: - -- 读取文件 -- 写入文件 -- 执行命令 - -每个工具接收参数,返回执行结果。先用模拟数据,不需要真的读写文件。 - -### 第二步:实现 Loop - -实现一个循环函数,接收用户任务描述,按以下流程执行: - -1. 感知:获取当前状态(任务描述、历史执行记录)。 -2. 思考:决定下一步做什么。调用 LLM,让它根据当前状态选择工具和参数。 -3. 行动:调用对应的工具。 -4. 观察:把工具返回的结果记录到历史中。 -5. 判断:任务是否完成?未完成则回到第 1 步,已完成则返回结果。 - -设置一个最大循环次数,防止死循环。 - -### 第三步:运行 - -用一个具体任务测试你的实现,比如“读取 config.json 并写入备份”。观察它的执行过程:每一轮循环做了什么,历史记录是怎么增长的。 - -### 进阶扩展 - -基础版本跑通之后,试着加入以下功能: - -- **权限检查**:为每个工具设置权限级别(Ask/Skip/Deny),执行前检查是否需要用户确认。 -- **错误处理**:工具调用失败时怎么办?重试?跳过?终止? -- **Snapshot**:每执行 N 步保存一次状态快照,支持回退到之前的快照。 - -这个练习的价值在于:通过亲手实现,你会理解 Agent 的 Loop 是怎么运转的。框架和库只是对这些基本模式的封装。 - -## 小结 - -- Agent 的基本机制是 Loop(感知→思考→行动→观察),一次 Run 包含多轮 Loop。 -- 先判断任务适合脚本、Workflow 还是 Agent;不是所有自动化都需要 Agent。 -- Memory 让 Agent 能“记住”过去的事情,分短期记忆和长期记忆两种。 -- Tool Use 让 Agent 从“能想”变成“能做”。 -- 工具设计要清楚、稳定、可恢复,把确定性的动作交给工具。 -- MCP 把各种工具统一成标准格式,让 Agent 能自动理解和调用。 -- 权限模型(Ask/Skip/Deny)控制 Agent 能做什么、不能做什么。 -- 安全意识:零信任、数据脱敏、最小权限。 -- 动手实现 Agent Harness 是理解 Agent 机制的最好方式。 - -## 练习 - -1. 用你熟悉的编程语言,实现一个简化版的 Agent Loop(参考上面的实现步骤)。 -2. 为你实现的 Agent 加入权限检查:哪些工具调用需要确认,哪些可以直接执行。 -3. 为其中一个工具写清楚输入参数、返回结果、失败时的错误信息。 -4. 思考一个你日常工作中的重复任务,先判断它适合脚本、Workflow 还是 Agent,再画出流程图,标注每一步需要什么工具。 - -## 延伸阅读 - -- [RAG 技术详解:向量检索与混合搜索](../reference/rag-deep-dive.md) — Memory 和 RAG 的技术细节 -- [API 调用与 Function Calling](../reference/api-interaction.md) — Tool Use 的底层实现 -- [安全与安全性 Safety & Security](../reference/glossary/safety-and-security.md) — 安全意识的延伸 diff --git a/docs/agentic-ai/ai-coding.md b/docs/agentic-ai/ai-coding.md deleted file mode 100644 index 791e345..0000000 --- a/docs/agentic-ai/ai-coding.md +++ /dev/null @@ -1,206 +0,0 @@ -# AI Coding 实践(AI Coding) - -“我不会写代码,也能做应用吗?” - -在 AI Coding 的语境下,答案是:可以从“会描述”开始。你不一定要懂所有语法,但要学会把需求说清楚。 - -## 自然语言编程:Chat-to-Code 的基本方式 - -Chat-to-Code 的重点不是“让 AI 代写”,而是“让你更快迭代”。 - -你可以这样开始: - -1. **先描述目标**(功能、范围、限制)。 -2. **要最小可用版本**(MVP)。 -3. **再逐步加功能**。 - -例如: - -```text -请用 JavaScript 写一个 3 秒倒计时的示例,输出到控制台,不要使用外部库。 -``` - -可能得到这样的代码: - -```javascript -let count = 3; -const timer = setInterval(() => { - console.log(count); - count -= 1; - if (count === 0) { - clearInterval(timer); - console.log("start"); - } -}, 1000); -``` - -然后你可以继续迭代: - -```text -很好,现在改成从页面上的按钮触发倒计时,倒计时数字显示在按钮上。 -``` - -每一步都小而可控。先跑通,再优化。 - -> 提醒:AI 生成的代码也可能有 bug,务必运行并验证。不要因为是 AI 写的就默认它是对的。 - -## 环境配置:让协作更顺滑 - -想把 AI Coding 用得顺手,环境配置会直接影响体验。 - -常见组合: - -- **VS Code + Copilot**:适合已有开发环境的人,代码补全体验好。 -- **Cursor / Windsurf**:AI 原生编辑器,对话和代码修改更紧密。 -- **Claude Code / Gemini CLI**:命令行 Agent,适合喜欢终端的开发者。 - -你不需要一下子切换所有工具。先从“让 AI 进编辑器”开始,就已经是质变。选一个顺手的,用熟了再说。 - -## 项目实战:从 0 到 1 的小应用 - -练习的目标不是做大项目,而是跑通流程。比如做一个“每日任务清单”: - -1. **定义需求**:添加/完成/删除任务。 -2. **拆分模块**:数据结构、UI、事件处理。 -3. **先做最小版本**:能新增和显示即可。 -4. **逐步增强**:加入持久化和样式。 - -AI 可以帮你写代码,但需求拆解仍然要你来做。你负责方向,AI 负责实现。 - -## 架构优先:先想清楚结构,再写代码 - -在开始写代码之前,花 5 分钟想清楚架构,能省下后面几小时的返工。 - -什么是架构?简单说就是: - -- **数据怎么存**:用什么数据结构?存在哪里? -- **模块怎么分**:哪些功能放在一起?哪些要分开? -- **接口怎么定**:模块之间怎么通信? - -你可以先让 AI 帮你画架构。 - -```text -我要做一个记账 App。请先给出技术方案:用什么框架、怎么组织文件结构、数据怎么存储。不要写代码,先讨论方案。 -``` - -等方案讨论清楚了,再开始写代码。这比直接让 AI “帮我写一个记账 App” 效果好得多。 - -### Plan Mode:先规划,再动手 - -一些 AI 编程工具(比如 Claude Code)提供了 Plan Mode:一个专门用来“想”的阶段,和“做”的阶段分开。 - -在 Plan Mode 下,AI 只做三件事: - -1. **探索**:读代码、搜文件、理解现有结构。 -2. **设计**:分析需求,提出实现方案,列出关键文件和步骤。 -3. **确认**:把方案呈现给你,等你批准后再动手写代码。 - -Plan Mode 的价值在于:你能在 AI 动手之前就知道它打算怎么做。如果方案有问题,这个阶段改方案的成本最低:改一行方案描述,比改十行已经写好的代码便宜得多。 - -```text -# 进入 Plan Mode -> 我想给这个项目加一个用户登录功能。先不写代码,帮我分析一下需要改哪些文件、用什么方案。 - -# AI 分析完,你确认方案后 -# 退出 Plan Mode,进入实现阶段 -> 好,按刚才的方案开始实现。 -``` - -这和“架构优先”是同一个思路:先想清楚,再动手。Plan Mode 只是把这个思路工具化了。 - -### 自主编码循环:持续实现、验证和修正 - -在 AI 编程里,更通用的说法是自主编码循环(autonomous coding loop)或编码 Agent 的执行-反馈循环(agentic coding loop)。它指的是:让 AI 围绕同一个目标持续工作,写代码、运行验证、根据错误修正,再进入下一轮,直到达到明确的验收标准。 - -有些社区会把其中一种做法叫 Ralph Loop 或 Wiggum loop。这个名字来自《辛普森一家》里的 Ralph Wiggum:一个总是犯迷糊、总是出错、但从不放弃的小男孩。这个说法可以当作案例理解,但它不是初学者必须掌握的基础术语。先记住“计划、执行、验证、修正”这条循环,更有助于继续学习。 - -传统的 AI 编程流程是: - -```text -你:帮我实现这个功能。 -AI:写了一段代码。 -你:有 bug。 -AI:修了这个 bug。 -你:还有别的问题。 -AI:(上下文不够了,或者放弃了) -``` - -自主编码循环的流程是: - -```text -你:帮我实现这个功能。遇到错误就修,直到所有测试通过、所有需求满足。 -AI:写了一段代码 → 跑测试 → 发现 2 个失败 → 修复 → 再跑 → 还有 1 个失败 → 修复 → 全部通过。 -``` - -区别在于:你不需要在每一步都手动介入。你给 AI 一个明确的目标和验收标准,它自己循环执行,直到达标。 - -怎么用好自主编码循环: - -- **给明确的验收标准**:“所有测试通过”“能正常运行”“没有 TypeScript 类型错误”。标准越明确,AI 越不容易“偷懒”。 -- **给最大循环次数**:防止 AI 在同一个问题上死循环。“最多尝试 10 次,如果还不行就停下来告诉我卡在哪里。” -- **每一步都要验证**:AI 不能只“说”它修好了,必须跑测试、跑代码来证明。 - -自主编码循环适合的场景:修 bug、补测试、代码迁移、格式化等有明确“完成标准”的任务。不太适合需要设计判断的任务(比如“帮我设计一个好看的 UI”)。 - -> 提醒:自主编码循环的“不放弃”不等于“不思考”。如果 AI 在同一个问题上连续失败 3 次以上,通常是它理解错了需求,而不是“再试一次就好了”。这时候应该停下来,重新描述问题。 - -## 局限与注意事项 - -AI Coding 很强大,但它有明确的边界。了解这些边界,才能用好它。 - -### 架构债务 - -AI 倾向于“能跑就行”。它不会主动考虑代码的可维护性、可扩展性、可测试性。 - -如果你一直让 AI “加功能”而不重构,项目会越来越臃肿,最终变成一个难以维护的“技术债务”。 - -应对:定期让 AI 帮你重构。每隔一段时间,停下来问一句:“这段代码有什么可以优化的?” - -### 上下文爆炸 - -当项目变大,AI 会失去全局视角。它可能不知道你 3 天前写的那个函数是干什么的,也不知道你昨天改的那个配置会影响什么。 - -这是目前所有 AI 编辑器的共同局限。 - -应对:保持代码结构清晰,用注释和文档记录关键决策。不要完全依赖 AI 的“记忆力”。 - -### 确认偏误在编码中的体现 - -还记得上一章提到的确认偏误吗?在 AI Coding 中,它表现为: - -- 你说“这个函数对吗?”AI 大概率说“看起来没问题”。 -- 你说“帮我加这个功能”,AI 不会主动说“这个需求本身有问题”。 - -应对:主动要求 AI 审查你的代码和需求。“这段代码有什么潜在问题?”“这个需求有没有更好的实现方式?” - -### 安全盲区 - -AI 不会主动考虑安全问题。它可能生成: - -- 没有输入验证的表单。 -- 硬编码的密钥和密码。 -- 没有权限检查的 API。 - -应对:在 Prompt 中明确安全要求。“请加上输入验证”“不要硬编码任何敏感信息”“需要做权限检查”。 - -## 小结 - -- AI Coding 的重点是会表达:把需求说清楚,AI 帮你实现。 -- 最小可用加迭代是常用节奏,先跑通再优化。 -- 架构优先:先想清楚结构,再写代码。 -- Plan Mode 把“规划”和“实现”分开,让你在 AI 动手之前就能审核方案。 -- 自主编码循环让 AI 持续工作直到达标,适合有明确验收标准的任务。 -- 了解边界:架构债务、上下文爆炸、确认偏误、安全盲区,都是真实存在的问题。 -- 应用尽用:能用 AI Coding 解决的问题就用,但要知道它不适合什么。 - -## 练习 - -1. 写一个“最小可用版本”的需求描述(不超过 80 字),让 AI 生成代码并运行。 -2. 在让 AI 写代码之前,先让它给出技术方案(框架选择、文件结构、数据存储)。对比“先方案后代码”和“直接写代码”的区别。 -3. 找一段 AI 生成的代码,主动要求 AI 审查它:“这段代码有什么潜在问题?有没有安全隐患?” - -## 延伸阅读 - -- [调试 Debug](../reference/glossary/debug.md) -- [测试 Testing](../reference/glossary/testing.md) -- [安全与安全性 Safety & Security](../reference/glossary/safety-and-security.md) diff --git a/docs/agentic-ai/ai-essentials.md b/docs/agentic-ai/ai-essentials.md deleted file mode 100644 index a9279f4..0000000 --- a/docs/agentic-ai/ai-essentials.md +++ /dev/null @@ -1,163 +0,0 @@ -# AI 协同基础(AI Essentials) - -这一章讲的是和 AI 协作的基础能力。不管技术怎么变,底层逻辑是一样的:你表达得越清楚,结果就越靠谱。 - -## 思维转变:从搜索到生成 - -搜索引擎是在“找答案”,而大模型是在“生成答案”。这两件事看起来很像,但本质不同。 - -- 搜索强调已存在的事实。搜索引擎给你的是别人写过的东西。 -- 生成强调合理的可能性。大模型给你的是“听起来最像答案”的内容。 - -这也解释了为什么它有时会”答非所问”:它在努力生成最像答案的内容,而不是最权威的答案。 - -怎么办?一个实用的做法:先让它给框架,再让它补细节。 - -```text -你先列出 3 种可能的解释,每一种给出适用场景和潜在风险。 -``` - -你能在框架阶段就判断方向对不对,而不是等它写了一大段才发现走偏了。 - -### 输出的随机性 - -LLM 生成每个词时,是从一个概率分布中采样,而不是每次都选概率最高的那个词。这就是为什么同一个问题问两遍,回答不会完全一样。 - -这个采样过程可以通过参数来控制: - -- **temperature**:值越低,输出越确定;值越高,输出越随机。 -- **top_p**:只从累积概率最高的那部分词中选择,过滤掉最不可能的词。 - -这些参数影响的是"怎么选词",不影响模型理解问题的能力。在聊天界面里你通常看不到它们——它们被隐藏了,对大多数读者来说,不需要手动调整这些参数;知道它们存在即可。但在用 API 构建应用时,可以调整它们来控制输出特性。Agent 框架也会帮你管理这些参数,所以大多数时候你不需要手动设置。 - -## 把需求说清楚:Prompting 的四要素 - -很多人把 Prompt 当作“提问”,其实更像下达任务。 - -一个清晰的指令,通常包含 4 个要素: - -- **目标(Goal)**:要完成什么。 -- **背景(Context)**:有哪些已知条件。 -- **约束(Constraint)**:不能做什么、必须符合什么。 -- **格式(Format)**:输出成表格、清单还是代码。 - -可以用一个简单的模板: - -```text -目标:帮我写一封请假邮件。 -背景:我下周三需要去医院做体检。 -约束:语气正式,不超过 100 字。 -格式:直接输出邮件正文。 -``` - -别担心一开始写得生硬。关键是可复用:当你发现某个 Prompt 效果好,下次可以直接复用或微调。 - -### Prompting 不是背模板 - -很多 Prompt 模板看起来很神奇,但它们有用的部分,通常不是某个固定句式,而是把规则说清楚了。 - -规则越准确、清晰、具体,AI 越不容易发散。比如“帮我写得专业一点”就很含糊;“面向公司内部周报,语气客观,不夸张,不超过 120 字”就更容易执行。 - -这也是为什么 AGENTS.md、Rules、Skills 本质上仍然离不开 Prompting。它们只是把临时的一句话,变成了可复用的规则。规则写得越抽象,越容易失效;规则写得越贴近具体任务,越容易稳定复现。 - -## 多轮迭代:对齐的过程 - -你有没有发现,和 AI 对话往往不是一轮就结束的? - -第一轮:它给的结果可能方向对,但细节不对。第二轮:你指出哪里不对,它调整。第三轮:越来越接近你想要的。 - -这个过程不是“失败”,而是对齐(Alignment)。你和 AI 在通过多轮对话,逐步把双方的理解拉到同一条线上。 - -想想你和人协作也是这样:第一版草稿很少就是最终版。每一轮都在缩小差距。 - -你可以按这个节奏来: - -1. **第一轮**:给简要描述,看大方向对不对。 -2. **第二轮**:基于结果,指出具体要调整的地方。 -3. **第三轮**:补充约束或格式要求,精修细节。 - -> 提醒:如果结果不对,不要只说“错了”或“再改改”。要指出哪里不符合你的预期:是方向偏了?细节不对?还是格式不符合要求?越具体,AI 越容易调整。 - -## 场景实战:写作、分析、决策辅助 - -AI 最常见的三类用法: - -### 写作 - -先给框架,再润色语言。 - -```text -帮我写一段产品介绍:目标用户是大学生,产品是一个记账 App,风格轻松活泼,不超过 150 字。 -``` - -### 分析 - -先列变量,再比较方案。 - -```text -我在考虑 A 和 B 两个方案。请按“成本、风险、时间、可控性”四个维度对比,用表格呈现,并给出一句话建议。 -``` - -### 决策辅助 - -先列利弊,再给风险提示。 - -```text -我要决定是否跳槽到一家创业公司。请列出 3 个可能的好处和 3 个潜在风险,并给出一个决策框架。 -``` - -AI 不一定给你最终答案,但能帮你把问题结构化。让 AI 帮你搭架子,你负责最后拍板。 - -## 常见陷阱 - -在学习 Prompting 的过程中,有几个常见的错误值得提前了解。 - -### Context Bloat(上下文膨胀) - -有些人觉得“给 AI 的信息越多越好”。于是把整篇文章、所有背景、全部历史对话都塞进去。 - -结果呢?AI 反而变得不聚焦,输出质量下降。 - -原因很简单:过多的上下文会稀释关键信息。AI 的注意力是有限的,无关信息越多,它越容易走偏。 - -正确做法:只给必要的上下文。如果信息量大,先自己做一轮筛选或摘要,再喂给 AI。 - -### Mega-Prompt(巨型提示词) - -另一个极端:试图在一条 Prompt 里塞进所有需求:目标、背景、约束、格式、示例、边界条件…… - -一条 Prompt 做所有事,结果往往是:AI 要么顾此失彼,要么理解偏差被放大。这里注意于 Context Bloat 的区别,Context Bloat 是塞了太多背景材料,Mega-Prompt 是在一条 Prompt 里堆了太多指令。 - -正确做法:把多步骤任务拆开。每一步只做一件事,每一步的输出是下一步的输入。这样每一步都可控、可验证。 - -### 忽视迭代 - -有些人期望“一条 Prompt 就能得到完美结果”。如果不行,就换一个工具或换一个模型。 - -问题常常在于:你没有给 AI 调整的机会。 - -正确做法:把第一轮的结果当作起点,而不是终点。多轮迭代是常态,不是失败。 - -## 小结 - -- LLM 是在"生成"答案,不是在"查找"答案。输出是概率性的,同一个问题问两遍结果不会完全一样。 -- temperature 和 top_p 控制输出的随机性。聊天界面隐藏了这些参数,API 和 Agent 框架可以调整它们。 -- Prompting 是一切的基础:精确表达意图,才能得到精确的结果。 -- 四要素:目标、背景、约束、格式。结构化表达才能稳定复现。 -- Prompting 的重点不是模板,而是准确、清晰、具体的规则。 -- 多轮迭代就是对齐过程:每一轮都在缩小理解差距。 -- 常见陷阱:Context Bloat(信息过多)、Mega-Prompt(一 prompt 做所有事)、忽视迭代。 -- AI 的常见价值:帮你把问题结构化,而不是替你做决定。 - -## 练习 - -1. 用“目标/背景/约束/格式”四要素写一个完整的 Prompt,然后用它和 AI 对话,记录需要几轮迭代才能得到满意结果。 -2. 找一个你之前写过的 Prompt(或者回忆一次和 AI 的对话),分析它是否存在 Context Bloat 或 Mega-Prompt 的问题,然后改写它。 -3. 把一句抽象指令改成具体规则,比如把“写得好一点”改成可执行的语气、长度、结构要求。 -4. 用“先框架后细节”的策略,让 AI 帮你分析一个你最近在纠结的小决定。 - -## 延伸阅读 - -- [调试 Debug](../reference/glossary/debug.md) -- [测试 Testing](../reference/glossary/testing.md) -- [安全与安全性 Safety & Security](../reference/glossary/safety-and-security.md) diff --git a/docs/agentic-ai/index.md b/docs/agentic-ai/index.md deleted file mode 100644 index de923b7..0000000 --- a/docs/agentic-ai/index.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -layout: home -title: "Agentic AI" -hero: - name: Agentic AI 入门指南 - text: 从对话到 Agent - tagline: 使用 Agentic AI 高效完成工作 - image: - src: /agentic-ai.png - actions: - - text: 开始阅读 → - link: /agentic-ai/intro - theme: brand -features: - - icon: 🗺️ - title: 看懂 AI 工具地图 - details: 从 ChatGPT、RAG、Workflow 到 Agent,先弄清每类工具适合解决什么问题,避免被新名词牵着走。 - - icon: ✍️ - title: 学会高质量表达需求 - details: 用目标、上下文、约束和格式组织 Prompt,把“试试看”变成可复用、可调整的 AI 协作方法。 - - icon: ⚙️ - title: 把重复工作做成流程 - details: 学会设计 Agentic workflow,让 AI 帮你整理资料、生成报告、处理多步任务,而不是只停留在聊天。 - - icon: 💻 - title: 用自然语言参与编程 - details: 从需求描述、方案规划到测试修复,理解 AI Coding 的协作节奏,用 Agent 帮你推进真实项目。 - - icon: 🎨 - title: 进入多模态创作 - details: 了解图像、视频、音频生成如何接入文本工作流,把想法变成能继续加工的素材和方案。 - - icon: 🛡️ - title: 让自动化保持可控 - details: 学会权限确认、Human-in-the-loop、任务样本、Memory 边界和结果验证,让 Agent 能做事,也能被检查。 ---- diff --git a/docs/agentic-ai/intro.md b/docs/agentic-ai/intro.md deleted file mode 100644 index 18c959a..0000000 --- a/docs/agentic-ai/intro.md +++ /dev/null @@ -1,156 +0,0 @@ -# Agentic AI:从对话到 Agent - -2022 年末,ChatGPT 让很多人第一次直观感受到 AI 的能力。紧接着,新名词一波接一波:Prompt Engineering、Agent、RAG、CoT、MCP…… - -你可能还在理解“大模型”,大家就已经在聊 Agentic AI(智能体)了。会迷糊吗?很正常。但好消息是:你不需要搞懂所有名词,也能开始用起来。 - -## Agent 是什么? - -先说清楚一个经常被混淆的概念:不是所有调用了 AI 的系统都叫 Agent。 - -一个 Agent 通常需要同时满足四个条件: - -- **目标驱动(Goal-driven)**:你给它一个意图,而不是一步一步的指令。比如“帮我整理今天的会议纪要”,而不是“先打开文件,再找到第三段,再复制……” -- **自主规划(Autonomous Planning)**:它自己决定怎么拆解任务、按什么顺序执行。 -- **工具使用(Tool Use)**:它能调用外部工具,比如读写文件、执行命令、访问 API,把想法变成结果。 -- **环境适应(Environmental Adaptation)**:它能根据中间结果调整策略。执行到一半发现路不通,会自己换一条路。 - -这四个条件缺一不可。再看几种常见的误解: - -一个只能问答的聊天机器人,即使背后调用了大模型 API,也不算 Agent:它没有自主规划和工具使用。一个预设好的自动化流程(比如“每天早上 8 点发邮件”),也不算 Agent:它缺少环境适应能力,遇到意外就卡住了。RAG 系统(检索增强生成)同样不算:它只是在做信息检索和总结,没有自主规划和行动能力。 - -这些系统都有各自的价值,但理解它们和 Agent 的区别,能帮你判断“什么时候该用什么”。 - -一个实用判断是:能用普通对话解决,就先用普通对话;能用固定流程解决,就先用固定流程;只有当任务需要根据中间结果不断调整路径时,再考虑 Agent。 - -## 不要焦虑 - -在正式开始之前,我想先说一件最重要的事:不要焦虑。 - -你可能会觉得:新工具这么多、模型更新这么快、别人都在用最新的东西了,我是不是落后了? - -答案是:没有。 - -模型排行榜每个月都在变,今天的第一名明天可能就被超越。对学习者来说,要紧的不是“哪个模型最强”,而是你能不能用它解决问题。 - -能用、好用、顺手用,就够了。不需要追新,不需要追最贵的。工具会过时,但你建立的思维方式和技能不会。 - -更实际的做法是:先把 AI 用进你手边的小任务里。让它帮你整理一段会议记录、改一封邮件、分析一个表格、写一个小脚本。你会很快知道它适合什么、不适合什么。 - -但“用起来”不等于“什么都交给 Agent”。Agent 处理的是自然语言和概率式判断,天然会有不稳定的一面。对高风险流程来说,可确定的部分仍然应该交给脚本、测试、审批和清晰的规则。AI 适合探索、整理、生成和调度;必须稳定执行的部分,要尽量落到确定性的流程里。 - -## Prompting 是一切的基础 - -不管你用的是最基础的聊天机器人,还是最复杂的 Agent 系统,有一件事始终不变:你需要把意图说清楚。 - -这就是 Prompting(提示):用精确的语言告诉 AI 你想要什么。 - -很多人觉得 Prompting 就是“写一条好问题”。其实不止。Prompting 是一种沟通能力:你想要什么结果?有哪些背景信息?有什么限制?期望什么样的输出? - -后面我们会详细展开。但现在请记住:不管技术怎么演进,Prompting 始终是一切能力的底层。MCP、Skills、Multi-agent 这些概念,都建立在你能精确表达意图的基础上。 - -## 升级对齐定律:底层能力会重置技巧优势 - -学习 AI 很容易陷入一种误区:今天学了一个复杂技巧,明天又冒出一个新工具,好像永远追不上。 - -这里有一个更适合长期记住的规律:一旦底层模型升级,很多围绕旧能力缺口发展出来的技巧,就会变成不那么要紧的细节。大家会被重新拉到接近同一个起点。 - -这就是升级对齐定律,也可以叫升级归零定律。 - -图像生成就是一个很直观的例子。早期你可能需要记一大串提示词、参数、风格权重,甚至要训练 LoRA、反复 inpainting。模型升级以后,同样的需求可能一句话就能完成。以前的技巧不是完全没用,而是从“必须掌握”变成了“需要时再用”。 - -Agent 也是一样。上下文变长、模型推理变强、工具调用更稳定以后,一些复杂的 Harness 设计、提示词花活、上下文压缩技巧,都会被底层能力吃掉一部分。你不需要因为没掌握每个流行词就焦虑,因为很多流行词本来就是在补旧模型的短板。 - -这并不意味着学习没用了。留下来的能力更基础:你是否能把问题说清楚,是否能判断结果对不对,是否能把不稳定的部分和确定性的流程分开。工具会升级,旧技巧会归零,但这些能力不会归零。 - -## 技能比工具重要 - -技术变化很快。今天的热门工具,明天可能就被替代。但有些东西不会过时: - -- 拆解问题的能力:不管用什么工具,你都需要先把问题想清楚。 -- 精确表达的能力:不管模型怎么进化,你都需要说清楚你要什么。 -- 迭代优化的能力:第一次结果不满意时,知道怎么调整。 -- 判断结果的能力:AI 给了答案,你能判断它对不对。 - -这些是技能,不是工具。工具会变,技能不会。 - -所以,学习本教程的目标不是“掌握某个工具”,而是建立一套思维方式:怎么发现问题、怎么拆解问题、怎么和 AI 协作解决问题。 - -## 教程大纲 - -本教程分为六个部分,外加技术附录。前三部分面向所有用户,后三部分面向开发者与进阶创造者。 - -### 第一部分:AI 协同基础(AI Essentials) -* 思维转变:从“搜索”到“生成”,理解 LLM 的概率式输出。 -* 把需求说清楚:Prompting 四要素:目标、上下文、约束、格式。 -* 迭代对齐:多轮对话的本质是不断对齐理解。 -* 常见陷阱:Context Bloat、Mega-Prompt 等反模式。 - -### 第二部分:多模态创作能力(Multimodal Creativity) -* 升级对齐定律:底层模型升级如何降低技巧门槛。 -* 视觉创作与理解:图像生成、截图分析、图表解读与设计反馈。 -* 动态叙事:视频生成与镜头语言。 -* 工作流整合:从文本到多媒体的创作链路。 - -### 第三部分:高级 Prompting 与推理增强(Advanced Prompting & Reasoning) -* Chain-of-thought:把复杂问题拆成可检查的假设、依据和结论。 -* ReAct:让 AI 在推理、行动和观察之间循环推进任务。 -* RAG:先检索外部知识,再基于资料生成回答。 -* 模型原生推理:理解强推理模型如何改变提示词写法。 -* 准确性控制:幻觉、确认偏误和高风险节点的验证方法。 - ---- *(以下章节面向开发者与进阶创造者)* --- - -### 第四部分:AI Coding 实践(AI Coding) -* 自然语言编程:Chat-to-Code 的基本模式。 -* 环境配置:Cursor / Windsurf / VS Code + Copilot。 -* 项目实战:从零构建并部署一个 Web 应用。 -* 局限与边界:架构债务、上下文爆炸、安全盲区。 - -### 第五部分:Agent 机制与构建(Agentic System) -* 从脚本、Workflow 到 Agent:先判断什么时候需要 Agent。 -* Agent 内部机制:Loop(感知→思考→行动→观察)、Run、Snapshot。 -* Memory:短期记忆、长期记忆与工作区检索的边界。 -* Tool Use 与 MCP:模型如何与外部世界交互,以及工具该怎么设计。 -* 权限模型:Ask / Skip / Deny。 -* 安全意识:零信任、数据脱敏、权限边界。 -* 动手练习:自己实现一个简单的 Agent Harness。 - -### 第六部分:Agent 实践与治理(Agent Practice) -* 配置与治理:Rules、Skills 的使用。 -* 指令层级:SOUL.md → AGENTS.md → Rules → Prompt。 -* Multi-agent:实现模式(顺序、并行、辩论、分层)与适用场景。 -* 评估与调试:任务样本、过程追踪、回归检查。 -* 安全与生产化检查:权限、成本、延迟、失败处理。 -* M×N Gateway:企业级 Agent 集成的概念。 - -### 技术附录 -* API 调用与 Function Calling:用 JavaScript 调用 LLM、Embedding、Image、Audio API。 -* RAG 技术详解:向量嵌入、HNSW 索引、混合搜索策略。 - -## 反馈与建议 - -如果发现教程中有过时、错误或不清楚的地方,或者有任何问题和建议,欢迎通过 [GitHub Issues](https://github.com/IntroToProgramming/IntroductionToProgramming/issues/new?labels=Agentic+AI) 提交反馈。 - -## 小结 - -- Agentic AI 的变化,是从“对话”走向“行动”。 -- Agent 需要同时满足目标驱动、自主规划、工具使用、环境适应四个条件。 -- Prompting 是一切的基础:不管技术怎么演进,精确表达始终有用。 -- 升级对齐定律提醒我们:底层模型升级会让很多旧技巧变成细节,把大家重新拉到接近同一个起点。 -- 技能比工具重要:建立思维方式,而非记忆工具。 -- 高风险流程要保留确定性:能用脚本、测试和审批说清楚的部分,不要全交给 Agent 猜。 -- 不要焦虑:Utilization is all you need。 - -## 练习 - -1. 用一句话描述一个你希望 AI 帮你完成的小任务,并补充 2 条限制条件。 -2. 把一个“看起来很大”的目标拆成 3 个可执行的步骤。 -3. 选一个你想自动化的任务,判断它更适合普通对话、固定流程,还是 Agent,并写下理由。 -4. 回想你最近一次和 AI 聊天的经历:你是怎么表达需求的?结果满意吗?如果不满意,问题出在哪里? - -## 延伸阅读 - -* [调试 Debug](../reference/glossary/debug.md) -* [测试 Testing](../reference/glossary/testing.md) -* [安全与安全性 Safety & Security](../reference/glossary/safety-and-security.md) diff --git a/docs/agentic-ai/multimodal-creativity.md b/docs/agentic-ai/multimodal-creativity.md deleted file mode 100644 index a653c68..0000000 --- a/docs/agentic-ai/multimodal-creativity.md +++ /dev/null @@ -1,215 +0,0 @@ -# 多模态创作能力(Multimodal Creativity) - -很多人第一次被 AI 震撼,不是因为“它会写字”,而是因为它能画图、剪视频、做海报。这就是多模态(Multimodal)的魅力。 - -但多模态创作远没有“说一句话就出图”那么简单。背后涉及表达方式的演进、控制精度的提升、以及工作流的搭建。这一章,我们把这些事情讲清楚。 - -## 图像生成的演进:从调参数到门槛重置 - -图像生成工具的演进,很能说明升级对齐定律:底层模型一旦升级,很多旧技巧就会从“必须掌握”变成“有需要时再用”。 - -### 第一阶段:复杂提示词时代 - -早期的图像生成工具(如 Midjourney、Stable Diffusion)对提示词极其敏感。你可能需要写这样的东西: - -```text -A cyberpunk cityscape at dusk, neon lights reflecting on wet streets, -cinematic lighting, ultra-detailed, 8k, volumetric fog, -shot on Sony A7III, 35mm lens, shallow depth of field, ---ar 16:9 --v 5.2 --style raw -``` - -每一个参数都有意义,每一个词都可能影响结果。稍微调整一下“volumetric fog”的位置,出来的图可能完全不同。 - -这时候,技巧门槛很高。你需要了解工具的内部逻辑,记住各种参数的含义,反复调试才能得到满意的结果。 - -### 第二阶段:结构化控制时代 - -随着技术发展,出现了更精细的控制方式。你可以: - -- 指定人物的姿势(通过骨架控制) -- 指定画面的构图(通过参考图) -- 指定风格的权重(通过风格混合) -- 指定局部区域的修改(通过区域控制) - -控制方式变得更结构化了。你不再需要在提示词里堆砌大量描述,而是通过更直观的方式告诉 AI 你想要什么。但你仍然需要理解这些控制工具的工作原理。 - -### 第三阶段:模型升级后的门槛重置 - -最新的多模态模型已经能做到:一句话生成质量不错的图片。 - -```text -画一张赛博朋克风格的城市夜景,要有霓虹灯和雨后的街道 -``` - -很多时候,不需要参数,不需要技术术语,也不需要反复调试。模型会自动补齐一部分过去需要你手动控制的细节。 - -这就是升级对齐定律在图像生成里的表现:底层模型升级以后,旧时代的大量技巧被重新定价。它们不是完全消失,而是不再是每个人入门时必须跨过的门槛。 - -### 那还需要学图像生成的技巧吗? - -需要,但重点变了。 - -早期你需要学的是“工具怎么用”:参数怎么调、模型怎么选。现在你需要学的是“意图怎么表达”:你要什么、给什么参考、怎么描述清楚。 - -工具的复杂度在降低,但表达的精确度要求没有降低。这和上一章讲的“Prompting 是一切的基础”是一回事。模型会把很多旧技巧变成细节,但不会替你决定你到底想表达什么。 - -## 视觉创作:图像生成的基本技能 - -虽然工具在进化,但有些基本技能始终有用。 - -### 给明确的主体 - -图像生成最怕的是模糊。“画一张好看的图”不如“画一只在书桌上睡觉的橘猫”。 - -主体越明确,AI 越不容易“自由发挥”到你不需要的方向。 - -### 给参考材料 - -比起用文字描述所有细节,给一张参考图往往更有效。 - -- 想要某种风格?给一张风格参考。 -- 想要某种构图?给一张构图参考。 -- 想要某个角色的姿势?给一张姿势参考。 - -多模态模型能理解图片,这是它最大的优势之一。给素材,比给想象更可靠。 - -### 给约束条件 - -明确告诉你不想要什么,和告诉它你想要什么同样重要。 - -- “不要出现文字” -- “背景不要太复杂” -- “色调偏冷” - -约束能有效减少 AI 的“自由发挥空间”,让结果更可控。 - -### 分步迭代 - -不要期望一次就得到完美结果。试试这个流程: - -1. **先出大方向**:用简单的描述生成初稿,看 AI 的理解是否和你一致。 -2. **再调整细节**:基于初稿,告诉 AI 哪里需要改:“人物再大一点”“背景换成夜景”。 -3. **最后精修**:对满意的版本做局部微调。 - -这个过程和写代码很像:先跑通 MVP,再迭代优化。 - -## 视觉理解:AI 也能读懂图片 - -多模态不只是“生成图片”。另一类同样重要的能力,是让 AI 识别和理解图片。 - -你可以把一张图片发给模型,让它回答: - -- 这张图里有什么? -- 这张截图里的报错是什么意思? -- 这张表格或图表表达了什么趋势? -- 这张设计稿里有哪些信息层级和排版问题? - -这类能力通常叫视觉理解(Vision Understanding)或图像理解(Image Understanding)。它让 AI 从只能处理文字,变成可以处理“文字 + 图像”的上下文。 - -### 视觉理解适合做什么? - -最常见的场景有几类。 - -**读截图**:把报错截图、产品界面、网页截图发给 AI,让它帮你定位信息、解释界面状态、提出修改建议。 - -**读图表**:把柱状图、折线图、仪表盘截图发给 AI,让它总结趋势,指出异常点,再转成文字报告。 - -**读资料**:把白板照片、手写笔记、PPT 截图发给 AI,让它整理成结构化文本。 - -**读设计**:把海报、封面、UI 设计稿发给 AI,让它检查布局、视觉重点、文案层级和一致性。 - -这里的关键不是“AI 会看图”本身,而是它可以把图像内容接入后续推理:先看懂图片,再解释、归纳、改写、生成下一步方案。 - -### 视觉理解的限制 - -视觉模型很强,但不要把它当成精确测量工具。 - -- 它可能看错细小文字、数字、小图标。 -- 它可能无法准确判断真实尺寸、距离、材质。 -- 它对专业图像的理解需要人来复核,比如医学影像、法律证据、工程图纸。 -- 它能描述图片内容,但不等于拥有图片来源、版权、拍摄时间等外部事实。 - -所以更稳妥的用法是:让 AI 做第一轮观察、整理和解释,再由人确认关键结论。 - -## 动态叙事:视频生成与镜头语言 - -视频生成不是“把图动起来”,而是“把故事拆成镜头”。 - -### 镜头语言基础 - -你需要学会用镜头语言思考: - -- **景别**:远景(展示环境)、中景(展示动作)、近景(展示表情) -- **镜头运动**:推(靠近)、拉(远离)、摇(水平转)、移(跟随) -- **节奏**:快切(紧张)、慢切(舒缓)、长镜头(沉浸) - -一个最小可用的分镜清单: - -```text -镜头 1:城市夜景远景,霓虹灯闪烁,轻微推镜。 -镜头 2:近景,女生抬头看雨,表情平静。 -镜头 3:中景,女生转身离开,雨声变大。 -``` - -### 视频生成的现实 - -坦率地说,目前的 AI 视频生成还处于早期阶段。你能做的是: - -- 用 AI 生成分镜脚本:这一步 AI 很擅长。 -- 用 AI 生成单帧画面:用来做参考或素材。 -- 用 AI 辅助剪辑和节奏:但最终的视觉把控还是需要人来完成。 - -不要期望“说一句话就出完整视频”。但也不要低估 AI 在前期策划和素材生成阶段的价值。 - -## 工作流整合:把文本、图片、视频串起来 - -提升效率靠的不是单点工具,而是流程(Workflow)。 - -一个多模态工作流的例子: - -1. 用文本 AI 生成创意大纲。 -2. 用文本 AI 把大纲扩展成分镜脚本。 -3. 用图像 AI 为每个镜头生成参考画面。 -4. 用视觉模型检查参考画面是否符合分镜意图。 -5. 用视频工具把画面串成动态故事板。 -6. 最后进行剪辑与排版。 - -每一步都可回退、可修正、可复用。每一步的输出都是下一步的输入。 - -> 提醒:多模态创作涉及版权与合规,尤其是商业场景,要明确素材来源与使用范围。 - -## 音频生成:AI 也能作曲和说话 - -除了图像和视频,AI 在音频领域也在快速发展。 - -**音乐生成**:像 Suno、Udio 这样的工具,输入一段文字描述就能生成完整的歌曲。你不需要懂乐理,只需要说“一首轻快的民谣,关于夏天和海边”。 - -**语音合成(TTS)**:文字转语音已经非常成熟。最新的模型不仅能生成自然的语音,还能控制语气、语速、甚至情感。克隆特定人的声音也已经成为现实,但只能在获得本人明确授权的情况下使用,不能用来冒充真人、误导听众,或绕过平台规则。 - -**语音识别(ASR)**:反过来,把语音转成文字也很成熟了。Whisper 等模型能处理多种语言和口音,是会议纪要、字幕生成等场景的基础。 - -音频生成的门槛比图像和视频更低:大部分工具直接输入文字就行,不需要“镜头语言”或“构图”这样的专业知识。但它的可控性也相对较低:你很难精确控制每一个音符或每一个字的发音。 - -## 小结 - -- 多模态创作的重点是精确表达,不是“会用工具”。 -- 图像生成工具在演进:从复杂参数到结构化控制到一句话生成,但表达的精度要求没变。 -- 图像生成的要点:明确主体、给参考材料、加约束条件、分步迭代。 -- 视觉理解让 AI 可以读取截图、图表、设计稿和资料图片,但关键结论仍然需要人复核。 -- 视频生成需要镜头语言思维,AI 擅长策划和素材生成,最终把控仍需人工。 -- 音频生成(音乐、语音合成、语音识别)门槛更低,但可控性也相对较低。 -- 工作流让多模态创作从单点操作变成可迭代的系统。 - -## 练习 - -1. 找一个图像生成工具,分别用“一句话”和“详细描述”生成同一主题的图,对比结果差异,思考为什么。 -2. 选一张你喜欢的图片,尝试用文字精确描述它的内容:包括主体、风格、构图、色调。然后用这个描述让 AI 生成一张类似的图。 -3. 找一张复杂截图或图表,让 AI 先描述它看到了什么,再让它总结三个关键结论,检查哪些地方需要人工修正。 -4. 把一个 30 秒短视频的想法拆成 3-5 个镜头,写出每个镜头的景别、运动和内容。 - -## 延伸阅读 - -- [安全与安全性 Safety & Security](../reference/glossary/safety-and-security.md) -- [编码与字符集 Encoding & Charset](../reference/glossary/encoding-and-charset.md) -- [序列化 Serialization](../reference/glossary/serialization.md) diff --git a/docs/agentic-ai/reasoning-logic.md b/docs/agentic-ai/reasoning-logic.md deleted file mode 100644 index 602727f..0000000 --- a/docs/agentic-ai/reasoning-logic.md +++ /dev/null @@ -1,218 +0,0 @@ -# 高级 Prompting 与推理增强(Advanced Prompting & Reasoning) - -基础 Prompting 解决的是“把需求说清楚”。高级 Prompting 解决的是另一个问题:当任务需要多步推理、外部信息、工具调用或反复校验时,怎么让 AI 更稳。 - -你可能已经遇到过这种情况:同一个问题,AI 有时回答得很清楚,有时又像“绕口令”。这不一定是模型突然变笨,而是任务本身需要拆解、检索、验证,甚至需要模型边行动边观察结果。 - -这一章,我们把几种常见方法放在同一个框架下看:Chain-of-thought、ReAct、RAG,以及模型原生推理(Model-native Reasoning)。它们不是互相替代的流行词,而是解决不同问题的工具。 - -## 从答案提示到过程提示 - -普通 Prompt 往往直接要结果: - -```text -帮我分析这个方案是否可行。 -``` - -高级 Prompting 更关心过程控制: - -```text -请先列出这个方案成立所需的关键假设, -再从成本、风险、实施难度三个角度分析, -最后给出结论和需要人工确认的事项。 -``` - -差别在哪里? - -- 不只是问“答案是什么”,而是要求 AI 说明判断维度。 -- 不只是让它生成内容,而是让它暴露关键假设。 -- 不只是相信一次回答,而是把验证步骤写进任务里。 - -这就是高级 Prompting 的核心:让 AI 的思考过程更可控,让你更容易发现它在哪里可能错。 - -## Chain-of-thought:把复杂问题拆开 - -Chain-of-thought(思维链,常写作 CoT)最初的意思,是让模型通过中间步骤解决复杂问题。它适合处理多步骤推理,比如数学题、逻辑题、方案比较、复杂决策。 - -但在实际使用中,不建议把它理解成固定模板:“一步一步思考”。更稳妥的做法,是让 AI 输出你真正需要检查的结构。早期模型推理能力弱,需要显式触发;现在模型能力强了,更重要的是控制输出结构。 - -比如: - -```text -请按下面结构回答: -1. 问题拆解 -2. 关键假设 -3. 推理步骤摘要 -4. 最终结论 -5. 可能出错的地方 -``` - -注意这里说的是“推理步骤摘要”,不是要求模型把所有内部思考逐字写出来。对用户来说,真正有价值的是可检查的中间结论:假设是什么、依据是什么、风险在哪里。 - -CoT 适合解决“信息已经在上下文里,但需要推理”的问题。它不适合解决“信息根本不存在”的问题。如果你问一个模型不知道的事实,它就算写出很长的推理过程,也可能只是把错误说得更像真的。 - -## ReAct:边推理,边行动,边观察 - -ReAct 是 Reason + Act 的组合。它的核心不是让 AI 一直想,而是让 AI 在推理和行动之间循环: - -1. 理解当前目标。 -2. 决定下一步行动。 -3. 执行动作,得到观察结果。 -4. 根据观察结果调整计划。 - -在纯对话里,你可以把 ReAct 当成一种任务管理方式: - -```text -请按循环方式推进这个任务: -先说明下一步要验证什么, -再告诉我需要提供什么信息, -等我补充后再继续下一步。 -``` - -在 Agent 系统里,ReAct 会更具体:模型可以调用搜索、读文件、执行命令、访问 API。每一次工具调用都会产生观察结果,模型再根据结果决定下一步。 - -ReAct 适合不确定性比较高的任务,比如调试问题、查资料、分析代码库、排查线上故障。它的优势是不会把一开始的计划当成真理,而是随着新信息不断修正。 - -但 ReAct 也有成本:步骤更多,延迟更高,也更容易在错误观察上继续走偏。所以它需要边界: - -- 每一步只做一个清晰动作。 -- 重要结论要回到证据本身。 -- 高风险动作必须由人确认。 - -## RAG:先找信息,再让模型回答 - -Chain-of-thought 和 ReAct 解决的是“怎么推理”和“怎么行动”。RAG 解决的是另一个问题:模型没有看到正确资料。 - -RAG(Retrieval-Augmented Generation,检索增强生成)的基本思路是:先从知识库里找出相关内容,再把这些内容放进 Prompt,让模型基于资料回答。 - -基本流程是: - -1. **离线阶段**:把文档切成小块(Chunking),每一块转换成向量(Embedding),存入向量数据库。 -2. **在线阶段**:用户提问时,把问题也转换成向量,在数据库里找到最相关的文档块。 -3. **生成阶段**:把检索到的文档块拼进上下文,让模型基于这些材料回答。 - -为什么需要向量?因为向量能捕捉语义。如果你不熟悉向量,可以暂时把它理解成“能把语义相似的内容找出来的搜索工具”。“怎么退货”和“退款流程是什么”在字面上差别很大,但语义接近,向量检索更容易把它们联系起来。 - -RAG 很适合企业知识库、产品文档、客服问答、内部制度检索。它的价值不是让模型“学会”这些资料,而是在回答时把资料临时放到模型面前。 - -RAG 的局限也很明确: - -- **检索质量决定回答质量**:找错资料,回答就会跑偏。 -- **文档质量决定上限**:过时文档、错误文档会被模型原样使用。 -- **上下文组织很重要**:塞太多材料会引入噪声,塞太少又可能缺关键证据。 - -> 提醒:RAG 不是万能药。它解决的是“让 AI 看到正确的信息”,但不能保证 AI “正确地使用这些信息”。输入质量决定输出质量。 - -> 延伸阅读:如果你想了解 RAG 的技术细节(向量嵌入、HNSW 索引、混合搜索策略等),可以看[RAG 技术详解](../reference/rag-deep-dive.md)。 - -## 模型原生推理:不要手工模拟一切 - -随着模型能力提升,很多推理能力开始变成模型原生能力。你不一定需要写很复杂的提示词来模拟“思考过程”,模型本身就能在内部做更长的推理、计划和自我检查。 - -这就是模型原生推理(Model-native Reasoning):把复杂推理交给底层模型能力,而不是完全依赖外部 Prompt 模板。 - -这和前面讲的升级对齐定律是一致的。底层模型升级后,一些过去必须手写的技巧会变成细节。比如早期你可能要反复写“请一步一步思考”,现在更重要的是: - -- 给清楚目标。 -- 给必要上下文。 -- 给明确约束。 -- 说明输出格式。 -- 要求列出关键假设、证据和风险。 - -也就是说,你不需要控制模型的每一个“内心步骤”,而是要控制它交付给你的结果结构。 - -一个更稳的提示是: - -```text -请先在内部完成必要推理。 -最终只输出: -1. 结论 -2. 关键依据 -3. 不确定点 -4. 建议的下一步验证 -``` - -这类写法适合强推理模型。它让模型保留内部推理空间,同时把用户真正需要检查的信息输出出来。 - -## 准确性控制:上下文、幻觉与验证 - -AI 有时会“编”。这叫幻觉(Hallucination)。它不是故意骗你,而是生成机制决定了它会补全缺失信息,即使那个信息并不存在。 - -避免幻觉的关键在于约束和验证。 - -### 约束:缩小生成空间 - -- **给明确的事实依据**:比如“根据以下文档回答”,而不是“你知道的都说”。 -- **限制范围**:比如“只讨论 2024 年以后的情况”。 -- **要求标注不确定性**:比如“如果不确定,请明确说‘我不确定’”。 - -### 验证:交叉检查 - -- **要求反例**:让 AI 自己找可能的反驳。 -- **要求依据**:让 AI 给出信息来源或上下文片段。 -- **多角度提问**:用不同方式问同一个问题,看答案是否一致。 - -例如: - -```text -请给出结论,并列出 2 条可能的反例或失败条件。 -``` - -> 提醒:如果你需要高可靠性结果,请让 AI 输出“假设清单”,然后你逐条确认。AI 的推理可以参考,但最终判断权在你。 - -## 确认偏误:AI 倾向于“同意”你 - -这里有一个很多人没意识到的陷阱:AI 倾向于同意用户。 - -你问“这个方案可行吗?”,AI 大概率会说“可行”,然后找理由支持你。你问“这个代码有 bug 吗?”,AI 可能会说“看起来没问题”,即使有问题。 - -这不是 AI 在“讨好”你,而是它的训练方式决定了它倾向于生成用户期望看到的内容。这就是确认偏误(Confirmation Bias):在 AI 语境下,它会倾向于支持你的预设。 - -怎么办? - -- **主动要求反驳**:“请反驳这个方案,列出 3 个可能的问题。” -- **换角度提问**:“如果你是这个方案的反对者,你会怎么说?” -- **给对立立场**:“假设这个方案是错的,最可能的原因是什么?” - -```text -我有一个方案:[方案内容]。 -请先不要评价它的好坏, -而是从反对者的角度列出 3 个最可能的问题。 -``` - -## Human-in-the-loop:高风险节点必须人工校验 - -不管 AI 的推理能力多强,有一个原则始终不变:高风险决策必须有人参与。 - -什么是“高风险节点”? - -- **不可逆操作**:删除数据、发布上线、发送邮件。 -- **高影响决策**:架构选型、技术方案、预算分配。 -- **涉及价值判断**:内容审核、风险评估、合规检查。 - -在这些节点上,不要让 AI 自己做决定。让它给出建议和依据,你来做最终判断。 - -这不是不信任 AI,而是一种合理分工:AI 擅长信息处理和方案生成,快速给出多个选项;人擅长价值判断和最终决策,在选项中做出选择。 - -## 小结 - -- 高级 Prompting 的重点不是更长的提示词,而是更可控的过程。 -- Chain-of-thought 适合多步骤推理,但要关注可检查的假设、依据和结论。 -- ReAct 适合需要边行动边观察的任务,尤其是调试、检索和工具调用场景。 -- RAG 适合把外部知识放进上下文,但检索质量和文档质量决定效果上限。 -- 模型原生推理会让一部分旧技巧变成细节,你需要控制输出结构,而不是控制每一步内心活动。 -- 幻觉、确认偏误和高风险节点都需要验证机制,不能只靠模型自信程度判断结果。 - -## 练习 - -1. 选一个多步骤问题,写一个“假设 → 依据 → 结论 → 风险”的提示,对比它和直接提问的区别。 -2. 给 AI 一个你认为正确的观点,然后要求它从反对者角度反驳。记录它提出的问题是否有价值。 -3. 找一篇长文章,先让 AI 总结成 5 条要点,再只基于其中一条继续追问,观察上下文缩短后回答是否更稳定。 -4. 设计一个简单的 ReAct 流程:让 AI 每次只提出下一步要验证的问题,等你提供观察结果后再继续。 - -## 延伸阅读 - -- [RAG 技术详解:向量检索与混合搜索](../reference/rag-deep-dive.md) -- [调试 Debug](../reference/glossary/debug.md) -- [测试 Testing](../reference/glossary/testing.md) -- [复杂度 Complexity](../reference/glossary/complexity.md) diff --git a/docs/chapters/answers.md b/docs/chapters/answers.md deleted file mode 100644 index b35a7d3..0000000 --- a/docs/chapters/answers.md +++ /dev/null @@ -1,307 +0,0 @@ -# 练习答案与提示 - -> 说明:以下为参考答案/提示,不是唯一解。为了保持可读性,示例代码尽量短,省略了异常处理。 - -## 第二章 计算 - -**练习 1:懒求值下的乘法次数** - -`pow5(x)` 展开是 `x*x*x*x*x`,需要 4 次乘法。如果参数不先求值,每一次出现都会重新计算。 - -设 `M(n)` 表示 `pow5` 嵌套 n 次的乘法次数: - -* `M(1) = 4` -* `M(n) = 4 + 5 * M(n-1)` - -因此:`M(2)=24`,`M(3)=124`,`M(4)=624`。 - -**练习 2:强制求值的问题** - -* 可能做很多无用计算(性能浪费)。 -* 不能处理“无穷序列/无穷结构”。 -* 即使分支不会被用到,也提前执行(丢失短路优势)。 -* 如果参数包含副作用,提前执行会改变行为。 - -## 第三章 过程 - -**阶乘(循环/递归/尾递归)** - -```javascript -function fact(n) { - let r = 1; - for (let i = 2; i <= n; i++) r *= i; - return r; -} - -function factRec(n) { - return n <= 1 ? 1 : n * factRec(n - 1); -} - -function factTail(n, acc = 1) { - return n <= 1 ? acc : factTail(n - 1, acc * n); -} -``` - -**斐波那契(循环/递归/尾递归)** - -```javascript -function fib(n) { - let a = 0, b = 1; - for (let i = 0; i < n; i++) { - [a, b] = [b, a + b]; - } - return a; -} - -function fibRec(n) { - return n <= 1 ? n : fibRec(n - 1) + fibRec(n - 2); -} - -function fibTail(n, a = 0, b = 1) { - return n === 0 ? a : fibTail(n - 1, b, a + b); -} -``` - -**短路求值说明** - -`condition ? expr1 : expr2` 只会计算其中一个分支。 -所以当 `y == 1` 时,`pow(x, y-1) * x` 不会被执行。 - -**顺序/分支/循环的现实过程** - -示例: - -* 顺序:起床 → 洗漱 → 早餐。 -* 分支:是否下雨?下雨带伞,否则不带。 -* 循环:刷牙 2 分钟(重复动作)。 - -## 第四章 编码 - -**小数的表示** - -`1/3` 在十进制是无限循环小数,到了二进制依然是无限循环。IEEE 754 用“符号位 + 指数 + 尾数”近似表示,所以会出现精度误差。 - -可以用 `0.1 + 0.2` 观察误差: - -```javascript -0.1 + 0.2; // 0.30000000000000004 -``` - -## 第五章 序列 - -**LCS 提示** - -经典解法是动态规划: - -``` -if a[i-1] == b[j-1] => dp[i][j] = dp[i-1][j-1] + 1 -else => dp[i][j] = max(dp[i-1][j], dp[i][j-1]) -``` - -这要求你能“多次访问任意位置”,所以在 C++ 中至少需要 Random Access 迭代器。 - -## 第六章 数据 - -**sumList** - -```javascript -function sumList(list) { - let sum = 0; - for (let p = list; p !== null; p = p.next) sum += p.value; - return sum; -} -``` - -**filterList** - -```javascript -function filterList(list, pred) { - if (list === null) return null; - if (pred(list.value)) { - return { value: list.value, next: filterList(list.next, pred) }; - } - return filterList(list.next, pred); -} -``` - -**树遍历** - -参考正文中的 `preorder/inorder/postorder`。 - -**DFS/BFS** - -参考正文中的 `dfs/bfs`,DFS 使用栈,BFS 使用队列。 - -## 第七章 状态 - -**计数器** - -```javascript -let counter = { - value: 0, - inc() { this.value += 1; }, - dec() { this.value -= 1; } -}; -``` - -**电梯状态机** - -状态示例:`idle / up / down / open`,转移规则取决于请求队列。 - -**move** - -```javascript -function move(point, dx, dy) { - return { x: point.x + dx, y: point.y + dy }; -} -``` - -**共享状态与库存示例(提示)** - -把“读库存 + 减库存”拆成两步,再用 `await` 人为制造切换点,就能看到负库存或丢失更新的情况: - -```javascript -let stock = 1; -async function buy() { - let current = stock; - await Promise.resolve(); - stock = current - 1; -} -``` - -## 第八章 引用 - -**数组输出** - -`a` 和 `b` 指向同一数组,`c` 是浅拷贝: - -```javascript -// 输出: -// a: [1, 2, 3] -// b: [1, 2, 3] -// c: [1, 2] -``` - -**cloneUser(浅拷贝)** - -```javascript -const cloneUser = user => ({ ...user }); -``` - -**freeze 示例** - -```javascript -let obj = Object.freeze({ x: 1 }); -obj.x = 2; -obj.x; // 1 -``` - -**共享尾部链表(提示)** - -两个链表可以共用同一个尾节点: - -```javascript -let tail = node(3, null); -let listA = node(1, node(2, tail)); -let listB = node(9, tail); -``` - -优点:节省内存;风险:修改共享节点会影响两条链表。 - -## 第九章 闭包 - -**makeOnce** - -```javascript -function makeOnce(fn) { - let called = false; - return function (...args) { - if (called) return; - called = true; - return fn(...args); - }; -} -``` - -**makeTimer** - -```javascript -function makeTimer() { - const start = Date.now(); - return () => Date.now() - start; -} -``` - -**outer()() 为什么能输出** - -因为返回的函数闭包捕获了 `msg`,即使 `outer` 结束了,`msg` 仍然可访问。 - -## 第十章 对象 - -**Animal / Dog / Cat** - -```javascript -class Animal { - speak() { console.log('...'); } -} -class Dog extends Animal { - speak() { console.log('woof'); } -} -class Cat extends Animal { - speak() { console.log('meow'); } -} -``` - -**feed** - -```javascript -function feed(animal) { - animal.eat(); -} -``` - -**组合 move + draw** - -```javascript -let canMove = { move() { console.log('move'); } }; -let canDraw = { draw() { console.log('draw'); } }; -let sprite = Object.assign({}, canMove, canDraw); -``` - -## 第十一章 并发 - -**修复银行竞态(队列化)** - -```javascript -function createQueue() { - let last = Promise.resolve(); - return function enqueue(task) { - last = last.then(() => task()).catch(() => {}); - return last; - }; -} - -let enqueue = createQueue(); - -enqueue(() => deposit(200)); -enqueue(() => withdraw(200)); -``` - -**顺序打印 1/2/3** - -```javascript -function delay(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -(async function () { - for (let i = 1; i <= 3; i++) { - await delay(1000); - console.log(i); - } -})(); -``` - -**任务队列(提示)** - -把每个任务包装成返回 Promise 的函数,然后用一个“串行链”依次执行。 diff --git a/docs/chapters/ch01_environment.md b/docs/chapters/ch01_environment.md deleted file mode 100644 index f02b5ef..0000000 --- a/docs/chapters/ch01_environment.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -description: 简单介绍下JavaScript编程语言,以及对应的环境准备工作。 ---- - -# 环境 - -## JavaScript - -JavaScript是目前使用最广泛的编程语言之一。作为互联网和移动计算时代的基础支撑语言,从基本的生产力工具,到日常的泛娱乐平台,它活跃在每一次你浏览网站、使用App的过程中,提供基本的运算和流程保障。 - -JavaScript的诞生和发展也颇具传奇色彩,Brendan Eich在几天的时间内设计并实现了这门编程语言,影响了后面几十年的软件和技术的发展。如今几乎每一个设备都接入了JavaScript,深入到了我们生活的方方面面。作为一门工业级的编程语言,JavaScript由欧洲计算机制造商协会(ECMA)制定标准化的行为和定义,来规范这个语言的使用和发展方向。TC39技术委员会也一直在更新JavaScript的功能,让这个有二十多年历史的旧技术不断焕发新的活力。 - -JavaScript 的环境触手可及。在电脑端你可以随时打开一个现代的浏览器,页面上右键菜单中选择“Inspect”或者“查看/检查元素”,然后切换到“控制台(Console)”标签。如果你还没用过开发者工具,请先阅读[浏览器开发者工具与 Console](../guides/browser-devtools.md)。 - -## 编写代码 - -当需要写大量代码组合使用的时候,你可以通过本地编辑Web页面的方式来编写JavaScript代码。JavaScript是Web端的基本编程语言,可以稳定地运行在大多数现代浏览器中。 - -通过文件编辑器创建下面的代码,命名为`index.html` - -```html - - - - - Introduction to Programming - - - - - -``` - -在``之间,就是你编写JavaScript代码的地方。编写完成后,用浏览器打开(或者刷新)这个文件,就能看到对应的效果和执行内容。 - -比如上面的代码,就是在控制台打印“hello world”。 - -本书推荐使用 Visual Studio Code 作为基本的代码编辑工具,安装与上手请参考[安装与上手 VS Code](../guides/vscode.md)。 - -当你希望在本地运行 JavaScript 或 TypeScript 时,需要安装 Node.js 与 TypeScript,对应的上手步骤见: - -* [安装 Node.js 并在 VS Code 运行 JavaScript](../guides/nodejs.md) -* [TypeScript 入门:安装、编译与运行](../guides/typescript.md) - -## 延伸阅读 - -* [进程与线程 Process & Thread](../reference/glossary/process-and-thread.md) -* [阻塞与非阻塞 Blocking & Non-blocking](../reference/glossary/blocking-nonblocking.md) -* [同步与异步 Sync & Async](../reference/glossary/sync-async.md) -* [调用栈 Call Stack](../reference/glossary/call-stack.md) - -一切准备就绪的话,我们就开始吧。 diff --git a/docs/chapters/ch02_computation.md b/docs/chapters/ch02_computation.md deleted file mode 100644 index 4b19156..0000000 --- a/docs/chapters/ch02_computation.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -description: 计算机可以完成哪些基本运算?如何把运算进行组合?如何进行函数封装?如何实现递归? ---- - -# 计算 - -编程语言是人与计算机打交道的一个重要工具。 - -计算机(Computer)是干嘛的?当然是计算(Computing)啦。 - -在浏览器的控制台输入以下代码(如果还没打开过控制台,请先看[浏览器开发者工具与 Console](../guides/browser-devtools.md)): - -```javascript -1 + 1 -``` - -是不是有看到浏览器在回应你:`2`。这里基本上是能够当成一个计算器用的。 - -没错,这就是计算嘛。 - -然而,有什么用呢? - -## 计算 - -简(pian)单(mian)地讲,一切问题都能抽出来一套数学模型,同样能够抽出来的数学模型都可以转化成计算问题,然后让计算机来解决。 - -所以**计算**可以看成是整个程序设计的基础。同样像我们之前提到的,计算机,本身也就是拿来做计算的。 - -那么我们都可以做哪些计算呢? - -当然普通的$+ - \times(*) \div(/)$不用说,你还可以把一些复杂的计算规则定义成函数: - -```javascript -function pow5(num) { - return num * num * num * num * num; -} -``` - -比如`pow5`的意思就是求一个数的5次方。我们定义了一个函数,怎么用呢? - -然后尝试下面的代码: -```javascript -pow5(1) -``` - -嗯,没错,输出了我们期望的结果(`1`)。 - -## 组合 - -什么是组合呢? - -想想我们学习函数和多项式的时候, - -$$ -f(x) = x^2 + 2x + 1 -$$ - -$$ -g(x) = (x+3)(x-5) -$$ - -然后我们要定义一个$h(x) = f(g(x))$,那么定义出来的$h(x)$长什么样呢? - -我们首先要把$g(x)$代入$f(x)$, - -$$ -f(g(x)) = g(x)^2 + 2g(x) + 1 -$$ - -即 - -$$ -f(g(x)) = [(x+3)(x-5)]^2 + 2 * (x+3)(x-5) + 1 -$$ - -好吧我编不下去了。总之展开来以后很丑的。 - -而且如果以后遇到$\sigma(x,y,z)$跟$\theta(a,b,c)$组合的,或者是更多参数的函数组合,结果更是可想而知。 - -但是在JavaScript里面我们并不用担心这个问题。 - -比如我们有两个函数 - -```javascript -function f(x) { - return x*x + 2*x + 1; -} - -function g(x) { - return (x+3) * (x-5); -} -``` - -那么我们的$h(x)$可以很简单的写成这个样子: - -```javascript -function h(x) { - return f(g(x)); -} -``` - -或者你也可以不用专门去定义,在用到的时候直接用就可以: - -```javascript -f(g(5)) -// or -h(5) -``` - -### 组合的意义 - -比如我们可以考虑一个更复杂的函数, - -$$ -\sigma(x,y) = x^2 + x*y + (x+y)\times5 -$$ - -当 $x, y$ 分别为 $4x+1,5x-3$的时候。你可以试着自己去展开一下这个算式: - -$$ -\sigma(4x+1, 5x-3) = -(4x+1)^2 + (4x+1)(5x-3)+(4x+1+5x-3)\times 5 -$$ - -总之,无论他有多长,都会在算式里面充斥着$4x+1$和$5x-3$,而且,计算机并没有那么聪明,所以只好按照特定的顺序一次次的计算。 - -于是$4x+1$被重复计算了三次,$5x-3$被重复计算了两次。对于`pow5`那种情况,更是会被求值5次。 - -令人欣慰的是,组合帮我们保证了这个问题不会有这些重复。 - -当你计算`pow5(3+1)`的时候,JavaScript并不会去展开成`(3+1)*(3+1)...*(3+1)`这种形式,而是直接变成了`pow5(4)`,最后的结果是1024。 - -也就是说,函数的每个自变量(编程语言里面我们会把他们叫做参数(argument))会先被求值,得到他们的值以后才会带入计算。 - -\*请记住我们现在讨论的这个问题,后面会展开并且深入地探讨。 - -### 补充:函数的可组合性 - -函数的组合不仅仅是数学里的“代入”,也可以用于日常操作的拼装。比如我们想要“排序后再反转”的结果: - -```javascript -function sorted(array) { - return array.slice().sort(); -} - -function reversed(array) { - return array.slice().reverse(); -} - -function sortReversed(array) { - return reversed(sorted(array)); -} -``` - -组合的意义在于:**用小而清晰的步骤,拼出更复杂的行为**。如果你想对数字排序,需要提供比较器(后面“作为数据的过程”会讲到)。 - -## 练习 - -* 请思考,如果函数的参数在代入函数之前并不进行强制求值,`pow5(pow5(pow5(pow5(5))))`展开后共计算多少次乘法。 -* 强制求值可能会造成的问题有哪些。 - -## 延伸阅读 - -* [复杂度 Complexity](../reference/glossary/complexity.md) -* [浮点数 Floating Point](../reference/glossary/floating-point.md) diff --git a/docs/chapters/ch03_procedure.md b/docs/chapters/ch03_procedure.md deleted file mode 100644 index f7fdc18..0000000 --- a/docs/chapters/ch03_procedure.md +++ /dev/null @@ -1,296 +0,0 @@ ---- -description: 程序的基本控制结构有哪几种?递归和循环的区别是什么?短路求值的作用是什么? ---- - -# 过程 - -什么是过程呢? - -凭经验我们就能知道,实际的问题并不是像理想中那么简单,不是一个表达式就能描述的清楚的,而且也并不一定能通过组合来直接解决。 - -甚至是简单的数学问题,也不能用他们来解决。比如: - -$$ -|x| - = abs(x) = \begin{cases} -x, &x\ge0 \\ --x, &x\lt0 -\end{cases} -$$ - -求绝对值,或者 - -$$ -pow(x,y) = \prod_{1}^{y}{x} -$$ - -求积。 - -其中一个需要我们根据参数的值,来选择不同的计算方式,返回不同的结果;另外一个则是通过对特定的表达式做一个累乘(没错其实行为跟乘方是一样的,所以我把它叫 pow)。 - -我们先看累乘。 - -## 累乘 - -如果我们已知指数的话,要写起来是很简单的: - -```javascript -function pow1(x) { return x; } -function pow2(x) { return pow1(x) * x; } -function pow3(x) { return pow2(x) * x; } -function pow4(x) { return pow3(x) * x; } -function pow5(x) { return pow4(x) * x; } -``` - -这样写其实是符合pow的定义的。 - -然后其实我们可以把它变换一下: - -```javascript -// 伪代码,无法正确运行 -function pow(x, 1) { return x; } -function pow(x, 2) { return pow(x, 1) * x; } -function pow(x, 3) { return pow(x, 2) * x; } -function pow(x, 4) { return pow(x, 3) * x; } -function pow(x, 5) { return pow(x, 4) * x; } -``` - -所以我们得到了pow函数的另一个形式: - -$$ -pow(x, y) = \begin{cases} -x, &y=1 \\ -pow(x, y -1)\times x, &y>1 -\end{cases} -$$ - -没错我们又归结到了选择结构上。后面我们会拿这个来讨论。现在只是简单提一提。 - -哦对了,现在pow的这个形式叫做递归定义,数学上叫递推关系式(好像又叫归纳定义?)。 - -好的,我们回到$\prod$运算符上来。 - -根据前面的分析,我们可以知道,如果能够拿到前面一个乘积,然后和第y个相乘,,就可以得到我们要的结果。 - -于是我们假设那第`y-1`个乘积是`prod`,最终的结果就是`prod*x`。再往前推`prod`,这个关系是基本上不变的。 - -那么,我们只要有一个东西来保存前面的结果,再跟后面的相乘之后更新保存的结果就可以了。 - -这个能保存数据的东西我们把它叫做变量(Variable)。 - -比如最开始`prod = 1`,然后`prod = prod * x`,会把`prod`与`x`相乘的结果计算出来,再保存进`prod`里面。 - -所以我们可以简单地把这个计算看成 - -```javascript -let prod=1 -prod=prod*x; -prod=prod*x; -prod=prod*x; -... -prod=prod*x; // 执行y次 -``` - -那么怎么让他能够只执行`y`次呢? - -## 循环 - -JavaScript提供了一种东西叫做循环语句。 - -大概是长这个样子的 - -```javascript -let prod=1; -for(let i = 1; i <= y; i = i+1) { - prod = prod*x; -} -``` - -这个叫循环语句,意思就是,在满足指定条件的情况下,会一直去执行语句体(花括号之间)的内容。 - -上面那段代码的意思是,让`prod`初值为1,让`i`从1开始,每次增加1,如果`i`小于等于`y`的话,执行`prod = prod*x`,否则跳出循环。 - -确实很复杂,特别是比起上面那个递归定义来。 - -我们对它进行一个简单的改写。 - -一般来说,程序员是从 $0$ 开始计数的。于是从 $1$ 到 $y$,就变成了从 $0$ 到 $y-1$,进一步地`i <= y-1`又可以进一步地写成`i < y`(反正都是整数,证明我就不给了),这样条件就简化掉了一个符号。 - -另外,但凡类似`x=x+y`这种的,都可以写成`x+=y`,特别地,当运算符为`+`和`-`,同时`y`为1的时候,可以直接写成`x++`或者`x--`; - -于是一个典型的循环就出来了: - -```javascript -let prod=1; -for(let i = 0; i < y; i++) { - prod *= x; -} -``` - -那么我们的pow函数也有了: - -```javascript -function pow(x, y) { - let prod=1; - for(let i = 0; i < y; i++) { - prod *= x; - } - return prod; -} -``` - -## 选择 - -好的,回头来看我们的abs函数。 - -我们要根据x的符号来决定时返回x还是他的相反数。 - -JavaScript让我们能通过选择语句`if...else...`做这件事。 - -```javascript -function abs(x) { - if(x >= 0) { - return x; - } else { - return -x; - } -} -``` - -当然`if/else`还有一种简单的形式,可以让我们写起来更省力。 - -```javascript -function abs(x) { - return (x >= 0) ? x : -x; -} -``` - -`?:`运算符共有三部分,问好前面是判断条件,问号后面由冒号分成两部分,如果判断条件为真,那么返回冒号与问号之间的,否则返回冒号之后的。 - -## 递归 - -好的,这样子我们就能完善我们前面写的递归定义的pow了。 - -```javascript -function pow(x, y) { - return (y == 1) ? x : pow(x, y-1) * x; -} -``` - -不能比循环的明白更多。 - -### 尾递归 - -尾递归这个概念可以简单做个了解。主要是因为如果有了尾递归,编译器就可以更好的优化程序的执行过程。而且更重要的一点是,尾递归跟循环几乎是一一对应的关系。 - -我们先简单的把这个实现写出来吧。 - -```javascript -function pow(x, y, prod = 1) { - return (y == 0) ? prod : pow(x, y-1, prod*x); -} -``` - -怎么转换到循环的呢? - -首先我们提取出变量来。 - -```javascript -function pow(x, y, prod = 1) { - if (y == 0) { - return prod; - } - y = y - 1; - prod*=x; - return pow(x, y, prod); -} -``` - -正式写成循环 - -```javascript -function pow(x, y, prod = 1) { - for(;y != 0;) { // or while(y != 0) - y = y - 1; - prod*=x; - } - return prod; -} -``` - -改写成递增的变量 - -```javascript -function pow(x, y) { - let prod = 1; - for(let i = 0; i < y; i++) { - prod *= x; - } - return prod; -} -``` - -### 递归树与效率(补充) - -递归在表达结构上很自然,但有时会重复计算。 - -以斐波那契为例(常用定义:`fib(0)=0, fib(1)=1`): - -```javascript -function fib(n) { - if (n <= 1) return n; - return fib(n - 1) + fib(n - 2); -} -``` - -当 `n` 增大时,`fib(n-2)` 等子问题会被重复计算很多次,调用次数增长很快。把它改成尾递归或循环,可以避免这种重复。 - -> 提醒:JavaScript 规范并不保证“尾调用优化”,很多运行时也没有开启,所以尾递归更多是理解思路,实际工程里常常直接用循环。 - -## 短路求值 - -试想一下我们的递归版pow代码 - -```javascript -function pow(x, y) { - return (y == 1) ? x : pow(x, y-1) * x; -} -``` - -如果这里你把函数自身的调用写成了`pow(x, y) * x`,结果会是怎么样的呢? - -如果这样写,y作为一个计数器并没有递减,那么每回调用都会跟当前调用一样传递相同的参数进去。 - -于是会一在重复递归地调用`pow(x, y)`,直到不能再调用导致栈溢出。 - -然而一定是这样吗?你可以调用一下`pow(4, 1)`试试,会正常输出`4`。 - -为什么会有这种不同呢? - -上一讲,我们提到了组合的概念,说,通过组合会强制求值每一个传递给函数的参数,但是当我们把这个东西忘`?:`操作符上套的时候,并没有效果。 - -这种特性叫做短路求值(short-circuit evaluation),对于`?:`来说,只有当条件为真的时候才去求值真分支,为假的时候才去求值假分支。 - -所以如果写错的那个pow,当`y == 1`的时候,还是能够正常执行的。 - -## 过程 - -一个再复杂的流程,最终都会由这样的三部分组成: - -* 顺序 -* 分支(选择) -* 循环 - -根据我们前面提到的组合的思想,再复杂的过程都能通过这些来组合解决。 - -## 练习 - -* 试写出求阶乘、求斐波那契数等函数的循环、递归和尾递归版本的实现。 -* 用以上代码解释短路求值 -* 请使用顺序、分支、循环来描述现实中的一些过程。 - -## 延伸阅读 - -* [递归 Recursion](../reference/glossary/recursion.md) -* [调用栈 Call Stack](../reference/glossary/call-stack.md) -* [尾调用优化 Tail Call Optimization](../reference/glossary/tail-call-optimization.md) diff --git a/docs/chapters/ch04_encoding.md b/docs/chapters/ch04_encoding.md deleted file mode 100644 index 62500b1..0000000 --- a/docs/chapters/ch04_encoding.md +++ /dev/null @@ -1,179 +0,0 @@ -# 编码 - -这里的编码(Encoding)当然不是指简单的编写代码。 - -为什么会有这个概念呢。 - -想想古老的印度人是怎么表示数字的吧:用十个不同的符号和位序组合来表示一个确切的数字,演化成今天我们用阿拉伯数字(Arabic Numerals)表示的十进制系统。 - -没错就是你身边到处都存在的**数字**。这套系统只需要简单的十个符号和并不怎么复杂的规则就能从0表示到任意大的确切数字。 - -我们在第二讲中提到,代码中的表示的“整数”,跟数学意义上的整数,是有所差距的。这个差距其宗之一就在于表示数字的能力上。 - -比如,我们来计算$2^{70}$: - -```javascript -pow(2, 70); -``` - -嗯,结果是什么鬼(`1.1805916207174113e+21`,特别是后面这段)! - -也就是说,我们能直观表示的整数也就21位(十万亿亿级),再之后就只能用科学计数法表示了。 - -实际你会发现,其实还没到这个数量级的时候,数字就已经无法精确表示了。 - -为什么会这样呢? - -## 二进制 - -跟阿拉伯数字系统一样,计算机内部也是利用位序来表示数值,但由于数字电路只有通和断两种状态,所以,相当于每位上只能有两种状态,作为传统,我们将用0和1来表示这两种状态。 - -然后我们就会看到计算机努力的表示数字: $0,1,10,11,100,101,110,111,1000,1001,1010$ 等你都觉得脑袋大了,计算机才数到10,不过其实关于这个问题并不需要你操心太多,反正跟阿拉伯数字一样,只要足够长,任意大的数都可以表示。 - -可是我们之前使用的过程告诉我们,其实并不是这样的。为什么呢? - -### 定长 - -既然计算机表示数字就是靠0和1,那么如果我要表示多个数字,怎么来区分呢? - -比如,你知道下面究竟是1,2,3,4还是13, 14? - -```javascript -11011100 -``` - -于是其实我们可以思考一下,出了数值,还有什么可以利用的。 - -长度。(以及其实你也可以用霍夫曼编码。) - -当我规定用于表示数据的长度必须是一定(比如4位)的时候,表示数据就特别地清晰了: - -```javascript -0001 0010 0011 0100 -``` - -就可以表示1,2,3,4这四个数。而上面那个我们理解不了的序列,也可以解释成13,14这两个数。 - -当你去规定一种解释二进制序列的方法的时候,其实你就是在定义一种**编码**。 - -二进制序列中,每个0或者1,叫一个bit(位),特定长度(一般是8)的基本解释单位,叫做byte(字节),这个特定长度,据说是依据当前系统中表示基本的字符编码的长度来确定。 - -换句话说,编码不仅仅是能用来表示数字的。 - -## ASCII - -现在提到字节大家都会反应是8位,就是因为这个东西。 - -全名叫American Standard Code for Information Interchange(美国标准信息交换码)。 - -这套代码从上世纪60年代一直用到现在,足以说明有多么经典。然而我们先不说它是什么,简单地从我们自己的角度出发先设计一套东西。 - -### 字符集 - -要知道美国人民的生活很是单调的,每天就面临大小写加起来四十多个字母,十个阿拉伯数字,以及十多个常用的标点符号。对比起来,深深地为自己的母语感到自豪。 - -当然,美国人民日常使用的字符量少,在信息化的时代,其实是有优势的,比如,把他们常用的字符加起来也就100个左右,而$2^{6} \lt 100 \lt 2^7$,所以我们只要有长度为7位的二进制串长度就能把美国人民用的那些字符给完全表示出来。 - -比如我们可以定义,0000000 ~ 0001001 表示数字字符0-9,0100001 ~ 0111010 表示大写字母字符的A-Z,1000001 ~ 1011010 表示小写字母字符的a-z,于是我们就能把对应的字母变成相应的编码了。 - -但是老是这样看着满屏的0和1,总是会感觉眼花。既然都是表示数据,并不是说相同的东西,计算机看到长什么样子,我们就要看一样的。只要是能够表示相同的数据,完全可以换一种我们更习惯的方式来表示。 - -### 十六进制 - -比如我们可以使用十六进制(Hexadecimal)代替二进制(Binary),也就是说,同一个位上面共有16个状态,习惯上分别用0-9加上a-f来表示,同时,为了与十进制表示进行区分,一般会加上0x前缀或者h后缀。对于刚刚的字符编码来说,就可以用0x00 ~ 0x09 表示 数字字符,0x21 ~ 0x3A 表示大写字母A-Z,0x40 ~ 0x5A表示小写字母a-z。 - -#### 为什么是十六进制而不是其他的十二进制或者十八、二十四进制呢? - -我们可以简单的做一个对比: - -``` -Bin 0001 0010 0011 .... 1010 1011 1100 1101 1110 1111 -Hex 1 2 3 A B C D E F -Dec 1 2 3 10 11 12 13 14 15 -``` - -十六进制的一位刚好一一对应二进制的每四个位。 - -于是本来用二进制表示起来很冗长的东西,用十六进制可以比较轻松的表述出来。而且相互之间的转换也很容易。 - -相反生活中常用的十进制跟二进制对应的就不那么协调,所以一般也比较少用,只要知道对应的位上的值就好。 - -也就是说,只要能够数 - -$$ -1,2,4,8,16,...,1024,2048,...,32768,65536, -131072,...,2147483648 -$$ - -嗯,我觉得如果你玩过2048的话一定不会对前面的那些数字感到陌生。 - -### ASCII - -嗯,ASCII跟我们想的一样,选择了8位作为实际的长度,同时加入了一些必须要用到的特殊字符(比如制表符tab 0x09,回车记号carriage return 0x0D,换行符line feed 0x0A..),以及一些控制字符(比如空字符NUL 0x00,文件结束符EOF等)。于是整体的设计相对于我们的想法有一些偏移: - -* `\x30` ~ `\x39` 表示0-9 -* `\x41` ~ `\x5a` 表示A-Z -* `\x61` ~ `\x7a` 表示a-z - -好的我们来试试吧: - -```javascript -'\x41' -``` - - -我们可以直接用引号`'`括起来单个字符,在程序中直接拿来使用。同时也可以反过来看: - -```javascript -let a_char = 'a'; -a_char.charCodeAt(0).toString(16) -``` - - -### CJK字符集 - -CJK(有时候也会带上V),是China,Japan,Korea(和Vietnam)的统称,在这些地区,因为同遗传自古汉语,字符的数量可是非常之多,8位的ASCII根本就没办法保存这么多东西。 - -于是随着各种语言诞生了各种编码,比如`GB2312` 简体中文,`Big-5` 繁体中文,`Shift-JIS` 日文等。这些编码并不是相互兼容的,于是一般对于特定的内容,需要专门指出其编码。同样地,当你尝试去一种编码来解释另外一种编码的文本时,就会出现**乱码**的情况。注意这个时候并不是你电脑中毒了。 - -### Unicode - -所以就有人想,不如把所有的编码都统一了吧,于是就出现了Unicode。 - -现行版本的Unicode加入了世界上能够找到的绝大多数语言的符号(嗯,包括emoji),然后将这些符号分类整理,统一编码。于是,在理想情况下,大家都使用Unicode字符集的话,就不会出现乱码的错误了。 - -Unicode包含的字符量足够的多,于是就要设计比ASCII更充分大的表示方式才能够容纳所有的符号。最初选择的是每个字符使用16位的二进制串来表示,这样的话一个中文字符虽然会超出8位范围之外,还是能够表示得出来,这种方法被称为UTF-16。 - -然而不久就发现,符号的数量(就算仅仅是中文字符)远远多于65536个,于是又进一步地扩充到了32位,可以表示到上亿个字符了,目前为止还要很久才会资源枯竭,这种方法类似地,被称为UTF-32。 - -然而UTF-32有个很致命的缺点。 - -对于西方人来说,并没有CJKV地区的人的需求,1个字节就能足够表示他们的符号,而UTF-32硬生生地为了统一,要求字母字符也要用4个字节来表示,这样就让空间整整大了4倍,而且另外3倍的空间完全是浪费掉的,没有任何作用。 - -但是,为了兼容,没办法。 - -### UTF-8 - -于是为了解决这个浪费空间的问题,UTF-8出现了。这是来自Ken Thompson的设计,他给整个IT界贡献了不少东西,Unix、C语言、UTF-8。 - -简单来说就是,在保持与ASCII兼容的情况下,通过添加一些标记来支持更长的编码。 - -具体的做法当然维基百科介绍的比我更清楚,这里提起来只是要说这么一个思路:变长编码(Variable-Width Encoding)。因为比较重要的就是在设计的过程中考虑变量和不变量,以及设置各种标志和不同的数据区块解释方式,这样就可以把简单的二进制序列变成我们想要的任意数据。 - -当然,这里的思想我们会在第6章详细地展开。 - -## 数系的扩充 - -你有没有尝试计算JavaScript里面`1/3`的结果? - -当我们需要小数的时候又该如何表示呢? - -这个问题留作练习。 - -作为参考答案,你可以搜索IEEE754。 - -## 延伸阅读 - -* [编码与字符集 Encoding & Charset](../reference/glossary/encoding-and-charset.md) -* [Unicode / UTF-8](../reference/glossary/unicode-utf8.md) -* [浮点数 Floating Point](../reference/glossary/floating-point.md) diff --git a/docs/chapters/ch05_sequence.md b/docs/chapters/ch05_sequence.md deleted file mode 100644 index 33b1d38..0000000 --- a/docs/chapters/ch05_sequence.md +++ /dev/null @@ -1,251 +0,0 @@ -# 序列 - -你应该记得,我们在编码那一张提过如何表示多个数字的,没错,定长编码可以帮我们解决这个问题,帮我们界定数字之间的边界。但是,当我们要表示的数据不只是一组的时候,又该怎么来界定边界? - -比如,我们有两组数,分别是$1,3,5,7,9,11$和$0,2,4,6,8,10$,假设以一个字节(十六进制)表示的话,就是下面这个样子的: - -```cpp -0x01030507 0x090B0002 0x0406080A -``` - -好的,接下来请完成以下任务: - -* 找到第二个序列中的第三个数。 - -嗯,是不是感觉自己懵了,如果不知道第一组有几个的话你是不可能准确找到这个数字的。 - -于是我们得到了一个简单的界定序列边界的方法,就是要知道各序列对应的长度。 - -比如我们知道这两个序列是连续放在一起的,第一个序列长度是$6$,同时也知道要找的是第二个序列的第三个数,那么这个数就应该是第$6+3$个数字。 - -当然,这个数字要从$1$数过去才行,否则从任意其他的位置数,都不能得到我们想要的结果。 - -## 长度 - -我们可以看到,界定一个序列,必须要知道的是两点,第一个是他的起始元素,第二个就是这个序列的长度。 - -一般来说,当这个序列的长度固定不变,并且每个元素是挨在一起的时候,我们常把它叫做array。 - -> 关于array,我非常不喜欢“数组”这个翻译,因为这里面并不一定是数,也有可能是符,所以我会尽可能地使用array来表示它。 - -在JavaScript中,定义一个array如下: - -```javascript -let nums = [1, 2, 3, 4, 5] -``` - -对于array来讲,初始元素是方括号中的取值为0的时候的值。在方括号中的值我们称之为index(**索引**,奇葩翻译一般叫做**下标**)。这些索引以数字的形式出现,作为序号来表示数据在序列中的位置。 - -有个重点是,这里的索引是从$0$开始,最大是$序列长度-1$,这其实也就是为什么我们在第三章讲过程的时候提到循环的写法的一个原因。 - -不过对于遍历序列来说还支持另外一种写法。 - -```javascript - -for(let item of nums) { - console.log(item); -} -``` - -不需要你去管什么索引了,这样子写就像是在说 - -```javascript -对于 nums 里面的 item - 输出 item 的值 -``` - -当然,在Python中看起来更自然: - -```python -for item in nums: - print(item) -``` - -## 串 - -JavaScript中的字符串与array类似,自己维护了长度。但在早期的一些编程语言(如C语言)中,是没有做这种记录的,取而代之的是使用一个特定的标志来表示,这里是最后一个值为空。这种字符串也有另外一个名字,叫空结尾串(Null-terminated String)。 - -这种特殊标记就可以让我们界定一个序列的边界的另外一种方式。 - -我们可以来简单模拟一下这个行为: -```javascript -let content = ['h', 'e', 'l', 'l', 'o', null]; - -// 求长度: - -function length(nts) { - let length = 0; - for (let item of nts) { - if (item != null) { - length ++; - } else { - break; - } - } - return length; -} - -length(content) // 5 -``` - -而实际存放这个串的空间大小: -```javascript -content.length // 6 -``` - -## 可变长序列 - -试想你在一个笔记本上记录内容,每一行算是一个条目,当一页记录满以后,并不能连续地往下面记了,就需要翻到下一页纸。 - -连续不连续并不重要,重要的是我们能从这一页翻到下一页。 - -对应到我们的序列表示上,只要当前元素能够记录下一个元素的位置,是不是就表示我们的序列就能从第一个元素全部找出来了呢? - -就像这张图片描述的: - -![list](../res/images/cons-cells.png) - -这种结构被称为链接列表(也叫链表,Linked List)在上世纪五十年代就已经设计用于实际的程序。链表这种结构非常的灵活,可以很轻松的在任意位置添加或者删除数据。因为并不需要内存结构上的连续,所有的位序关系都是靠前一个元素和后一个元素之间的链接关系来确定,而添加或者删除数据只要改变一下这种链接关系就可以了。 - -但是对于链表来说一个很大的问题就是访问数据。比如我们要访问链表的第n个元素的时候,对于array和串这种结构来说,因为内存空间上是连续的,直接做一次加法就能找到,但是对于链表来说每次都要从第一个元素开始找起。而且,这种形式的链表又称作单链表(Singly Linked List),是没有办法从尾部往头部走的。 - -所以,在单链表的基础上,又构造了双链表(Double Linked List),每一个元素除了纪录自己对应的下一个元素之外,还要保存自己对应的上一个元素。这种时候如果做一些相邻元素的访问,就比单链表高效了一些。 - -比如,当我访问列表中第7个元素,然后再去访问第6个的时候,只要从第7个元素向前查找一次就可以了。 - -也许以后你可能还会听说到循环链表(Circular Linked List),或者是其他的形式,这些都离不开最初链表的思想:把数据和位置做关联。 - -## 线性结构 - -我们简单的做一个总结。 - -对于array,string和list我们都称为序列,原因其实是显而易见的。 - -首先,他们满足这样一点:所有的都是从头到尾连续地组织在一起,从任何地方出发都能找到下一个元素或者是到序列尾部。就像是在头和尾之间有一条线把他们串起来一样,而且在逻辑表示上,我们也是把他们放成一串。 - -另外,对于每一个元素,他们在序列中的位置是固定的,换句话说,序列中的元素是有前后顺序的。当我任意交换两个元素的位置,得到的新序列就跟原来的序列是不同的。 - -第三点就是,当我们从第一个元素开始找下去的时候,总是能找到最后一个元素(对于没有最后一个元素的情况,我们会在以后进行探讨)。 - -总结下来就是,序列是线性的(Linear)、有序的(Ordered,类比“排序过的(Sorted)”)和有穷的(Finite)。 - -## 生成器 - -很多时候我们的序列是通过某种规则产生的,而这种规则并不会一次性把所有元素都算出来。 - -比如自然数、斐波那契、日志流、用户滚动加载的结果……这类序列可能很长,甚至没有终点。如果你强行把它们“全算出来”,要么没必要,要么根本算不完。 - -这时候就需要**生成器**(Generator)和**迭代器**(Iterator):让序列“按需生产”,需要一个元素就产出一个,产完就停。 - -### 迭代器与可迭代对象 - -在 JavaScript 里,迭代器是一个有 `next()` 方法的对象,`next()` 返回 `{ value, done }`: - -```javascript -let counter = { - current: 0, - next() { - return { value: this.current++, done: false }; - } -}; - -counter.next(); // { value: 0, done: false } -counter.next(); // { value: 1, done: false } -``` - -而**可迭代对象**(Iterable)是指“可以被 `for...of` 遍历”的对象,它们内部会提供一个迭代器。 - -### 生成器函数 - -生成器函数用 `function*` 定义,用 `yield` 产出元素: - -```javascript -function* range(start, end) { - for (let i = start; i <= end; i++) { - yield i; - } -} - -for (let n of range(1, 5)) { - console.log(n); -} -``` - -这就像一个“可暂停”的函数:每次 `yield` 交出一个值,下次从上次停下的位置继续。 - -更有趣的是,可以定义**无穷序列**: - -```javascript -function* naturals() { - let n = 0; - while (true) { - yield n++; - } -} -``` - -但是要小心:无穷序列如果直接展开,会卡死。 - -所以通常会配合一个“截断器”(take): - -```javascript -function* take(n, iterable) { - let i = 0; - for (let item of iterable) { - if (i++ >= n) break; - yield item; - } -} - -[...take(5, naturals())]; // [0, 1, 2, 3, 4] -``` - -生成器的意义在于:**节省内存、延迟计算、按需处理**。 -这也是很多“流式处理”能成立的基础。 - -### 扩展阅读:函数式惰性序列(可跳过) - -除了 `function*`,还可以用“函数 + 闭包”来模拟惰性序列(按需展开)。 - -```javascript -function cons(x, y) { - return f => f(x, y); -} - -function head(s) { - return s((x, y) => x); -} - -function tail(s) { - return s((x, y) => y); -} - -function zeros() { - return cons(0, () => zeros()); -} - -function take(n, s) { - if (n === 0) return []; - return [head(s), ...take(n - 1, tail(s)())]; -} - -take(5, zeros()); // [0, 0, 0, 0, 0] -``` - -这里 `tail` 返回的是一个函数,只有在你“强制求值”的时候(调用它)才会继续生成后续内容,这就是惰性(lazy)的核心思路。 - -### 练习:最长公共子序列(Longest Common Subsequence) - -通常我们会对两个序列的内容进行对比,比较这两个序列有什么地方不同。这就是典型的LCS问题。LCS的应用很多,大部分版本控制系统的核心逻辑中就有它的存在。 - -请参阅Wikipedia对LCS问题的分析和讨论,尝试实现求LCS的算法,并分析: - -* 如果用 JavaScript 实现,你至少需要“可重复遍历”的序列,还是“可随机访问”的序列? -* 如果你了解 C++ 的迭代器分类,请判断 LCS 至少需要哪一类迭代器(比如 Forward / Random Access)。 - -> 提示:LCS 经典算法需要频繁访问任意位置。 - -## 延伸阅读 - -* [复杂度 Complexity](../reference/glossary/complexity.md) -* [缓存 Cache](../reference/glossary/cache.md) diff --git a/docs/chapters/ch06_data.md b/docs/chapters/ch06_data.md deleted file mode 100644 index 85cd5dc..0000000 --- a/docs/chapters/ch06_data.md +++ /dev/null @@ -1,276 +0,0 @@ -# 数据 - -很多时候我们要处理的东西并不只是一个简单的数据,或者是一个由多个数据组成的简单序列。比如,当我们要考虑一间房子,关注的是户型、面积和位置等;考虑一辆汽车,关注的是品牌、价格和最高时速等;考虑一个学生,关注的是他的学号、姓名、各科成绩等。 - -这时候,序列、引用和各种标量(Scalar)类型都不能很好地满足我们的需要,我们需要**把多种数据打包在一起**。 - -## 结构 - -比如我们要表示一辆车,最直接的方式是用数组: - -```javascript -let car = ['Nissan', 500000, 180]; -``` - -这种写法虽然能用,但不够清晰:谁是价格?谁是速度?一不小心就会写错。 - -于是我们更常用“带名字的数据”,也就是**结构**。在 JavaScript 里,这就是对象(Object): - -```javascript -let car = { - brand: 'Nissan', - price: 500000, // RMB - speed: 180 // km/h -}; - -car.brand; // 'Nissan' -car['price']; // 500000 -``` - -结构的好处是: - -* 读起来更像人话; -* 字段有名字,语义更明确; -* 字段可以是不同类型,甚至是嵌套结构。 - -比如一个学生: - -```javascript -let student = { - id: 'S001', - name: 'Kimmy', - scores: { math: 92, english: 86 }, - hobbies: ['reading', 'music'] -}; -``` - -### 结构的本质 - -房子、汽车、学生这些“概念性实体”(Entity)都有一些属性(Property)以及与其他实体的关系(Relation)。 - -当我们给这些属性贴上标签,把它们组织起来,就形成了结构。换句话说,我们给一块数据“赋予了语义”。 - -## 递归数据类型 - -有些结构是**递归的**:它包含了它自己。 - -最经典的例子就是链表(List)。一个节点包含一个值和下一个节点: - -```javascript -function node(value, next = null) { - return { value, next }; -} - -let list = node(1, node(2, node(3, null))); -``` - -遍历它很简单: - -```javascript -function printList(list) { - for (let p = list; p !== null; p = p.next) { - console.log(p.value); - } -} -``` - -当然也可以用递归(注意递归层数太深会导致调用栈溢出): - -```javascript -function printList(list) { - if (list === null) return; - console.log(list.value); - printList(list.next); -} -``` - -### 扩展阅读:C++ 中的递归结构(对比) - -> 可以跳过,只是帮助理解“指针 + 递归”。 - -```cpp -struct ListNode { - int value; - ListNode* next; -}; - -void printList(ListNode* list) { - if (list == nullptr) return; - std::cout << list->value; - printList(list->next); -} -``` - -## 构造器(Constructor) - -当结构变复杂时,我们希望“创建时就自动把字段填好”。 - -JavaScript 提供了 `class` 和 `constructor`: - -```javascript -class User { - constructor(name, age, height) { - this.name = name; - this.age = age; - this.height = height; - } -} - -let user = new User('Kimmy', 18, 178); -``` - -你也可以用工厂函数做到类似效果: - -```javascript -function makeUser(name, age, height) { - return { name, age, height }; -} -``` - -构造器的意义在于:**统一初始化规则**,并且可以在创建时做一些必要的检查或计算。 - -## 递归结构上的操作 - -既然结构是递归的,那么操作也可以是递归的。 - -比如我们把链表里的每个数取反: - -```javascript -function mapList(list, transformer) { - if (list === null) return null; - return { - value: transformer(list.value), - next: mapList(list.next, transformer) - }; -} - -let list2 = mapList(list, x => -x); -``` - -## 作为数据的过程(Procedure as Data) - -在 JavaScript 中,函数本身就是数据。我们可以把它传给另一个函数,或者作为返回值。 - -```javascript -const negate = x => -x; -mapList(list, negate); -``` - -这就是“高阶函数”的基础,也是之后闭包与对象封装的铺垫。 - -### 补充:函数对象的常见用法 - -JavaScript 里函数是第一类值,可以像变量一样传递。一个常见场景是“比较器”: - -```javascript -[1, 111, 12].sort((x, y) => x - y); // [1, 12, 111] -``` - -`(x, y) => x - y` 就是一个函数对象,它告诉 `sort` 如何比较两个元素。 - -## 树 - -另一种经典的递归结构是树(Tree)。比如二叉树: - -```javascript -class TreeNode { - constructor(value, left = null, right = null) { - this.value = value; - this.left = left; - this.right = right; - } -} -``` - -下面这棵树表示表达式 `(1 + (2 - 3)) * 4`: - -``` - * - / \ - + 4 - / \ - 1 - - / \ - 2 3 -``` - -### 遍历(Traverse) - -先序遍历(Preorder): - -```javascript -function preorder(node, visit) { - if (!node) return; - visit(node.value); - preorder(node.left, visit); - preorder(node.right, visit); -} -``` - -中序遍历(Inorder): - -```javascript -function inorder(node, visit) { - if (!node) return; - inorder(node.left, visit); - visit(node.value); - inorder(node.right, visit); -} -``` - -后序遍历(Postorder): - -```javascript -function postorder(node, visit) { - if (!node) return; - postorder(node.left, visit); - postorder(node.right, visit); - visit(node.value); -} -``` - -### 深度优先(Depth-First)和广度优先(Breadth-First) - -如果从根节点出发,**先一路走到底再回退**,就是深度优先(DFS)。 - -如果**一层一层地访问**,就是广度优先(BFS)。 - -实现 DFS 通常用“栈”,实现 BFS 通常用“队列”。 - -```javascript -function dfs(root, visit) { - if (!root) return; - let stack = [root]; - while (stack.length) { - let node = stack.pop(); - visit(node.value); - if (node.right) stack.push(node.right); - if (node.left) stack.push(node.left); - } -} - -function bfs(root, visit) { - if (!root) return; - let queue = [root]; - while (queue.length) { - let node = queue.shift(); - visit(node.value); - if (node.left) queue.push(node.left); - if (node.right) queue.push(node.right); - } -} -``` - -> 提醒:`queue.shift()` 会移动数组,性能一般。生产环境里通常会用真正的队列结构。 - -## 练习 - -* 实现一个 `sumList`,返回链表所有元素的和。 -* 改造 `mapList`,写出一个 `filterList`。 -* 用代码实现先序、中序、后序遍历。 -* 用代码实现 DFS 和 BFS,并比较它们在“查找指定节点”上的行为差异。 - -## 延伸阅读 - -* [值语义与引用语义 Value vs. Reference](../reference/glossary/value-reference.md) -* [序列化 Serialization](../reference/glossary/serialization.md) diff --git a/docs/chapters/ch07_state.md b/docs/chapters/ch07_state.md deleted file mode 100644 index be68166..0000000 --- a/docs/chapters/ch07_state.md +++ /dev/null @@ -1,137 +0,0 @@ -# 状态 - -程序中的很多数据是在不停变化的,比如时间、用户输入、网络请求结果、游戏中的位置和生命值。当一个可变对象发生变化时,我们就说它的**状态**发生了改变。 - -简单说:**数据是静态的,状态是“随时间变化的数据”。** - -## 可变状态 - -最典型的状态就是变量: - -```javascript -let count = 0; -count = count + 1; // 现在 count 的状态变了 -``` - -`count` 在不同的时刻有不同的值,这就是状态的变化。 - -### 状态与副作用 - -如果一个函数会改变外部状态,我们就说它有“副作用”: - -```javascript -let total = 0; -function add(x) { - total += x; // 修改了外部状态 -} -``` - -副作用并不“坏”,但它会让程序更难预测。所以很多设计会尽量把“计算逻辑”和“状态修改”分开。 - -## 共享状态与冲突 - -当多人(或多个任务)同时修改同一份状态,就会出现“冲突”。 - -例如一个账户余额: - -```javascript -let balance = 1000; - -function deposit(amount) { - let current = balance; - balance = current + amount; -} - -function withdraw(amount) { - let current = balance; - balance = current - amount; -} -``` - -如果两个操作“交错执行”,可能出现意外结果: - -``` -balance = 1000 -A: 读到 1000 -B: 读到 1000 -A: 写回 1200 -B: 写回 800 -最终:800(丢失了一次更新) -``` - -这类问题叫**竞态条件**。它很难排查,因为结果依赖“执行顺序”。 - -> 提醒:即使 JavaScript 是单线程,只要出现异步切换(`await` / 回调),也会发生类似“交错执行”。 - -### 为什么尽量避免共享状态? - -* 结果依赖时序,难测试、难复现。 -* 一个地方的修改,会影响所有“别名”。 -* 并发或异步时容易出现竞态。 -* 代码读起来不清楚“谁在改”。 - -## 不可变更新 - -一种常见的对策是**不直接修改原对象**,而是创建一个新对象: - -```javascript -let user = { name: 'Kimmy', age: 18 }; -let updated = { ...user, age: 19 }; // 不修改原对象 -``` - -这种方式叫“不可变更新”(Immutable Update)。它更安全,但会多创建一些对象。 - -> 提醒:`const` 只保证变量不变,不保证对象内容不变。 - -## 迭代器也是状态 - -迭代器之所以能“往前走”,就是因为它内部保存了当前位置: - -```javascript -let iterator = { - index: 0, - next(arr) { - if (this.index >= arr.length) return { done: true }; - return { value: arr[this.index++], done: false }; - } -}; -``` - -这个 `index` 就是状态。每次 `next()` 都在改变它。 - -## 状态机 - -当一个系统只有有限种状态,并且可以在状态之间切换,我们就称它是**状态机**。 - -例如红绿灯: - -``` -红 -> 绿 -> 黄 -> 红 ... -``` - -用代码可以这样表达: - -```javascript -let light = { - state: 'red', - next() { - if (this.state === 'red') this.state = 'green'; - else if (this.state === 'green') this.state = 'yellow'; - else this.state = 'red'; - } -}; -``` - -状态机特别适合描述“流程有明显阶段”的问题,比如登录流程、订单流程、游戏关卡等。 - -## 练习 - -* 写一个计数器对象 `counter`,包含 `inc()` 和 `dec()` 方法,并维护内部状态。 -* 用状态机描述“电梯”的状态转换(例如:停靠、上行、下行、开门)。 -* 设计一个函数 `move(point, dx, dy)`,返回移动后的新坐标,要求不修改原对象。 -* 写一个“库存扣减”示例,说明共享状态在并发时可能出现负库存。 - -## 延伸阅读 - -* [不可变性 Immutability](../reference/glossary/immutability.md) -* [竞态条件 Race Condition](../reference/glossary/race-condition.md) diff --git a/docs/chapters/ch08_reference.md b/docs/chapters/ch08_reference.md deleted file mode 100644 index e5cf2e0..0000000 --- a/docs/chapters/ch08_reference.md +++ /dev/null @@ -1,205 +0,0 @@ -# 引用 - -> 注:本文中提到的“引用”是广义概念,不仅仅指 JavaScript 里的“引用类型”。 - -在程序里,我们有两种常见的“东西”: - -* **值**(Value):比如数字、布尔值、字符串。 -* **引用**(Reference):指向某块数据的“地址”。 - -## 内存里发生了什么(极简版) - -可以把内存想成一个大仓库: - -* 小而简单的数据(例如数字)可以直接放在变量里。 -* 复杂对象通常放在“仓库里”,变量里只放一个“门牌号”(引用)。 - -在 JavaScript 里,基本类型是“值语义”,对象/数组/函数是“引用语义”。 - -## 值语义 vs. 引用语义 - -看一个最简单的例子: - -```javascript -let a = 1; -let b = a; - -b = 2; -a; // 1 -``` - -`a` 和 `b` 是两个独立的值,互不影响。 - -再看对象: - -```javascript -let obj1 = { x: 1 }; -let obj2 = obj1; - -obj2.x = 2; -obj1.x; // 2 -``` - -`obj1` 和 `obj2` 实际上指向同一份数据,这就叫“引用语义”。当两条变量指向同一块数据时,它们就成了**别名**(alias)。 - -## 引用的作用 - -引用让我们可以**共享大型结构**,避免频繁复制。例如一个很大的数组: - -```javascript -let big = new Array(1000000).fill(0); -let shared = big; // 共享引用,几乎不占额外内存 -``` - -如果每次都复制一份,性能和内存都会受影响。 - -## 引用与数据结构 - -链表、树、图等复杂结构都离不开引用。 - -以链表为例:每个节点保存“下一个节点”的引用: - -```javascript -function node(value, next = null) { - return { value, next }; -} - -let list = node(1, node(2, node(3))); -``` - -树和图也是一样:通过引用把节点连接起来。 - -更进一步:**同一个节点也可以被多个父节点引用**,这样就形成了“有向无环图”(DAG)。这在编译器、缓存结构里很常见。 - -## 函数参数里的引用 - -JavaScript 函数传参时,传递的是“值”,但对于对象来说,这个“值”是引用: - -```javascript -function addOne(point) { - point.x += 1; -} - -let p = { x: 0 }; -addOne(p); -p.x; // 1 -``` - -这也是很多“状态被意外修改”的来源。 - -## 相等与同一 - -两个对象长得一样,不代表它们是“同一个”: - -```javascript -let a = { x: 1 }; -let b = { x: 1 }; - -(a === b); // false -``` - -`===` 对对象比较的是**引用是否相同**。如果你需要比较内容是否相同,就要写深度比较(或用库)。 - -## 拷贝:浅拷贝与深拷贝 - -避免“别名问题”的一种方式是复制: - -```javascript -let user = { name: 'Kimmy', age: 18 }; -let copy = { ...user }; // 浅拷贝 -``` - -浅拷贝只复制“第一层”。如果有嵌套对象,还是共享引用: - -```javascript -let u1 = { profile: { city: 'SH' } }; -let u2 = { ...u1 }; - -u2.profile.city = 'BJ'; -u1.profile.city; // 'BJ' -``` - -深拷贝更彻底,但实现更复杂。一个常见(但有局限)的方法是: - -```javascript -let deep = JSON.parse(JSON.stringify(u1)); -``` - -> 提醒:这种方式会丢失函数、`Date`、`Map` 等复杂类型。 - -## 引用与对象生命周期(内存管理) - -在某些语言里(如 C/C++),你需要手动申请和释放内存。释放不当就会造成“内存泄漏”或“野指针”。 - -JavaScript 有自动垃圾回收(GC),但这并不等于“不会泄漏”。 - -### 引用计数(概念) - -一种常见的内存管理方式叫“引用计数”: - -* 每个对象有一个计数。 -* 有一个新的引用指向它,计数 +1。 -* 引用消失,计数 -1。 -* 计数为 0 时释放。 - -问题是:**循环引用**会让计数永远不为 0: - -```javascript -let a = {}; -let b = {}; -a.other = b; -b.other = a; -``` - -引用计数在这种情况下可能无法回收。现代 JS 引擎通常使用“可达性分析”而不是纯引用计数,所以可以处理循环引用。 - -### 常见的内存泄漏场景(JS) - -* **全局变量**:全局对象引用一直存在。 -* **事件监听未移除**:DOM 结点被移除,但监听还在。 -* **缓存无限增长**:数组/Map 只增不减。 -* **闭包长时间持有大对象**:函数引用了一个很大的结构。 - -例如: - -```javascript -let cache = []; -function remember(data) { - cache.push(data); // cache 不断变大 -} -``` - -### 如何避免 - -* 及时清理不用的引用(设为 `null`)。 -* 给缓存设置上限。 -* 事件监听记得 `removeEventListener`。 -* 临时映射用 `WeakMap` / `WeakSet`。 - -## 小结 - -引用让我们能够构建复杂结构、提高效率,但也带来共享状态、别名、生命周期等问题。理解引用是理解“程序行为”的关键一步。 - -## 练习 - -* 解释下面代码的输出: - -```javascript -let a = [1, 2]; -let b = a; -let c = a.slice(); - -b.push(3); - -console.log(a, b, c); -``` - -* 写一个 `cloneUser(user)`,返回一个“只做浅拷贝”的函数。 -* 写一个 `freeze` 的例子,说明不可变对象能减少引用带来的副作用。 -* 设计一个链表结构,让两个链表共享同一个尾部节点,并解释这样做的好处与风险。 - -## 延伸阅读 - -* [值语义与引用语义 Value vs. Reference](../reference/glossary/value-reference.md) -* [垃圾回收 Garbage Collection](../reference/glossary/garbage-collection.md) -* [栈与堆 Stack & Heap](../reference/glossary/stack-and-heap.md) diff --git a/docs/chapters/ch09_closure.md b/docs/chapters/ch09_closure.md deleted file mode 100644 index 2431e30..0000000 --- a/docs/chapters/ch09_closure.md +++ /dev/null @@ -1,180 +0,0 @@ -# 闭包 - -闭包(Closure)是 JavaScript 里最重要、也最容易被误解的概念之一。 - -一句话定义:**闭包是“函数 + 它捕获的外部变量”**。 - -## 词法作用域 - -JavaScript 采用词法作用域(Lexical Scope):变量的可见范围由“写代码的位置”决定,而不是“运行时的位置”。 - -这意味着:内部函数可以访问外部函数里的变量。 - -```javascript -function makeAdder(x) { - return function (y) { - return x + y; - }; -} - -let add10 = makeAdder(10); -add10(3); // 13 -``` - -这里 `add10` 这个函数里,依然能访问 `x`,即使 `makeAdder` 已经执行完毕。 - -这个“仍然能访问”的能力,就是闭包。 - -### 扩展阅读:用闭包模拟对象(可跳过) - -只用函数也可以模拟“对象 + 方法 + 封装”。比如构造一个“有序对”: - -```javascript -function pair(left, right) { - return fn => fn(left, right); -} - -function left(a, b) { - return a; -} - -function right(a, b) { - return b; -} - -let p = pair(1, 2); -p(left); // 1 -p(right); // 2 -``` - -`pair` 的内部数据并没有直接暴露出来,只能通过 `left` / `right` 访问,这就是“封装”的味道。 - -## 用闭包保存状态 - -闭包最常见的用法是“封装状态”: - -```javascript -function makeCounter() { - let count = 0; - return function () { - count += 1; - return count; - }; -} - -let counter = makeCounter(); -counter(); // 1 -counter(); // 2 -``` - -`count` 是私有变量,只能通过闭包访问,这就是“私有状态”的来源。 - -## 闭包与回调 - -很多回调函数依赖外部变量,这也是闭包的体现: - -```javascript -function repeat(message) { - setTimeout(function () { - console.log(message); - }, 1000); -} -``` - -`setTimeout` 执行时,`repeat` 已经结束了,但 `message` 仍然可用。 - -## 闭包与模块 - -闭包可以用来“封装私有变量”,形成简单的模块: - -```javascript -function createStore() { - let data = {}; - return { - set(key, value) { data[key] = value; }, - get(key) { return data[key]; } - }; -} - -let store = createStore(); -store.set('x', 10); -store.get('x'); // 10 -``` - -这里的 `data` 只有 `set/get` 能访问,这就是“私有状态”。 - -## 闭包与生命周期 - -闭包会“延长”被捕获变量的生命周期:只要闭包还存在,这些变量就不会被回收。 - -这通常很有用,但也可能造成内存占用: - -```javascript -function holdBigData() { - let big = new Array(1000000).fill(0); - return () => big.length; -} - -let f = holdBigData(); // big 被一直保留 -``` - -因此,闭包不是问题本身,**不释放的引用**才是问题。 - -## 常见陷阱:循环与 var - -```javascript -var funcs = []; -for (var i = 0; i < 3; i++) { - funcs.push(function () { - console.log(i); - }); -} - -funcs[0](); // 3 -funcs[1](); // 3 -funcs[2](); // 3 -``` - -因为 `var` 没有块级作用域,三个函数共享同一个 `i`。 - -解决方式有两种: - -```javascript -// 方式一:用 let -for (let i = 0; i < 3; i++) { - funcs.push(function () { console.log(i); }); -} - -// 方式二:用立即执行函数 -for (var i = 0; i < 3; i++) { - (function (x) { - funcs.push(function () { console.log(x); }); - })(i); -} -``` - -## 小结 - -闭包让函数具有“记忆”,也是对象、模块、异步回调的基础。 - -下一章的“对象”,很多时候可以看作“闭包 + 约定”。 - -## 练习 - -* 写一个 `makeOnce(fn)`,让 `fn` 只能执行一次。 -* 写一个 `makeTimer()`,返回一个函数,每次调用返回距离创建时的毫秒数。 -* 解释为什么下面的代码能正常输出 `hello`: - -```javascript -function outer() { - let msg = 'hello'; - return function () { console.log(msg); }; -} - -outer()(); -``` - -## 延伸阅读 - -* [词法作用域 Lexical Scope](../reference/glossary/lexical-scope.md) -* [垃圾回收 Garbage Collection](../reference/glossary/garbage-collection.md) diff --git a/docs/chapters/ch10_object.md b/docs/chapters/ch10_object.md deleted file mode 100644 index 280d30f..0000000 --- a/docs/chapters/ch10_object.md +++ /dev/null @@ -1,143 +0,0 @@ -# 对象 - -广义地讲,任何一个在程序中出现的元素都可以称作对象:整数对象、字符串对象、函数对象…… - -但在“面向对象编程”(OOP)里,**对象**通常指“带状态 + 带行为”的实体。 - -## 面向对象编程(Object-Oriented Programming) - -面向对象的核心思想是: - -* **封装**:把数据和操作放在一起。 -* **抽象**:只暴露必要的接口。 -* **多态**:同一接口,多个实现。 - -### 一个最简单的例子 - -不同的“车”(Vehicle)都可以跑(run):汽车、卡车、推车…… - -```javascript -class Vehicle { - run() { - console.log('vehicle running'); - } -} - -class Car extends Vehicle { - run() { - console.log('car running'); - } -} - -class Truck extends Vehicle { - run() { - console.log('truck running'); - } -} - -let v = new Car(); -v.run(); // car running -``` - -这里 `Vehicle` 提供了统一接口 `run()`,不同子类各自实现。 - -### 多态:同一个接口,不同表现 - -```javascript -function start(vehicle) { - vehicle.run(); -} - -start(new Car()); -start(new Truck()); -``` - -只要对象“能 run”,就能被 `start` 使用。这种风格叫 **鸭子类型**(Duck Typing)。 - -## JavaScript 的对象模型(极简版) - -JavaScript 的对象本质上是“键值对 + 原型链”。 - -`class` 只是语法糖,本质还是函数和原型: - -```javascript -function Person(name) { - this.name = name; -} -Person.prototype.say = function () { - console.log('hi, ' + this.name); -}; -``` - -使用 `class` 只是更直观: - -```javascript -class Person { - constructor(name) { - this.name = name; - } - say() { - console.log('hi, ' + this.name); - } -} -``` - -## 封装与私有数据 - -对象常常需要“只读公开,内部私有”。在 JavaScript 里有两种常见写法: - -```javascript -// 写法一:用闭包 -function createCounter() { - let count = 0; - return { - inc() { count += 1; return count; } - }; -} -``` - -```javascript -// 写法二:用私有字段(#) -class Counter { - #count = 0; - inc() { this.#count += 1; return this.#count; } -} -``` - -封装的意义在于:外部只能通过“方法”访问内部状态,避免随意修改。 - -## 继承与组合 - -继承很方便,但容易让结构变复杂。很多时候,我们用**组合**来代替继承: - -```javascript -let canRun = { - run() { console.log('run'); } -}; - -let canFly = { - fly() { console.log('fly'); } -}; - -let bird = Object.assign({}, canRun, canFly); -bird.run(); -bird.fly(); -``` - -简单理解:**继承是一棵树,组合是积木**。 - -## 小结 - -对象能把“数据 + 行为”放在一起,是程序建模最常用的方法之一。 - -下一章我们会看到:当对象和并发混在一起时,会出现新的问题。 - -## 练习 - -* 定义一个 `Animal` 类,包含 `speak()` 方法,再派生 `Dog`、`Cat`。 -* 写一个 `feed(animal)` 函数,只依赖 `animal.eat()`,不要检查类型。 -* 用组合的方式,让一个对象同时具备 `move` 和 `draw` 两种能力。 - -## 延伸阅读 - -* [原型链 Prototype Chain](../reference/glossary/prototype-chain.md) diff --git a/docs/chapters/ch11_concurrency.md b/docs/chapters/ch11_concurrency.md deleted file mode 100644 index 3d79439..0000000 --- a/docs/chapters/ch11_concurrency.md +++ /dev/null @@ -1,150 +0,0 @@ -# 并发 - -并发(Concurrency)关注的是:**多个任务在时间上交错执行**。 - -它不等于并行(Parallelism)。并行是“同时在多个核心上执行”,并发则是“看起来在同时发生”。 - -## 竞态条件(Race Condition) - -并发最常见的问题是竞态:多个任务读写同一份状态,结果取决于“谁先谁后”。 - -一个经典例子是账户余额: - -```javascript -let balance = 1000; - -function delay(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -async function deposit(amount) { - let current = balance; - await delay(10); - balance = current + amount; -} - -async function withdraw(amount) { - let current = balance; - await delay(10); - balance = current - amount; -} - -Promise.all([ - deposit(200), - withdraw(200) -]).then(() => console.log(balance)); -``` - -最终结果可能是 800 或 1200,取决于哪一个先写回。 - -这种“读取-修改-写回”的操作如果不是原子的,就会出现竞态。 - -## 原子性与临界区 - -`count = count + 1` 看起来是一步,其实包含“读取 + 计算 + 写回”三步。 -当多个任务交错执行时,增量可能丢失。 - -```javascript -let count = 0; - -async function inc() { - let current = count; - await delay(0); - count = current + 1; -} - -Promise.all([inc(), inc()]).then(() => console.log(count)); // 可能是 1 -``` - -这段代码里,`await` 让两个任务交错执行,导致“加 1”只发生了一次效果。 -“必须连续执行、不能被打断”的那段代码就叫**临界区**。 - -## 常见解决思路 - -* **锁/互斥**:同一时间只能一个任务修改共享状态。 -* **队列化**:把操作排队,按顺序执行。 -* **不可变数据**:不改共享对象,而是产生新对象。 -* **消息传递**:不共享内存,只传递消息(如 Worker)。 - -## 死锁(概念) - -如果两个任务互相等待对方释放资源,就会永远卡住,这叫死锁。 -JavaScript 主线程很少直接写锁,但在多线程环境(或数据库事务)里这是典型问题。 -你要记住的不是“死锁怎么写”,而是:**多个资源 + 相互等待 = 风险**。 - -## JavaScript 的并发模型 - -JavaScript 在浏览器和 Node.js 中都遵循“事件循环”模型: - -* 代码在一个主线程上执行。 -* 异步任务被放到队列中。 -* 事件循环不断从队列里取任务执行。 - -### 宏任务与微任务(极简版) - -* `setTimeout` 属于宏任务。 -* `Promise.then` / `await` 的回调属于微任务。 - -```javascript -console.log('A'); -setTimeout(() => console.log('B')); -Promise.resolve().then(() => console.log('C')); -console.log('D'); - -// 输出顺序:A D C B -``` - -### async/await - -`async/await` 是 Promise 的语法糖,本质仍然是“任务队列 + 回调”。 - -## 在单线程里也会发生“并发问题” - -即使只有一个线程,只要有异步切换点(`await`、回调),也会出现“交错执行”。 - -上面的银行例子就是典型。 - -一种简单的修复方法是**串行化**: - -```javascript -async function runInOrder() { - await deposit(200); - await withdraw(200); - console.log(balance); -} -``` - -如果任务很多,可以用一个“队列”把它们排起来: - -```javascript -function createQueue() { - let last = Promise.resolve(); - return function enqueue(task) { - last = last.then(() => task()).catch(() => {}); - return last; - }; -} - -let enqueue = createQueue(); - -enqueue(() => deposit(200)); -enqueue(() => withdraw(200)); -``` - -## 并行:Worker 里的消息传递 - -浏览器里可以用 Web Worker,Node.js 里有 Worker Threads。它们和主线程通过消息通信,避免共享内存带来的竞态。 - -这部分属于进阶内容,这里先记住一个原则:**能不共享就不共享**。 - -## 练习 - -* 修改上面的银行例子,让它在并发情况下仍然得到正确结果。 -* 写一个 `delay(ms)`,然后用 `async/await` 顺序打印 1、2、3(每秒一个)。 -* 写一个“任务队列”,保证多个异步任务按顺序执行。 - -## 延伸阅读 - -* [事件循环 Event Loop](../reference/glossary/event-loop.md) -* [竞态条件 Race Condition](../reference/glossary/race-condition.md) -* [死锁 Deadlock](../reference/glossary/deadlock.md) diff --git a/docs/chapters/index.md b/docs/chapters/index.md deleted file mode 100644 index 20c00a0..0000000 --- a/docs/chapters/index.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: home -title: 计算机编程 -hero: - name: 计算机编程入门指南 - text: Introduction to Programming - tagline: "从零开始你的计算机编程之旅" - image: - src: /hero.png - alt: Introduction to Programming - actions: - - text: 开始阅读 → - link: /chapters/intro - theme: brand - - text: 工具上手 🛠️ - link: /guides/ - theme: alt -# features: -# - icon: 🛠️ -# title: Simple and minimal, always -# details: Lorem ipsum... -# - icon: -# src: /cool-feature-icon.svg -# title: Another cool feature -# details: Lorem ipsum... -# - icon: -# dark: /dark-feature-icon.svg -# light: /light-feature-icon.svg -# title: Another cool feature -# details: Lorem ipsum... ---- diff --git a/docs/chapters/intro.md b/docs/chapters/intro.md deleted file mode 100644 index 804016c..0000000 --- a/docs/chapters/intro.md +++ /dev/null @@ -1,99 +0,0 @@ -# 引言 - -## 编程 - -编程(Programming)是一项创造性的活动,如同创作(Composing)、绘画(Painting)等一样。我们常常会通过一系列的步骤把所需要的部件进行组合,得到另一个期望的结构,或者是解决一个实际性的问题。表达和构建这些步骤的过程,就是编程(当然,在脱离程序语言以后,也可以把它叫做**规划**(Programme))。 - -我们会简单地把一件件小事按**顺序**做,这样一件件小事的组合以后就可以完成一件更大的事情。比如我们会切菜,生火,炒菜,煮饭,这样我们就能够做出一顿午餐。 - -当然在这个过程中可能会设计一些**计算**。我们会考虑,需要放几勺盐和几把米,以适合最后吃午餐的人的营养均衡。在特殊的时候,甚至需要精确到毫克级别的营养规划,以及不同人的营养结构的差异等。我们会在第二节探讨关于计算,以及计算的**组合**等相关问题。 - -在规划和解决问题的时候可能并不是一帆风顺的,有时候我们会面临各种抉择。比如,考虑明天下午吃素还是吃荤,吃鸡肉和还是鱼肉,吃宫保鸡丁还是鱼香肉丝等等。有比如,在我们准备吃鱼香肉丝的时候发现没有鱼了,又该怎么办。 - -在任何需要做选择的时候,作为一个程序员(**规划师**),你不应该出现选择困难症。因为任何一个亟待解决的问题都必须得到解决之后才能让这个程序正常地执行。所以,我们在第三章中会探讨关于过程设计,以及如何利用循环和选择等手法来设计一个完善的程序的相关问题。 - -但是做饭的时候你是怎么区分盐和砂糖、醋和酱油的呢?当然你会说每个都尝一遍,但是这样既有可能浪费,又不卫生。所以习惯性地我们会关注这些材料瓶上面的标签,从上面知道我们用的是什么东西。当然对于读不懂标签的计算机来说,这些材料作为商品的时候都会有固定的条形码,这个条形码也能够让计算机知道一个被它看到的东西代表着什么。因为它知道这些条码是如何**编码**(Encoding)的。 - -当你遇到大量具有相同组件的时候,可以考虑用**序列**(Sequence)或者列表(List)来安排这些组件。比如做一道菜的每一个步骤对应在菜谱上的每一项。或者是在处理具有层级结构以及更复杂的关联的结构的时候采用**树**(Tree)和**图**(Graph)来组织。比如六度关系理论,实际上就是一张非常大的**图**(Graph)。 - -有时候会考虑到一部分组件是可变的。比如时间总是流逝的,人总会长大的。当我们尝试描述一个组件改变的时候,就引入了**状态**(State)。这种时候我们更希望能够把改变的数据和相应的操作做一个绑定,这样子绑定了组件的操作就可以看成一个闭包(Closure)。简单地比如一个计数器。当在组件上面绑定更多操作的时候,就可以把它看作一个**对象**(Object)。 - -可变的状态可能会带来的一个问题就是,如果有多个人尝试同时去修改这个状态的时候,会造成结果的不一致性。比如,一个没有加入事务处理的银行账户,两个人同时在进行操作,账户中原有1000元,甲存入了200块,但在甲存入200块还未完成的同时乙取出了200块。这种时候会有几种可能的结果,第一种就是甲先于乙完成操作,结果就是账户余额变成了800块,凭空损失了200块;另外一种就是乙先于甲完成了操作,结果账户余额就变成了甲操作的结果,1200块,凭空多出来400块。这种问题就属于**并发**(Concurrency)。 - -## JavaScript - -本书选择使用 JavaScript 来讲述编程中的各种思想和概念。 - -我们并不是在讲述 JavaScript 这门编程语言的细节,只是通过它来讲述关于“程序设计”的内容。当你需要查阅资料时,请从本书的“工具上手指南”里整理好的官方入口开始查找。 - -作为入门书籍,为了直观地表述各种概念或者原理,本书可能会使用一些非专业词汇,或者与准确定义有偏差的理解。JavaScript 作为一种生产力工具,其准确定义和行为以 ECMA-262 国际标准为准。对专业人员如有任何困惑或者争议的点,请参考标准文档的描述。同样,如果你通过本书对编程和相关的东西产生了兴趣,希望深入了解和学习,也请使用专业的书籍和资料。本书只是作为入门的引导,不对专业内容做解释和定义。 - -## 学习方式与工具 - -阅读过程中你会用到浏览器开发者工具(Inspector/Console),也会在本地使用 VS Code、Node.js 和 TypeScript。如果你是第一次接触这些工具,建议先阅读: - -* [浏览器开发者工具与 Console](../guides/browser-devtools.md) -* [安装与上手 VS Code](../guides/vscode.md) -* [安装 Node.js 并在 VS Code 运行 JavaScript](../guides/nodejs.md) -* [TypeScript 入门:安装、编译与运行](../guides/typescript.md) - -## 更新状态 - -这本书从2015年8月开始编写,中间经过了多次的重写过程,包括把编程语言从偏硬核且专业的C++转向为JavaScript、引入更多资料和练习、增补和调整章节顺序等,目前内容仍处于不稳定的状态,请参考下面的状态来确定这一章是否值得学习: - -* 第一章 环境 \(rewriting\) -* 第二章 计算 Computation \(outdated\) -* 第三章 过程 Procedural \(outdated\) -* 第四章 编码 Encoding \(outdated\) -* 第五章 序列 Sequence \(updated\) -* 第六章 数据 Data \(updated\) -* 第七章 状态 State \(updated\) -* 第八章 引用 Reference \(updated\) -* 第九章 闭包 Closure \(updated\) -* 第十章 对象 Object \(updated\) -* 第十一章 并发 Concurrency \(updated\) -* 附录 练习答案与提示 \(draft\) - -## 目录 - -* [引言](intro.md) -* [环境](ch01_environment.md) -* [计算](ch02_computation.md) -* [过程](ch03_procedure.md) -* [编码](ch04_encoding.md) -* [序列](ch05_sequence.md) -* [数据](ch06_data.md) -* [状态](ch07_state.md) -* [引用](ch08_reference.md) -* [闭包](ch09_closure.md) -* [对象](ch10_object.md) -* [并发](ch11_concurrency.md) -* [练习答案与提示](answers.md) - -## 参与贡献 - -本书的源码托管在 GitHub [IntroToProgramming/IntroductionToProgramming](https://github.com/IntroToProgramming/IntroductionToProgramming)仓库,你可以提交issue或者pull request贡献和改进本书。 - -## 支持我 - -欢迎关注我的微信公众号轻技术(lightych)或者[Telegram Lightych 频道](https://t.me/lightychannel)及时获取更新。 - -### 特别感谢以下朋友的资助 - -* [esrever10](https://github.com/esrever10) -* [karakapi](https://github.com/karakapi) -* [负一的平方根](http://sqrt-1.me) -* [linkgod](http://www.linkgod.net) -* [shellvon](https://github.com/shellvon) & [tingoo](https://github.com/tingoo) diff --git a/docs/draft/draft.md b/docs/draft/draft.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/draft/fpi.md b/docs/draft/fpi.md deleted file mode 100644 index 1bd15d5..0000000 --- a/docs/draft/fpi.md +++ /dev/null @@ -1,702 +0,0 @@ ---- -title: 你所需要知道的关于函数的一切 -date: 2020-07-17 ---- - -> 本文源自于我在 2012 ~ 2013 年写的 Meta Functions 系列文章,以及知乎专栏的《map 四种》。通过细致地讲解和练习函数相关的内容来让大家了解**函数**、**闭包**、**延迟计算**和**流**。 -> -> 你可以把它看成一个 SICP 前三章的超级简化版,不过这其中我不会讨论过多的细节。文中所有的代码都在浏览器控制台执行通过,你也可以简单地对着浏览器或者任意实现了 ES6 特性的 JavaScript REPL 试验其中的代码。 - -# 函数 - -这里的函数我比较倾向于集合论中的定义,即是两个集合之间的映射关系。也就是那种非常**纯**(pure)的函数。后文也会涉及一些带有状态的函数,不过绝大多数情况下我所期待的是对于一个函数 $f$ ,给定其参数 $x$ ,那么 $f(x)$ 的结果一定是**确定的**(deterministic)。 - -我们先来实现一个简单的函数,比如**斐波那契(fibonacci)数列**的定义。 - -$$ -fib(0) = \begin{cases} - 1, &x = 0, or &x = 1\cr - fib(n-1) + fib(n-2), &x \ge 2 -\end{cases} -$$ - -如果你熟悉递归,可以很容易根据上面的递归定义把 `fib` 函数实现了: - -```js -function fib(n) { - if (n === 0 || n === 1) { - return 1; - } - return fib(n - 1) + fib(n - 2); -} -``` - -我们来看求 `fib(10)` 的时候的展开图: - -``` -fib(10) => -fib(9) + fib(8) => -fib(8) + fib(7) + fib(7) + fib(6) => -fib(7) + fib(6) + fib(6) + fib(5) + fib(6) + fib(5) + fib(5) + fib(4) => -..... -``` - -最后展开会是一个深度为 10 节点非常多的**递归树**。 - -在我的电脑上,计算 `fib(30)` 已经需要等待一段时间,明显感觉到延迟了。 - -## 尾递归 - -对于这个操作,实际可以用另外一个办法来简化,就是通过**尾递归**,把计算的操作合并到尾调用的参数求值中去。 - -```js -function fib(n, a = 1, b = 1) { - if (n == 0) { - return a; - } - return fib(n-1, b, a + b); -} -``` - -这个时候的 `fib(10)` 的展开: - -``` -fib(10) => -fib(9, 1, 2) => -fib(8, 2, 3) => -fib(7, 3, 5) => -fib(6, 5, 8) => -fib(5, 8, 13) => -fib(4, 13, 21) => -fib(3, 21, 34) => -fib(2, 34, 55) => -fib(1, 55, 89) => -fib(0, 89, 144) => -89 -``` - -所有的计算通过两个额外的参数来累加,而不是通过反复的递归调用展开,这个时候只有递归层次的深度,就连计算 `fib(100)` 也都只需要递归调用 100 次就好,能够立即返回结果。 - -如果你曾经用**循环**的方式实现过斐波那契,那要想导出这个尾递归版本其实并不困难: - -```js -function fib(n) { - let a = 1; - let b = 1; - for (let i = 0; i < n; i++) { - [a, b] = [b, a+b]; - } - return a; -} -``` - -好的,首先我们把循环倒过来写: - -```js -function fib(n) { - let a = 1; - let b = 1; - for (let i = n; i > 0; i--) { - [a, b] = [b, a+b]; - } - return a; -} -``` - -把 `for` 改成死循环: - -```js -function fib(n) { - let a = 1; - let b = 1; - let i = n; - while(true) { - if (i === 0) { - return a; - } - [i, a, b] = [i-1, b, a+b]; - } -} -``` - -改一下参数和变量名: - -```js -function fib(n, a = 1, b = 1) { - while(true) { - if (n === 0) { - return a; - } - [n, a, b] = [n-1, b, a+b]; - } -} -``` - -`while(true)` 里面的内容,跟尾递归版的结构几乎一模一样,而这里直接改到参数的赋值,就可以一步替换为尾递归。 - -你可以对比尾递归形式和最后这个循环实现的内容。现实其实通常是反过来的,就是会通常是我们**写成尾递归的样子**,然后再由编译器**优化成循环**。这样的结果是能够省掉函数传参和压栈的过程,算是一步提效明显的优化,对时间(额外的压栈和传参操作)和空间(调用栈)的开销都能节省很多,还能避免不必要的**爆栈**(Stack Overflow)风险。 - -### 练习 - -- 请实现另外一个常用递归函数阶乘(factorial)的直接递归版和尾递归版。阶乘函数的定义如下: - -$$ -n! = \begin{cases} - 1, &n = 0 \cr - (n-1)!\times n, &n \gt 0 -\end{cases} -$$ - -# 函数进阶 - -我们上一节简单实现了一个函数,那么现在来认真地回顾考虑这么一个问题,函数到底是什么? - -其实很简单,函数就是这么一个结构,你给他一个输入,他返回给你一个输出。而其由输入得到输出的过程,是你可以定义的。 - -这其实是一个非常广义的抽象,可以拿来类比各种东西,比如一个生产流水线也可以这么抽象地看,原材料作为输入,产品作为输出;而这个流水线的每一个节点又都是有自己的输入和输出。 - -于是我们可以看到函数的另外一个特征:可组合(Composable)。 - -## 可组合性 - -比如我们定义一个函数,去求一个数组排序后的结果: - -```js -function sorted(array) { - return array.slice().sort(); -} -``` - -然后再定义一个函数,来求一个数组反转之后的结果: - -```js -function reversed(array) { - return array.slice().reverse(); -} -``` - -那如果我们要求一个数组的降序排序的结果,就直接组合这两个调用就好: - -```js -function sort_reversed(array) { - return reversed(sorted(array)); -} -``` - -正式函数的这种可组合性,让我们能通过定义有限的正交算子,来组合出无限的操作。 - -> 有些人可能会说,这个直接做成 Array 的方法,也是能够做成链式调用来组合的: -> -> ```javascript -> Array.prototype.sorted = function() { -> return this.slice().sort(); -> } -> -> Array.prototype.reversed() = function() { -> return this.slice().reverse(); -> } -> ``` -> -> 实际这两种做法本质上是同一件事情。有些编程语言(比如 D 语言)中,`x.f(y)` 与 `f(x, y)` 是等价操作,实现的这种转换叫做**统一函数调用语法**(Uniform Function Call Syntax)。 - -## 函数对象 - -回到上面的排序函数。 - -JavaScript 有一个问题,就是排序默认是用的文本序,对数字也是如此,也就是说,111 是会排在 12 前面的,而 JavaScript 的 `Array.prototype.sort` 函数会让我们传递一个匿名函数进去来作为排序的比较器(Comparator),达到对数值做排序的目的。 - -这个比较器就是一个函数对象。 - -```js -[1, 111, 12].sort((x, y) => x - y) -// => [1, 12, 111] -``` - -**函数对象**,又叫**匿名函数**,在某些语境下面也叫 lambda 表达式(因其源自于 lambda 演算)。一个函数对象是可以赋值给某个变量、作为参数传递的;或者说,函数对象,是**第一类值**(first class value)。 - -比如上文中的 `(x, y) => x - y`。 - -## 闭包 - -既然函数是**第一类值**,那么我们也就能有方式来构造他了。 - -通常一个函数在传递过程中不只是作为一个独立的单位存在,还会包括一些**上下文**,这样组合起来的一个结构就叫闭包。 - -比如,我们定义以下函数,会返回一个函数对象,而这个函数对象每次调用都会累加一次,行为表现像是一个计数器: - -```js -function makeCounter() { - let count = 0; - return () => count ++; -} -``` - -这里返回的函数对象,其实就捕获了 `makeCounter` 函数中的变量 `count`,并一直持有。在任何时候我们操作这个函数,都会改变这个变量的值。这个时候的变量可以被称为 **upvalue**(因为是**上层**(upper)函数中的**值**(value))。 - -这样看起来似乎毫无意义。 - -我们来看一个深入的结构: - -```js -function pair(left, right) { - return (fn) => fn(left, right); -} - -function left(a, b) { - return a; -} - -function right(a, b) { - return b; -} - -let p = pair(1, 2); - -p(left) // => 1; -p(right) // => 2; -``` - -`pair` 给我们构造了一个对象(这个时候已经不仅仅限于函数对象了),然后我们通过另外定义的两个函数来去访问这个对象的内部结构,而这个实际的内部结构并没有直接暴露给我们。 - -上面这段话你看到了哪些关键的东西? - -- **对象**:`pair` 构造了一个有序对,这个对象可以由我们后续定义的方法来操作和访问 -- **方法**:`left` 和 `right` 两个函数本身并没有实际的意义,与 `pair` 对象绑定以后,可以利用 `pair` 中的数据来实现预期的行为 -- **封装**:`pair` 的内部结构在构造时确定,无法在外部获取和改变内部结构,只能通过定义 `pair` 的方法来处理 - -在只有“函数”这个构造的情况下我们也能做这些事情。甚至在一些编程语言(比如 Java/C++)里面,函数对象就是通过**结构/类**加上特定的**函数/方法**来实现的,也就只有这样他们才能做捕获形成闭包。 - -# 数据抽象 - -## 可变性 - -我们知道 OO 的一个特色是可变性来描述状态,闭包在这里也完全可以实现。当然需要借助一些额外的结构。 - -```js -function mutablePair(a, b) { - return (fn) => { - let x; - [a, b, x] = fn(a, b) - return x; - } -} - -function getLeft(a, b) { - return [a, b, a]; -} - -function getRight(a, b) { - return [a, b, b]; -} - -function setLeft(value) { - return (a, b) => [value, b, a] -} - -function setRight(value) { - return (a, b) => [a, value, b] -} - -let mp = mutablePair(1, 2); - -mp(getLeft); // 1 -mp(getRight); // 2 -mp(setLeft(5)); // 1 -mp(getLeft); // 5 -``` - -其实可以看到,`mp` 接受的每个函数都是 `(a, b) => [a, b, x]` 这种形式。这样就是能够给传递函数充分地信息来获取结构,然后生成充分足够地信息来更新 `mp` 闭包的内部。如果我们再简化一点,把 `a` 和 `b` 作为一个对象比如 `this` 或者 `self` 来看,`mp` 接受的每个函数变成了 `(self) => result`。其中对 `self` 的改变也包括在内。 - -这其实就是大部分编程语言的面向对象的“方法”的实现机制,都会给这些方法绑定一个 `this` 或者 `self` 变量,让方法内部可控制。 - -你看,就这么简单。 - -### 练习 - -1. 给 mutablePair 实现一个方法,可以交换 pair 左右两边的值。 -2. 有没有更简单地只用函数来实现 mutablePair 的方式? - -## 生成器 - -我们回想一下上一个场景中的计数器,其实是一个非常有用的结构。 - -比如我们希望得到斐波那契数列,但又不希望提前获取这所有内容的时候: - -```js -function fibGenerator() { - let n = 0; - return () => fib(n++) -} -``` - -进一步地我们可以抽象出来这样一个结构,就叫生成器,根据你提供的函数再来求一个结果: - -```js -function generator(fn) { - let n = 0; - return () => fn(n++); -} - -y = generator(fib); - -y(); // 1 -y(); // 1 -y(); // 2 -y(); // 3 -y(); // 5 -y(); // 8 -``` - -然后,不知道你是否发现了这么一个问题,就是,这个生成器只能前向迭代,也就是说,没法倒带的。 - -来我们看一下怎么实现: - -```js -function bidirectionalGenerator(fn) { - let n = 0; - return (direct) => { - let result; - [n, result] = direct(n, fn); - return result; - } -} - -function backward(n, fn) { - return [n-1, fn(n-1)]; -} - -function forward(n, fn) { - return [n+1, fn(n+1)]; -} - -let bg = bidirectionalGenerator(fib); -bg(forward); // 1 -bg(forward); // 2 -bg(forward); // 3 -bg(forward); // 5 -bg(forward); // 8 -bg(backward); // 5 -bg(backward); // 3 -bg(forward); // 5 -bg(forward); // 8 -``` - -思路与 `mutablePair` 类似,我们构造一个特定结构作为这个对象的`this`,所有的操作都是围绕这里所谓的 `this` 来进行操作。 - -### 练习 - -- 给 `generator` 加上范围限制,实现 `boundedGenerator`。 - -## 不可变性 - -前面的生成器中,有一个很关键的问题就是我们**改变了 `generator` 的内部状态**,才得以每次获取都拿到不同的结果。这种情况实际违背了函数其执行的**确定性**。通常我们很少会用这种繁杂的方式实现对象和状态,而是用另外一些更加直观的形式。这个话题我们以后在涉及对象的时候再去讨论。 - -继续看,如果我们不去改变生成器的内部状态,如何实现一个类似的结构。 - -一个生成器会产生一系列的值,形成一个序列结构。我们先定义一个序列结构,可以依照前面的定义来: - -```js -function cons(x, y) { - return (f) => f(x, y); -} - -function head(cons) { - return cons((x, y) => x); -} - -function tail(cons) { - return cons((x, y) => y); -} - -function nil() { - return (f) => f(nil(), nil(), true); -} - -function isNil(s) { - return s((x, y, z) => z === true); -} -``` - -这样一个结构中,序列的每一个节点所对应的下一个都是另外一个节点(`cons` 或者 `nil`),这样连续递归组合起来的结构就是个单链表序列,也是大多数数据结构的基础。 - -一个包含了前 5 个自然数的序列,可以表示成以下形式: - -```js -const seq_first_5_nats = cons(0, cons(1, cons(2, cons(3, (cons(4, nil())))))); - -head(seq_first_5_nats); // 0 -head(tail(tail(tail(seq_first_5_nats)))); // 3 -``` - -而生成器每次除了给我们一个值以外,还会产生一个新的状态(因为这里不能再用可变状态了,所以这个新状态肯定是新生成的)。这样一个新的状态也正是跟原本的生成器一致的结构。这有点类似上面的序列,但由于生成器是会持续构造下去的,我们不得不采用一种方式来阻断这个构造的执行。 - -我们可以先从重复产生同一个状态开始构造这个结构: - -```js -function zeros() { - return cons(0, () => zeros()); // 而不是 cons(0, zeros()) -} - -head(zeros()); // 0 - -head(tail(tail(zeros())())()); // 0 -``` - -这里的 `zeros` 就是一个生成器,在这里 `cons` 所对应的 `tail` 部分不再是一个 `cons` 或者 `nil`,而变成了一个函数。这里比较明显的就是,只要你不去对一个函数尝试进行求值,那么他可以作为一个仅有的对象保持该状态,这样就暂时阻断了生成器的持续构造。而当我们需要生成器继续执行的时候,只要对其进行一次强制求值就好了。 - -可以看到我们每次调用 `tail` 以后都会尝试对它重新求值一次,这正是从一个状态转换到另一个状态的过程。 - -为了方便从生成器中取出对应的值,我们预定义一个方便的功能函数 `take`: - -```js -function take(n, gen) { - if (n == 1) { - return [head(gen)]; - } - return [head(gen), ...take(n - 1, tail(gen)())]; -} - -take(10, zeros()); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -``` - -### 练习 - -1. 实现 `take` 函数的非生成器(即由 `cons`/`nil` 组合而成的链表)版本。 -2. 通常与 `take` 配套的函数时 `drop`,即舍去掉 `take` 所获取的的那部分结果,直接把生成器快进到某个状态。请实现 `drop` 函数。 - -同样的方式你也可以定义连续产生特定数字的序列: - -```js -function repeat(n) { - return cons(n, () => repeat(n)); -} - -take(10, repeat(10)); // [10, 10, 10, 10, 10, 10, 10, 10, 10, 10] -``` - -以及,持续递增的生成器: - -```js -function counter(start = 0) { - return cons(start, () => counter(start + 1)); -} - -take(10, counter(0)); //  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] -``` - -可以看出来,这里的 `counter` 无非是在反复地应用 `+1` 这个操作,来持续的到结果。 - -我们把 `+1` 操作变成函数,也即: - -```js -function inc(x) { - return x + 1; -} - -function counter(start = 0) { - return cons(start, () => counter(inc(start))) -} -``` - -这时候我们可以考虑把这个函数作为参数抽取出来: - -```js -function iterate(fn) { - function generator(start = 0) { - return cons(start, () => generator(fn(start))); - } - - return generator; -} - -take(10, iterate(inc)(0)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - -take(10, iterate(x => x * 2)(1)); // [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] -``` - -`iterate(fn)` 所产生的生成器对应的序列是: - -```js -start, fn(start), fn(fn(start)), fn(fn(fn(start))), ... -``` - -但是这毕竟还是只能单项地产生结果,怎么能像前面一样生成一个 fibonacci 序列呢? - -同样的,我们再来抽取一个变量。 - -每次我们的 `generator` 都只是直接把对应的迭代结果返回,而如果在这个迭代结果上再做一些操作会如何呢? - -```js -function generate(gn, fn) { - function generator(start = 0) { - return cons(gn(start), () => generator(fn(start))); - } - - return generator; -} - -take(10, generate(fib, inc)()); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] -``` - -同样,`generate(gn, fn)` 产生的生成器所对应生成的序列则是: - -```js -gn(start), gn(fn(start)), gn(fn(fn(start))), ... -``` - -请思考前面`generate(fib, inc)`所实现的 fibonacci 序列生成器,获取其前 10 项时所完整展开的结构以及对 `fib` 函数的调用次数。 - -你会发现,`generate` 这种方式对于需要复杂迭代运算的 `gn` 并不友好。 - -## 通用生成器 - -其实根据 fibonacci 序列的递归定义,我们可以写出更合适的生成器。 - -```js -function generateFib() { - function generator(a = 1, b = 1) { - return cons(a, () => generator(b, a + b)); - } - - return generator; -} - -take(10, generateFib()()); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] -``` - -这样得到的就是一个每取一步进行**一步计算**的 fibonacci 序列生成器,比起来前面每一次都要**重新计算**一遍的要更容易很多。 - -其实这样我们仍然能够进行更进一步的抽取: - -可以看到的是,`generateFib` 里的 `generator` 函数的参数是有一个相对固定的**映射关系**的,也就是我们前面推出来的迭代式 fibonacci 的映射关系,而只要把这组**映射关系**表示出来,那就可以进一步的抽象了: - -```js -function generateBy(fn) { - function generator(arg, ...args) { - return cons(arg, () => generator(...fn(arg, ...args))); - } - - return (...initials) => generator(...initials); -} - -take(10, generateBy((a, b) => [b, a + b])(1, 1)); -// [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] -``` - -### 练习 - -- 编写与 `generateFib` 类似的阶乘函数(factorial)所对应的版本,并用 `generateBy` 进行改写。 -- 把前面的 `zeros`、`repeat` 和 `counter` 用 `generateBy` 进行改写 - -### 发生了什么? - -生成器这种构造,最主要的优势是利用函数在构造过程中捕获并保留其上下文的特性,来保持执行状态。这样每次在我们对生成器进行求值的时候,随即创建一个新的保持下一次状态的函数,就能够拿到下一个状态的生成器。 - -而如何生成下一个状态,存在两种场景: - -- 第一,根据**反复应用特定函数**的结果转换得到,即我们的 `generate` 函数 -- 第二,根据前面几个**状态组合**得到,即我们的 `generateBy` 函数 - -这两种场景也自然对应了许多对于实际应用场景中对于数据的复合操作。 - -同样,由我们导出两种类型生成器的过程可以看到,程序的演进过程就是一整个的**抽象**与**组合**的过程。每一次我们得到一个**相对直观的结果**以后,都可以进一步对其再**抽象**,获得一个**更加通用**的方案。 - -反过来,如果我们有一些简单函数(像 `inc`)可以做到**预期**的操作,与其他函数**组合**所构成的结构将可以解决几乎任意复杂度的问题。 - -你所需要知道的与函数相关的一切,绝对不会超出这两个过程的范围。 - -### 练习 - -- 请考虑为这些不可变迭代器加入倒带功能。 - -# 附文:《[map 四种](https://zhuanlan.zhihu.com/p/28471507 "map 四种")》 - -```js -// * common functions -inc = x => x + 1 -double = x => x + x -square = x => x * x - -compose = (f, g) => x => f(g(x)) - -flip = f => (x, y) => f(y, x) - -id = x => x - -force = f => f() - -// * cons, nil and list -cons = (x, y) => f => f(x, y) - -car = cons => cons((x, y) => x) - -cdr = cons => cons((x, y) => y) - -nil = () => nil - -nilP = x => x == nil - -list = (a, ...args) => args.length ? cons(a, list.apply(null, args)) : cons(a, nil) - -// * helper -printList = lst => nilP(lst) || (console.log(car(lst)), printList(cdr(lst))) - -tap = f => (...args) => (console.log(args), f.apply(null, args)) - -// # map the traditional recursive way -map = (fn, lst) => nilP(lst) ? nil : cons(fn(car(lst)), map(fn, cdr(lst))) - -Y = rec => (f => f(f))(f => rec(x => (f(f))(x))) - -reduce = Y(r => c => i => l => nilP(l) ? i : c(car(l), r(c)(i)(cdr(l)))) -// # map by reduce -// map = (fn, l) => reduce((h, t) => cons(fn(h), t))(nil)(l) -filter = p => reduce((h, t) => p(h) ? cons(h, t) : t)(nil) - -foldr = reduce - -// foldl :: (a -> b -> a) -> a -> [b] -> a -foldl = Y(f => c => i => l => nilP(l) ? i : f(c)(c(i, car(l)))(cdr(l))) - -reverse = foldl(flip(cons))(nil) - -len = reduce((h, t) => t + 1)(0) - -// * streams -// type Stream = () -> (a, Stream) -zeroP = (x) => x === 0 - -// zeros = () => cons(0, zeros) -// 0, 0, 0, ... -zeros = Y (z => () => cons(0, z)) - -// o, o, o, ... -cycle = o => Y(c => () => cons(o, c)) - -ones = cycle(1) - -// take_lazy = n => l => n == 0 ? nil : cons(car(l), take_lazy(n-1)(force(cdr(l)))) -take_lazy = Y(t => n => l => zeroP(n) ? nil : cons(car(l), t(n - 1)(force(cdr(l))))) - -// n, n + 1, n + 1 + 1, ... -ints = Y(i => n => cons(n, () => i(n + 1))) - -range = (off, from = 0) => take_lazy(off)(ints(from)) - -// n, f(n), f(f(n)), ... -iterate = f => Y(i => n => cons(n, () => i(f(n)))) -// ints = iterate(inc) - -// g(n), g(f(n)), g(f(f(n))), ... -generate = (g, f) => Y(i => n => cons(g(n), () => i(f(n)))) -// iterates = generate(id) -// ints = generate(id, inc) -// # map by generate -// map = (fn, l) => take_lazy(len(l))(generate(compose(fn, car), cdr)(l)) - -squares = (from = 1) => generate(square, inc)(from) - -stream = Y(s => l => nilP(l) ? nil : cons(car(l), () => s(cdr(l)))) -// # map on streams -map_lazy = (f, s) => generate(compose(f, car), compose(force, cdr))(s) -``` diff --git a/docs/draft/stream.md b/docs/draft/stream.md deleted file mode 100644 index 0a3bf1f..0000000 --- a/docs/draft/stream.md +++ /dev/null @@ -1 +0,0 @@ -# 流 diff --git a/docs/guides/browser-devtools.md b/docs/guides/browser-devtools.md deleted file mode 100644 index fce9f9b..0000000 --- a/docs/guides/browser-devtools.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -description: 学会打开浏览器开发者工具(Inspector/DevTools),并使用 Console 作为 REPL。 ---- - -# 浏览器开发者工具与 Console - -## 你将学到什么 - -- 打开浏览器开发者工具(Inspector/DevTools) -- 进入 Console 并执行 JavaScript -- 用 Console 做最小实验与排错 - -## 第一次打开开发者工具 - -下面以 Chrome / Edge 为例,其他浏览器入口相近: - -1. 在网页空白处右键,选择“检查 / Inspect”。 -2. 或者使用快捷键: - - Windows / Linux:`F12` 或 `Ctrl + Shift + I` - - macOS:`Cmd + Option + I` -3. 打开后,点击顶部的 “Console(控制台)” 标签。 - -如果你只想直接打开 Console: - -- Windows / Linux:`Ctrl + Shift + J` -- macOS:`Cmd + Option + J` - -Firefox 的快捷键略有不同:`Ctrl + Shift + K`(macOS 为 `Cmd + Option + K`)。 - -## 把 Console 当作一个 REPL - -REPL 就是“输入一行,马上看到结果”。你可以从这几步开始: - -1. 输入 `2 + 2`,按 Enter。 -2. 输入 `document.title`,查看当前网页标题。 -3. 输入 `console.log('hello')`,看输出信息。 -4. 需要多行时,用 `Shift + Enter` 换行,最后再按 Enter 执行。 - -Console 里运行的代码只影响当前页面,刷新页面就会恢复原状。 - -## 常见问题 - -- 看不到 Console:先确认 DevTools 已打开,再切到 Console 标签。 -- 输入没有反应:确认光标在 Console 内,按 Enter 执行。 -- 看到红色报错:通常是代码错误,不会“弄坏”你的电脑或浏览器。 - -## 什么时候会用到它 - -- 快速试验一段代码 -- 查看程序输出或报错 -- 理解网页上发生了什么 - -## 官方文档入口 - -- [Chrome DevTools 打开方式](https://developer.chrome.com/docs/devtools/open) -- [Chrome DevTools Console 使用说明](https://developer.chrome.com/docs/devtools/console/) -- [Edge DevTools 快捷键](https://learn.microsoft.com/microsoft-edge/devtools/shortcuts/) -- [Firefox Web Console 使用说明](https://firefox-source-docs.mozilla.org/devtools-user/web_console/) - -(需要更深入时,再去阅读这些官方资料即可。) diff --git a/docs/guides/index.md b/docs/guides/index.md deleted file mode 100644 index b4bf1ab..0000000 --- a/docs/guides/index.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -layout: home -hero: - name: 工具指南 - text: Tooling Guides - tagline: "从零开始搭建 JavaScript 学习环境的工具上手指南" - actions: - - text: 开始阅读 → - link: /guides/browser-devtools - theme: brand -description: 从零开始搭建 JavaScript 学习环境的工具上手指南。 ---- - -本书会在浏览器里做小实验,也会在本地写代码。下面这几篇指南专为零基础读者准备,按顺序完成即可: - -1. [浏览器开发者工具与 Console](./browser-devtools.md) -2. [安装与上手 VS Code](./vscode.md) -3. [安装 Node.js 并在 VS Code 运行 JavaScript](./nodejs.md) -4. [TypeScript 入门:安装、编译与运行](./typescript.md) - -每一篇指南都包含: - -- 这项工具是做什么的 -- 最小可运行的上手步骤 -- 常见问题与自查办法 -- 官方文档入口(需要深入时再看) - -如果你已经用过其中某些工具,可以跳过对应指南,直接进入章节。 diff --git a/docs/guides/nodejs.md b/docs/guides/nodejs.md deleted file mode 100644 index fd26371..0000000 --- a/docs/guides/nodejs.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -description: 安装 Node.js,并在本地运行 JavaScript。 ---- - -# 安装 Node.js 并在 VS Code 运行 JavaScript - -## 你将学到什么 - -- 安装 Node.js(推荐 LTS 版本) -- 验证 Node.js 与 npm 是否可用 -- 在 VS Code 终端运行 JavaScript 文件 - -## 安装 Node.js - -1. 打开 Node.js 官网下载页,选择 LTS 版本。 -2. 按提示完成安装。 -3. 安装后先关闭并重新打开终端。 - -## 验证是否安装成功 - -在终端里分别输入: - -```bash -node -v -npm -v -``` - -如果能看到版本号,说明安装完成。 - -## 运行你的第一个本地脚本 - -1. 用 VS Code 打开一个文件夹。 -2. 新建 `hello.js`,写入: - -```javascript -console.log("hello from node"); -``` - -3. 打开 VS Code 内置终端。 -4. 在终端中运行: - -```bash -node hello.js -``` - -看到输出就成功了。 - -## 常见问题 - -- `node` 命令不存在:关闭并重开终端,或重新安装 Node.js。 -- 没看到输出:确认终端所在目录与 `hello.js` 在同一文件夹。 - -## 官方文档入口 - -- [Node.js 下载页面](https://nodejs.org/en/download/) -- [Node.js 官方使用文档](https://nodejs.org/en/learn/) -- [VS Code 的 Node.js 教程](https://code.visualstudio.com/docs/nodejs/nodejs-tutorial) - -(需要深入时再查即可。) diff --git a/docs/guides/typescript.md b/docs/guides/typescript.md deleted file mode 100644 index f983850..0000000 --- a/docs/guides/typescript.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -description: 安装 TypeScript,完成第一次编译与运行。 ---- - -# TypeScript 入门:安装、编译与运行 - -## 你将学到什么 - -- 安装 TypeScript -- 编写第一个 `.ts` 文件 -- 编译成 `.js` 并运行 - -## 前置条件 - -- 已安装 VS Code(见 [安装与上手 VS Code](./vscode.md)) -- 已安装 Node.js(见 [安装 Node.js 并在 VS Code 运行 JavaScript](./nodejs.md)) - -## 安装 TypeScript - -首次体验可以用“全局安装”,更简单;长期使用建议“项目内安装”。 - -**方式 A:全局安装(上手更快)** - -```bash -npm install -g typescript -``` - -**方式 B:项目内安装(更推荐)** - -```bash -npm init -y -npm install -D typescript -``` - -## 写下第一个 TypeScript 文件 - -新建 `hello.ts`,写入: - -```typescript -const message: string = "hello from ts"; -console.log(message); -``` - -## 编译并运行 - -全局安装时,直接运行: - -```bash -tsc hello.ts -``` - -项目内安装时,使用: - -```bash -npx tsc hello.ts -``` - -编译后会生成 `hello.js`,再运行: - -```bash -node hello.js -``` - -## 常见问题 - -- `tsc` 命令找不到:先用 `npx tsc`,或确认是否全局安装成功。 -- 没生成 `hello.js`:确认当前目录是否有写入权限。 - -## 官方文档入口 - -- [TypeScript 下载与安装](https://www.typescriptlang.org/download/) -- [TypeScript Handbook(从零开始)](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch) -- [VS Code 的 TypeScript 编辑支持说明](https://code.visualstudio.com/docs/typescript/typescript-editing) - -(需要深入时再查即可。) diff --git a/docs/guides/vscode.md b/docs/guides/vscode.md deleted file mode 100644 index ce04bef..0000000 --- a/docs/guides/vscode.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -description: 安装并上手 Visual Studio Code(VS Code),完成第一次本地编辑。 ---- - -# 安装与上手 VS Code - -## 你将学到什么 - -- 下载与安装 VS Code -- 打开文件夹与创建文件 -- 使用内置终端(Terminal) - -## 安装 VS Code - -1. 打开 VS Code 官网下载页面,选择与你系统匹配的安装包。 -2. 用默认选项完成安装即可。 -3. 安装完成后,第一次打开会看到欢迎页,先不用配置太多。 - -## 第一次打开一个“项目文件夹” - -编程时我们通常把相关文件放在同一个文件夹里。可以这样做: - -1. 在桌面或任意位置新建一个文件夹,例如 `js-playground`。 -2. 打开 VS Code,选择“打开文件夹 / Open Folder”。 -3. 选择你刚刚创建的文件夹。 -4. 在左侧文件树里,新建文件 `hello.js`。 - -## 写下第一行代码 - -在 `hello.js` 里输入下面这一行: - -```javascript -console.log("hello"); -``` - -先保存文件,后面我们会在 Node.js 指南里运行它。 - -## 使用内置终端 - -VS Code 自带终端,可以直接执行命令: - -1. 打开菜单 “Terminal / 终端”。 -2. 选择 “New Terminal / 新建终端”。 -3. 终端会出现在窗口底部,后面运行 Node.js 和 TypeScript 都会用到它。 - -## 常见问题 - -- 找不到文件:确认你是“打开文件夹”,而不是“打开文件”。 -- 看不到终端:用菜单或快捷键重新打开终端。 - -## 官方文档入口 - -- [VS Code 下载页面](https://code.visualstudio.com/download) -- [VS Code 安装指南](https://code.visualstudio.com/docs/setup/setup-overview) -- [VS Code 内置终端说明](https://code.visualstudio.com/docs/terminal/getting-started) - -(遇到具体问题时再查这些资料即可。) diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 8795e51..0000000 --- a/docs/index.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: home -title: 入门指南 -hero: - name: 入门指南 - text: Introduction Guides - tagline: "给初学者的上手之路" - image: - src: /hero.png - alt: Introduction to Programming - actions: - - text: 计算机编程入门指南 → - link: /chapters/ - theme: brand - - text: Agentic AI 入门指南 → - link: /agentic-ai/ - theme: brand -# features: -# - icon: 🛠️ -# title: Simple and minimal, always -# details: Lorem ipsum... -# - icon: -# src: /cool-feature-icon.svg -# title: Another cool feature -# details: Lorem ipsum... -# - icon: -# dark: /dark-feature-icon.svg -# light: /light-feature-icon.svg -# title: Another cool feature -# details: Lorem ipsum... ---- diff --git a/docs/public/agentic-ai.png b/docs/public/agentic-ai.png deleted file mode 100644 index eda11ba..0000000 Binary files a/docs/public/agentic-ai.png and /dev/null differ diff --git a/docs/public/hero.png b/docs/public/hero.png deleted file mode 100644 index 5f1ecd9..0000000 Binary files a/docs/public/hero.png and /dev/null differ diff --git a/docs/reference/api-interaction.md b/docs/reference/api-interaction.md deleted file mode 100644 index 0cd85b6..0000000 --- a/docs/reference/api-interaction.md +++ /dev/null @@ -1,536 +0,0 @@ -# API 调用与 Function Calling - -本附录讲解如何用代码直接调用 AI API。当你想让 AI 融入自动化流程、构建 Agent、或做 AI 驱动的应用时,聊天界面就不够用了,你需要直接和 API 打交道。 - -这一章用 JavaScript 写例子。你不需要是 JavaScript 专家,能看懂基本语法就行。 - -## 准备工作 - -使用 OpenAI 的 API 之前,你需要: - -1. 注册账号并获取 API Key。 -2. 安装官方 SDK(推荐),或者直接用 fetch 调用。 - -```bash -npm install openai -``` - -API Key 不要硬编码在代码里,用环境变量管理: - -```bash -export OPENAI_API_KEY="sk-xxxx" -``` - -国内模型(豆包、Kimi、DeepSeek、Qwen)的 API 结构和 OpenAI 兼容,换一个 baseURL 就行。 - -## 方式一:用 OpenAI SDK - -SDK 封装了 HTTP 请求、错误处理、流式输出等细节,推荐在正式项目中使用。 - -### 基本对话 - -```javascript -import OpenAI from "openai"; - -const client = new OpenAI(); - -const response = await client.chat.completions.create({ - model: "gpt-4o", - messages: [ - { role: "system", content: "你是一个有用的助手。" }, - { role: "user", content: "用一句话解释什么是 API。" }, - ], -}); - -console.log(response.choices[0].message.content); -``` - -SDK 会自动处理认证头、JSON 序列化、错误重试等。你只需要关注业务逻辑。 - -### 流式输出 - -```javascript -const stream = await client.chat.completions.create({ - model: "gpt-4o", - messages: [{ role: "user", content: "写一首短诗。" }], - stream: true, -}); - -for await (const chunk of stream) { - const content = chunk.choices[0]?.delta?.content; - if (content) process.stdout.write(content); -} -``` - -流式输出让前端可以逐字显示结果,ChatGPT 的“打字机”效果就是这么实现的。 - -### 传入图片 - -支持视觉输入的模型可以同时接收文字和图片。消息里的 `content` 不再只是字符串,而是一个内容数组:一段文字说明,加上一张或多张图片。 - -```javascript -const response = await client.chat.completions.create({ - model: "gpt-4o", - messages: [ - { - role: "user", - content: [ - { - type: "text", - text: "请描述这张图,并指出里面最重要的三个信息。", - }, - { - type: "image_url", - image_url: { - url: "https://example.com/dashboard.png", - }, - }, - ], - }, - ], -}); - -console.log(response.choices[0].message.content); -``` - -如果图片在本地,可以把它转成 Base64,再作为 Data URL 传入: - -```javascript -import { readFile } from "node:fs/promises"; - -const imageBuffer = await readFile("screenshot.png"); -const base64Image = imageBuffer.toString("base64"); - -const response = await client.chat.completions.create({ - model: "gpt-4o", - messages: [ - { - role: "user", - content: [ - { type: "text", text: "这张截图里的报错可能是什么原因?" }, - { - type: "image_url", - image_url: { - url: `data:image/png;base64,${base64Image}`, - }, - }, - ], - }, - ], -}); - -console.log(response.choices[0].message.content); -``` - -这种调用适合处理截图分析、图表解读、设计稿反馈、资料整理等任务。它和图像生成不同:这里不是让模型画图,而是让模型读取图片并回答问题。 - -### 使用国内模型 - -国内模型大多兼容 OpenAI 格式,只需要换 baseURL: - -```javascript -// DeepSeek -const deepseek = new OpenAI({ - baseURL: "https://api.deepseek.com", - apiKey: process.env.DEEPSEEK_API_KEY, -}); - -// 通义千问 -const qwen = new OpenAI({ - baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", - apiKey: process.env.QWEN_API_KEY, -}); - -// 用法和 OpenAI 完全一样 -const response = await deepseek.chat.completions.create({ - model: "deepseek-chat", - messages: [{ role: "user", content: "你好" }], -}); -``` - -## 方式二:用 fetch 直接调用 - -如果你不想装 SDK,或者想理解底层原理,可以直接发 HTTP 请求: - -```javascript -const response = await fetch("https://api.openai.com/v1/chat/completions", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, - }, - body: JSON.stringify({ - model: "gpt-4o", - messages: [{ role: "user", content: "用一句话解释什么是 API。" }], - }), -}); - -const data = await response.json(); -console.log(data.choices[0].message.content); -``` - -SDK 和 fetch 的关系:SDK 是对 fetch 的封装。理解 fetch 能帮你理解 SDK 在做什么,但日常开发用 SDK 更方便。 - -## 推理模式 - -推理模型(如 o1、o3)在回答之前会进行内部思考。这个过程会消耗额外的 token,但能显著提升复杂任务的表现。 - -### 基本用法 - -```javascript -const response = await client.chat.completions.create({ - model: "o3", - messages: [ - { role: "user", content: "分析这段代码的时间复杂度,并给出优化方案" } - ], -}); - -console.log(response.choices[0].message.content); -``` - -推理模型会自动进行内部推理,你不需要额外配置。 - -### 控制推理深度 - -通过 `reasoning_effort` 参数,你可以控制模型推理的深度: - -```javascript -const response = await client.chat.completions.create({ - model: "o3", - reasoning_effort: "medium", // low / medium / high - messages: [ - { role: "user", content: "解释快速排序的时间复杂度" } - ], -}); -``` - -- `low`:快速推理,适合简单问题。 -- `medium`:平衡推理深度和速度(默认)。 -- `high`:深度推理,适合复杂问题,但消耗更多 token。 - -> 提醒:推理模型的 token 价格通常更高。对于简单任务,用普通模型(如 gpt-4o)更划算。 - -## 生成参数 - -模型生成文本时,有几个参数可以控制输出的特性。这些参数不影响模型"理解"问题的能力,但会影响它"怎么回答"。 - -### temperature - -`temperature` 控制输出的随机性,取值范围通常是 0 到 2。 - -- **低 temperature(如 0)**:输出更确定、更集中。每次问同样的问题,得到的回答会很相似。适合需要一致性和准确性的场景,比如代码生成、数据提取。 -- **高 temperature(如 0.8)**:输出更随机、更多样。适合创意写作、头脑风暴等需要多样性的场景。 - -```javascript -// 低 temperature:确定性输出 -const response = await client.chat.completions.create({ - model: "gpt-4o", - temperature: 0, - messages: [{ role: "user", content: "写一个计算斐波那契数列的函数" }], -}); - -// 高 temperature:创意输出 -const response = await client.chat.completions.create({ - model: "gpt-4o", - temperature: 0.8, - messages: [{ role: "user", content: "写一首关于编程的诗" }], -}); -``` - -### top_p - -`top_p`(nucleus sampling)是另一种控制随机性的方式。它从概率最高的词开始累加,直到累积概率达到 `top_p`,然后只从这些词中选择。 - -- `top_p = 1`:考虑所有可能的词(默认行为)。 -- `top_p = 0.9`:只从累积概率前 90% 的词中选择,过滤掉最不可能的词。 - -```javascript -const response = await client.chat.completions.create({ - model: "gpt-4o", - top_p: 0.9, - messages: [{ role: "user", content: "解释量子计算" }], -}); -``` - -> 提醒:通常不需要同时调整 `temperature` 和 `top_p`。选择其中一个调整即可。大多数情况下,使用默认值就好。 - -### 什么时候需要调整这些参数? - -大多数应用场景下,默认参数就够用了。以下是一些可能需要调整的场景: - -- **需要高度一致的输出**:把 `temperature` 设为 0。适合代码生成、格式化输出、数据提取。 -- **需要创意多样性**:适当提高 `temperature`(如 0.7-0.9)。适合创意写作、生成多种方案。 -- **需要过滤低概率输出**:降低 `top_p`(如 0.9)。适合需要输出更"安全"的场景。 - -> 提醒:Agent 框架通常会帮你管理这些参数,你不需要手动设置。但理解这些参数有助于你调试 Agent 的行为——如果 Agent 的输出太随机或太死板,你可能需要调整这些参数。 - -## Token 管理与成本 - -API 调用按 Token 计费。Token 是模型处理文本的最小单位,英文大约 1 个单词 ≈ 1 个 Token,中文大约 1 个字 ≈ 1.5-2 个 Token。 - -控制成本的方法: - -- **控制输入长度**:只给必要的上下文,不要把整本书塞进去。 -- **控制输出长度**:用 max_tokens 参数限制回复长度。 -- **监控消耗**:API 返回里包含 usage 字段。 - -```javascript -const response = await client.chat.completions.create({ - model: "gpt-4o", - max_tokens: 500, // 限制输出长度 - messages: [{ role: "user", content: "简要介绍 JavaScript。" }], -}); - -console.log(`输入: ${response.usage.prompt_tokens} tokens`); -console.log(`输出: ${response.usage.completion_tokens} tokens`); -console.log(`总计: ${response.usage.total_tokens} tokens`); -``` - -不同模型价格差异很大。简单任务用便宜模型(如 gpt-4o-mini),复杂任务再用贵的模型。 - -## Embedding API:文本转向量 - -Embedding 把文本转换成一组数字(向量),用于语义搜索、聚类、相似度比较。这是 RAG 的基础。 - -```javascript -const embedding = await client.embeddings.create({ - model: "text-embedding-3-small", - input: "什么是机器学习?", -}); - -console.log(embedding.data[0].embedding); // 一个 1536 维的向量 -console.log(embedding.usage.total_tokens); // 消耗的 Token 数 -``` - -批量处理多段文本: - -```javascript -const texts = [ - "什么是机器学习?", - "深度学习和机器学习有什么区别?", - "今天天气真好。", -]; - -const embeddings = await client.embeddings.create({ - model: "text-embedding-3-small", - input: texts, -}); - -// 返回的向量数组,可以用于相似度计算 -for (const item of embeddings.data) { - console.log(`文本 ${item.index}: ${item.embedding.slice(0, 5)}...`); -} -``` - -计算两个向量的余弦相似度: - -```javascript -function cosineSimilarity(a, b) { - let dot = 0, normA = 0, normB = 0; - for (let i = 0; i < a.length; i++) { - dot += a[i] * b[i]; - normA += a[i] * a[i]; - normB += b[i] * b[i]; - } - return dot / (Math.sqrt(normA) * Math.sqrt(normB)); -} - -const emb1 = embeddings.data[0].embedding; -const emb2 = embeddings.data[1].embedding; -const emb3 = embeddings.data[2].embedding; - -console.log(cosineSimilarity(emb1, emb2)); // 接近 1(语义相似) -console.log(cosineSimilarity(emb1, emb3)); // 接近 0(语义无关) -``` - -“什么是机器学习”和“深度学习和机器学习有什么区别”的相似度会很高,因为它们讨论的是相关话题。而“今天天气真好”和前两个的相似度会很低。 - -## Image API:图像生成 - -用代码调用图像生成 API: - -```javascript -const image = await client.images.generate({ - model: "gpt-image-1", - prompt: "一只橘猫坐在书桌上,旁边有一杯咖啡,水彩画风格", - size: "1024x1024", -}); - -console.log(image.data[0].url); // 生成图片的 URL -``` - -图片通常以 URL 返回,你可以下载保存: - -```javascript -const imageUrl = image.data[0].url; -const buffer = await fetch(imageUrl).then((r) => r.arrayBuffer()); -await fs.writeFile("cat.png", Buffer.from(buffer)); -``` - -用参考图进行编辑(Image Edit): - -```javascript -const edit = await client.images.edit({ - model: "gpt-image-1", - image: fs.createReadStream("original.png"), - prompt: "把背景换成海滩", -}); -``` - -> 提醒:图像生成 API 的成本比文本 API 高得多。生成一张图的费用可能是几百次文本对话的总和。开发调试时用小尺寸,确认效果后再用大尺寸。 - -## Audio API:语音识别 - -语音识别(ASR)的输入是音频文件,输出是文字。常见用途包括会议纪要、播客转写、视频字幕、语音笔记整理。 - -用 Whisper 转写音频: - -```javascript -import fs from "node:fs"; - -const transcript = await client.audio.transcriptions.create({ - model: "whisper-1", - file: fs.createReadStream("meeting.mp3"), - language: "zh", -}); - -console.log(transcript.text); -``` - -如果你想直接得到字幕格式,可以指定 `response_format`: - -```javascript -const subtitles = await client.audio.transcriptions.create({ - model: "whisper-1", - file: fs.createReadStream("meeting.mp3"), - language: "zh", - response_format: "srt", -}); - -console.log(subtitles); -``` - -语音识别的结果通常还需要后处理。比如分段、加标点、区分说话人、提取行动项,这些步骤可以继续交给文本模型完成。 - -## Function Calling:让模型调用函数 - -Function Calling 让模型能调用你定义的函数。这是 Agent “使用工具”的底层实现。 - -### 第一步:定义函数的 Schema - -```javascript -const tools = [ - { - type: "function", - function: { - name: "get_weather", - description: "获取指定城市的天气信息", - parameters: { - type: "object", - properties: { - city: { type: "string", description: "城市名称,如'北京'" }, - }, - required: ["city"], - }, - }, - }, -]; -``` - -### 第二步:发送请求,处理函数调用 - -```javascript -const response = await client.chat.completions.create({ - model: "gpt-4o", - messages: [{ role: "user", content: "北京今天天气怎么样?" }], - tools, -}); - -const message = response.choices[0].message; - -if (message.tool_calls) { - const toolCall = message.tool_calls[0]; - const args = JSON.parse(toolCall.function.arguments); - - // 执行你自己的函数 - const result = await getWeather(args.city); - - // 把结果发回给模型 - const finalResponse = await client.chat.completions.create({ - model: "gpt-4o", - messages: [ - { role: "user", content: "北京今天天气怎么样?" }, - message, - { role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(result) }, - ], - tools, - }); - - console.log(finalResponse.choices[0].message.content); -} -``` - -整个流程:用户提问 → 模型决定调用函数 → 你执行函数 → 把结果返回 → 模型生成最终回答。 - -这就是 Agent “使用工具”的底层实现。当你理解了这个流程,再回头看 Agent 的 Tool Use 和 Loop,就会明白它们是怎么工作的。 - -## 错误处理 - -API 调用可能失败。SDK 内置了一些重试机制,但你仍然需要处理异常: - -```javascript -import OpenAI from "openai"; - -const client = new OpenAI({ maxRetries: 3 }); - -try { - const response = await client.chat.completions.create({ - model: "gpt-4o", - messages: [{ role: "user", content: "你好" }], - }); - console.log(response.choices[0].message.content); -} catch (error) { - if (error.status === 429) { - console.error("请求太频繁,稍后再试"); - } else if (error.status === 401) { - console.error("API Key 无效"); - } else { - console.error(`请求失败: ${error.message}`); - } -} -``` - -常见错误: - -- **429 Too Many Requests**:被限流了,SDK 会自动重试(指数退避)。 -- **401 Unauthorized**:API Key 无效或过期。 -- **500 Server Error**:服务端出错,通常重试就好。 - -## 小结 - -- SDK 封装了底层细节,推荐在正式项目中使用。fetch 调用帮你理解底层原理。 -- 国内模型大多兼容 OpenAI 格式,换 baseURL 就行。 -- 推理模型会自动进行内部思考,`reasoning_effort` 参数控制推理深度。 -- 流式传输让前端可以逐字显示结果,改善用户体验。 -- `temperature` 和 `top_p` 控制输出的随机性,大多数情况下使用默认值即可。 -- Token 决定了成本,控制输入输出长度是基本功。 -- Embedding API 把文本变成向量,是 RAG 和语义搜索的基础。 -- 支持视觉输入的模型可以通过 Chat Completions 接收图片,用于截图分析、图表解读和设计反馈。 -- Image API 调用图像生成,成本比文本 API 高得多。 -- Audio API 可以用 Whisper 把语音转成文字,再交给文本模型做整理和总结。 -- Function Calling 是 Agent “使用工具”的底层实现。 - -## 练习 - -1. 用 OpenAI SDK 写一个最简单的调用:发一条消息,打印回复。 -2. 用 Embedding API 把三段文本转成向量,计算它们两两之间的余弦相似度。 -3. 用 Chat Completions 传入一张截图,让模型解释截图里的主要信息。 -4. 用 Image API 生成一张图片,保存到本地。 -5. 用 Whisper 转写一段短音频,再让文本模型总结成三条要点。 -6. 实现一个完整的 Function Calling 流程:定义一个“查询股票价格”的函数,让模型在用户问到股票时自动调用它。 - -## 延伸阅读 - -- [RAG 技术详解:向量检索与混合搜索](../reference/rag-deep-dive.md) -- [同步与异步 Sync & Async](../reference/glossary/sync-async.md) -- [编码与字符集 Encoding & Charset](../reference/glossary/encoding-and-charset.md) diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md deleted file mode 100644 index 204b900..0000000 --- a/docs/reference/glossary.md +++ /dev/null @@ -1,80 +0,0 @@ -# 概念解释 Glossary - -## A - -- [安全](./glossary/safety-and-security.md) - -## B - -- [阻塞与非阻塞 Blocking & Non-blocking](./glossary/blocking-nonblocking.md) - -## C - -- [缓存 Cache](./glossary/cache.md) -- [调用栈 Call Stack](./glossary/call-stack.md) -- [复杂度 Complexity](./glossary/complexity.md) -- [临界区 Critical Section](./glossary/race-condition.md) -- [测试](./glossary/testing.md) - -## D - -- [Debug](./glossary/debug.md) -- [死锁 Deadlock](./glossary/deadlock.md) - -## E - -- [编码与字符集 Encoding & Charset](./glossary/encoding-and-charset.md) -- [事件循环 Event Loop](./glossary/event-loop.md) - -## F - -- [浮点数 Floating Point](./glossary/floating-point.md) - -## G - -- [垃圾回收 Garbage Collection](./glossary/garbage-collection.md) - -## I - -- [不可变性 Immutability](./glossary/immutability.md) - -## L - -- [词法作用域 Lexical Scope](./glossary/lexical-scope.md) - -## M - -- [内存泄漏 Memory Leak](./glossary/garbage-collection.md) - -## P - -- [进程与线程 Process & Thread](./glossary/process-and-thread.md) -- [原型链 Prototype Chain](./glossary/prototype-chain.md) - -## R - -- [竞态条件 Race Condition](./glossary/race-condition.md) -- [递归 Recursion](./glossary/recursion.md) -- [引用计数 Reference Counting](./glossary/garbage-collection.md) - -## S - -- [Safety, Security](./glossary/safety-and-security.md) -- [栈与堆 Stack & Heap](./glossary/stack-and-heap.md) -- [同步与异步 Sync & Async](./glossary/sync-async.md) -- [序列化 Serialization](./glossary/serialization.md) - -## T - -- [Tail Call Optimization 尾调用优化](./glossary/tail-call-optimization.md) -- [Testing](./glossary/testing.md) -- [Transaction 事务](./glossary/transaction.md) -- [调试](./glossary/debug.md) - -## U - -- [Unicode / UTF-8](./glossary/unicode-utf8.md) - -## V - -- [值语义与引用语义 Value vs. Reference](./glossary/value-reference.md) diff --git a/docs/reference/glossary/blocking-nonblocking.md b/docs/reference/glossary/blocking-nonblocking.md deleted file mode 100644 index cfb7dab..0000000 --- a/docs/reference/glossary/blocking-nonblocking.md +++ /dev/null @@ -1,35 +0,0 @@ -# 阻塞与非阻塞(Blocking & Non-blocking) - -阻塞与非阻塞描述的是:当你“等待结果”时,当前线程是否还能继续做别的事。 - -## 阻塞(Blocking) - -阻塞意味着“停下来等结果”。 - -比如同步读取文件: - -``` -读文件 -> 卡住 -> 文件读完 -> 继续执行 -``` - -## 非阻塞(Non-blocking) - -非阻塞意味着“先发起请求,然后继续做别的事”。 - -``` -发起读文件 -> 继续执行 -> 文件读完后回调 -``` - -## 与同步/异步的关系 - -* 同步/异步:什么时候拿到结果 -* 阻塞/非阻塞:等待时能不能干别的事 - -例如: - -* **同步 + 阻塞**:最常见的传统模式。 -* **异步 + 非阻塞**:事件循环常见模式。 - -## 进一步阅读 - -理解阻塞/非阻塞有助于你理解性能与响应性。 diff --git a/docs/reference/glossary/cache.md b/docs/reference/glossary/cache.md deleted file mode 100644 index 6d0efb9..0000000 --- a/docs/reference/glossary/cache.md +++ /dev/null @@ -1,24 +0,0 @@ -# 缓存(Cache) - -缓存是一种“用空间换时间”的技术:把常用的数据先存起来,下次更快访问。 - -## 命中与未命中 - -* **命中(Hit)**:缓存里有,直接用。 -* **未命中(Miss)**:缓存里没有,需要从原始来源获取。 - -## 常见场景 - -* 浏览器缓存网页资源 -* 数据库查询结果缓存 -* 函数计算结果缓存(Memoization) - -## 代价与风险 - -* 缓存过期导致数据不一致 -* 缓存占用内存 -* 需要淘汰策略(如 LRU) - -## 进一步阅读 - -缓存很常见,但“缓存一致性”也是常见问题来源。 diff --git a/docs/reference/glossary/call-stack.md b/docs/reference/glossary/call-stack.md deleted file mode 100644 index 1dfd6cb..0000000 --- a/docs/reference/glossary/call-stack.md +++ /dev/null @@ -1,25 +0,0 @@ -# 调用栈(Call Stack) - -调用栈用来记录“当前正在执行的函数链条”。每调用一次函数,就压入一层;函数返回就弹出一层。 - -## 为什么重要 - -* 帮你理解递归。 -* 解释“栈溢出”错误。 -* 解释错误堆栈(stack trace)。 - -## 一个简单例子 - -```javascript -function a() { b(); } -function b() { c(); } -function c() { return; } - -a(); -``` - -调用顺序是:a -> b -> c,然后依次返回。 - -## 进一步阅读 - -调用栈和事件循环结合起来,可以帮助你理解“同步/异步的执行顺序”。 diff --git a/docs/reference/glossary/complexity.md b/docs/reference/glossary/complexity.md deleted file mode 100644 index 0774445..0000000 --- a/docs/reference/glossary/complexity.md +++ /dev/null @@ -1,29 +0,0 @@ -# 复杂度(Complexity) - -复杂度用来描述算法在输入规模增大时,时间或空间的增长趋势。 - -## 大 O 记号(Big O) - -常见的几个量级: - -* O(1):常数时间 -* O(log n):对数时间 -* O(n):线性时间 -* O(n log n):常见排序复杂度 -* O(n^2):二次时间 - -## 一个直观例子 - -在数组中查找一个元素: - -* 从头到尾扫描:O(n) -* 如果数组有序,可以用二分查找:O(log n) - -## 为什么需要它 - -* 帮你判断算法在大输入下会不会变慢。 -* 让你理解“为什么有些方法不可行”。 - -## 进一步阅读 - -复杂度是算法入门最重要的概念之一。 diff --git a/docs/reference/glossary/deadlock.md b/docs/reference/glossary/deadlock.md deleted file mode 100644 index 0a78463..0000000 --- a/docs/reference/glossary/deadlock.md +++ /dev/null @@ -1,33 +0,0 @@ -# 死锁(Deadlock) - -死锁是指多个任务互相等待对方释放资源,导致所有任务都无法继续。 - -## 经典场景 - -任务 A 占有资源 1 等待资源 2,任务 B 占有资源 2 等待资源 1: - -``` -A: 持有 1,等待 2 -B: 持有 2,等待 1 -``` - -双方都在等待,系统卡住。 - -## 产生死锁的四个条件(经典说法) - -1. **互斥**:资源同一时间只能被一个任务占用。 -2. **占有并等待**:任务占有资源,同时等待其他资源。 -3. **不可剥夺**:资源不能被强行夺走。 -4. **循环等待**:任务之间形成等待环。 - -四个条件同时满足时容易发生死锁。 - -## 如何避免(概念层面) - -* 统一资源获取顺序(打破循环等待)。 -* 限制一次持有多个资源。 -* 增加超时机制,放弃等待。 - -## 与 JavaScript 的关系 - -JavaScript 主线程通常不直接写锁,但在多线程(Worker/后端)或数据库事务中非常常见。 diff --git a/docs/reference/glossary/debug.md b/docs/reference/glossary/debug.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/reference/glossary/encoding-and-charset.md b/docs/reference/glossary/encoding-and-charset.md deleted file mode 100644 index aa9c184..0000000 --- a/docs/reference/glossary/encoding-and-charset.md +++ /dev/null @@ -1,48 +0,0 @@ -# 编码与字符集(Encoding & Charset) - -编码(Encoding)和字符集(Charset)经常被混用,但它们不是一回事。 - -## 字符集(Charset) - -字符集是“有哪些字符”的清单,以及这些字符对应的编号(码点)。 - -比如 Unicode 字符集里: - -* `A` 的码点是 U+0041 -* `中` 的码点是 U+4E2D - -## 编码(Encoding) - -编码是“如何把码点变成字节序列”。 - -例如 UTF-8 是一种编码方式: - -* `A` -> 1 字节(0x41) -* `中` -> 3 字节(0xE4 0xB8 0xAD) - -不同编码方式决定了“同一个字符”如何变成不同的字节序列。 - -## 乱码的本质 - -乱码通常是“用错了解码方式”。 - -例如: - -* 文件是 UTF-8 编码 -* 你用 GBK 去解码 - -结果就会出现“看起来像噪音”的字符。 - -## 常见组合 - -* **Unicode + UTF-8**:现代 Web 的事实标准。 -* **Unicode + UTF-16**:某些系统内部表示常见。 - -## 简单建议 - -* 文件统一使用 UTF-8。 -* 工具显示乱码时,尝试切换编码。 - -## 进一步阅读 - -理解编码与字符集的区别,是理解“文本为何会坏掉”的基础。 diff --git a/docs/reference/glossary/event-loop.md b/docs/reference/glossary/event-loop.md deleted file mode 100644 index 9635eff..0000000 --- a/docs/reference/glossary/event-loop.md +++ /dev/null @@ -1,41 +0,0 @@ -# 事件循环(Event Loop) - -事件循环是 JavaScript 在浏览器和 Node.js 中执行异步任务的核心机制。它决定了“什么时候执行哪段代码”。 - -## 基本流程(极简版) - -1. 执行主线程上的同步代码。 -2. 异步任务完成后把回调放进队列。 -3. 事件循环不断从队列取任务执行。 - -## 任务队列与优先级 - -在多数运行时中,任务大致分成两类: - -* **宏任务**(Macro Task):如 `setTimeout`、I/O。 -* **微任务**(Micro Task):如 `Promise.then`、`queueMicrotask`。 - -微任务通常在一次宏任务结束后立即执行,因此“优先级更高”。 - -```javascript -console.log('A'); -setTimeout(() => console.log('B')); -Promise.resolve().then(() => console.log('C')); -console.log('D'); -// 输出顺序:A D C B -``` - -## 为什么单线程也会有“并发感” - -事件循环会在同步任务和异步任务之间来回切换,所以看起来像“同时发生”。 - -这也是共享状态在异步场景下容易出错的原因:任务交错执行,顺序不可预测。 - -## 常见误解 - -* **“单线程就没有并发问题”**:错。异步切换同样会造成竞态。 -* **“`await` 会阻塞线程”**:错。`await` 只是把后续逻辑放进微任务队列。 - -## 进一步阅读 - -如果你想理解更完整的执行流程,可以结合“竞态条件”“临界区”等概念一起阅读。 diff --git a/docs/reference/glossary/floating-point.md b/docs/reference/glossary/floating-point.md deleted file mode 100644 index 9061bb2..0000000 --- a/docs/reference/glossary/floating-point.md +++ /dev/null @@ -1,39 +0,0 @@ -# 浮点数(Floating Point) - -浮点数是计算机表示小数的一种方式。它使用“符号位 + 指数 + 尾数”的结构来近似表示实数。 - -## 为什么会有误差 - -很多十进制小数在二进制里是无限循环的,例如: - -``` -0.1 -> 0.0001100110011... (二进制循环) -``` - -因此在计算机里只能近似存储。 - -```javascript -0.1 + 0.2; // 0.30000000000000004 -``` - -## IEEE 754(简述) - -现代计算机大多使用 IEEE 754 标准: - -* **单精度**:32 位 -* **双精度**:64 位(JS 的 Number) - -## 实用建议 - -* 不要用 `==` 直接比较浮点数。 -* 用“误差范围”判断: - -```javascript -function nearlyEqual(a, b, eps = 1e-10) { - return Math.abs(a - b) < eps; -} -``` - -## 进一步阅读 - -浮点数问题常见于金融计算、科学计算。遇到精度问题时,请优先用整数或专门的数值库。 diff --git a/docs/reference/glossary/garbage-collection.md b/docs/reference/glossary/garbage-collection.md deleted file mode 100644 index 016ac64..0000000 --- a/docs/reference/glossary/garbage-collection.md +++ /dev/null @@ -1,52 +0,0 @@ -# 垃圾回收(Garbage Collection) - -垃圾回收(GC)是自动内存管理机制,它会回收“已经不可能再被使用”的对象。 - -## 引用计数(Reference Counting) - -最简单的做法是给每个对象一个计数: - -* 有一个新引用指向它,计数 +1。 -* 引用消失,计数 -1。 -* 计数为 0 时释放。 - -问题是:**循环引用**会让计数永远不为 0。 - -```javascript -let a = {}; -let b = {}; -a.other = b; -b.other = a; -``` - -## 可达性分析(Tracing / Mark & Sweep) - -现代 JS 引擎通常采用“可达性分析”: - -* 以全局对象、调用栈等为根(root)。 -* 从根出发,能到达的对象都“活着”。 -* 无法到达的对象就是垃圾。 - -这种方法能处理循环引用。 - -## 内存泄漏(Memory Leak) - -即使有 GC,也可能泄漏:对象一直“可达”,但逻辑上早已没用。 - -常见原因: - -* 全局变量过多。 -* 事件监听未移除。 -* 缓存无限增长。 -* 闭包长时间持有大对象。 - -## 简单建议 - -* 用完的引用设为 `null`。 -* 给缓存设置上限。 -* 事件监听及时移除。 -* 临时映射用 `WeakMap` / `WeakSet`。 - -## 进一步阅读 - -理解 GC 与引用,会帮助你理解“对象生命周期”和性能问题。 diff --git a/docs/reference/glossary/immutability.md b/docs/reference/glossary/immutability.md deleted file mode 100644 index 4ee572c..0000000 --- a/docs/reference/glossary/immutability.md +++ /dev/null @@ -1,38 +0,0 @@ -# 不可变性(Immutability) - -不可变性指的是:对象一旦创建,就不再被修改。如果需要变化,就创建一个新的对象。 - -## 为什么有用 - -* **减少共享状态冲突**:不修改原对象,减少别名带来的副作用。 -* **更容易推理**:同一输入得到同一输出。 -* **更容易做回滚/撤销**:旧对象仍然保留。 - -## 成本 - -* 会产生更多对象,可能增加内存占用。 -* 需要垃圾回收器回收旧对象。 - -## JavaScript 中的常见做法 - -```javascript -let user = { name: 'Kimmy', age: 18 }; -let updated = { ...user, age: 19 }; -``` - -数组也可以用 `slice()` / `concat()` / `map()` 生成新数组。 - -## Object.freeze - -`Object.freeze` 可以阻止修改,但它是浅层冻结: - -```javascript -let obj = Object.freeze({ profile: { city: 'SH' } }); -obj.profile.city = 'BJ'; // 仍然能改 -``` - -如果需要深度冻结,需要递归处理。 - -## 进一步阅读 - -不可变性和函数式编程关系紧密,也是很多现代框架的设计基础。 diff --git a/docs/reference/glossary/lexical-scope.md b/docs/reference/glossary/lexical-scope.md deleted file mode 100644 index c76f6f9..0000000 --- a/docs/reference/glossary/lexical-scope.md +++ /dev/null @@ -1,34 +0,0 @@ -# 词法作用域(Lexical Scope) - -词法作用域指的是:变量的可见范围由“代码写在哪里”决定,而不是“运行时在哪执行”。 - -## 一个简单例子 - -```javascript -function outer() { - let x = 1; - function inner() { - return x + 1; - } - return inner; -} - -let f = outer(); -f(); // 2 -``` - -`inner` 捕获了 `outer` 里的 `x`,这就是闭包的基础。 - -## 与动态作用域的区别 - -在动态作用域中,变量的查找取决于“调用链”。但 JavaScript 使用的是词法作用域,所以按“定义时的位置”查找。 - -## 常见影响 - -* 函数可以访问外层变量(闭包)。 -* 变量遮蔽(inner 里定义的变量会遮住外层同名变量)。 -* `var` 没有块级作用域,会导致循环陷阱。 - -## 进一步阅读 - -理解词法作用域能帮助你理解闭包、模块、以及一些“看起来很奇怪的变量值”。 diff --git a/docs/reference/glossary/process-and-thread.md b/docs/reference/glossary/process-and-thread.md deleted file mode 100644 index ae58ee6..0000000 --- a/docs/reference/glossary/process-and-thread.md +++ /dev/null @@ -1,30 +0,0 @@ -# 进程与线程(Process & Thread) - -## 进程(Process) - -进程可以理解为“正在运行的程序实例”。 - -* 有独立的内存空间。 -* 进程之间默认互相隔离。 - -## 线程(Thread) - -线程是进程内部的“执行单元”。 - -* 同一进程的线程共享内存。 -* 线程之间切换更轻量。 - -## 为什么要区分 - -* **进程隔离**更安全,但通信成本高。 -* **线程共享**更高效,但容易出现共享状态问题。 - -## 与 JavaScript 的关系 - -浏览器主线程是单线程执行 JS,但它可以创建 Worker 作为“后台线程”。 - -Node.js 也可以用 Worker Threads,但大多数代码仍是单线程事件循环模型。 - -## 进一步阅读 - -理解进程与线程能帮助你理解“并发、并行、隔离和共享”的差别。 diff --git a/docs/reference/glossary/prototype-chain.md b/docs/reference/glossary/prototype-chain.md deleted file mode 100644 index 4a4a94d..0000000 --- a/docs/reference/glossary/prototype-chain.md +++ /dev/null @@ -1,41 +0,0 @@ -# 原型链(Prototype Chain) - -JavaScript 对象通过“原型链”实现属性查找与继承。 - -## 一个直观的说法 - -当你访问 `obj.foo` 时: - -1. 先在 `obj` 自己身上找。 -2. 找不到,就去 `obj.__proto__`(原型)上找。 -3. 继续往上,直到 `null` 为止。 - -这条向上的链条就是原型链。 - -## `prototype` 与 `__proto__` - -* `Function.prototype`:函数的原型对象(给实例用)。 -* `obj.__proto__`:对象的原型引用(指向构造函数的 `prototype`)。 - -## `class` 的本质 - -`class` 只是语法糖,底层仍然是“函数 + 原型链”。 - -```javascript -class Person { - say() { console.log('hi'); } -} - -let p = new Person(); -``` - -`p` 的 `__proto__` 指向 `Person.prototype`。 - -## 常见误区 - -* 原型链不是“复制”,而是“引用”。 -* 修改原型会影响所有实例。 - -## 进一步阅读 - -原型链让 JavaScript 拥有灵活的继承方式,但也需要小心全局修改。 diff --git a/docs/reference/glossary/race-condition.md b/docs/reference/glossary/race-condition.md deleted file mode 100644 index 3d7576a..0000000 --- a/docs/reference/glossary/race-condition.md +++ /dev/null @@ -1,38 +0,0 @@ -# 竞态条件(Race Condition) - -当多个任务同时读写同一份共享状态时,结果取决于“谁先谁后”,这就叫竞态条件。 - -## 原子性与临界区 - -`count = count + 1` 看起来是一句,但实际包含三个步骤:读取、计算、写回。只要这三个步骤之间可以被打断,就可能出错。 - -**必须连续执行、不能被打断的代码段**,叫做临界区。 - -```javascript -let count = 0; - -async function inc() { - let current = count; - await Promise.resolve(); - count = current + 1; -} - -Promise.all([inc(), inc()]).then(() => console.log(count)); // 可能是 1 -``` - -## 常见后果 - -* **丢失更新**:两个操作互相覆盖。 -* **结果不稳定**:同样输入多次运行结果不同。 -* **难以复现**:只有在特定时序下才出现。 - -## 常见对策 - -* **锁/互斥**:同一时间只允许一个任务进入临界区。 -* **队列化**:把操作排队顺序执行。 -* **不可变数据**:避免修改共享对象。 -* **消息传递**:不共享内存,只传递消息(如 Worker)。 - -## 进一步阅读 - -竞态条件是并发的“第一坑”,理解它会帮你理解死锁、原子性、事务等概念。 diff --git a/docs/reference/glossary/recursion.md b/docs/reference/glossary/recursion.md deleted file mode 100644 index 6cd4044..0000000 --- a/docs/reference/glossary/recursion.md +++ /dev/null @@ -1,29 +0,0 @@ -# 递归(Recursion) - -递归就是“函数调用自己”。 - -## 关键点 - -* 必须有**终止条件**(base case)。 -* 每次调用都“更接近终止条件”。 - -## 经典例子:阶乘 - -```javascript -function fact(n) { - return n <= 1 ? 1 : n * fact(n - 1); -} -``` - -## 递归与栈 - -每次递归调用都会压入调用栈。如果层数过多,就会“栈溢出”。 - -## 递归 vs. 循环 - -* 递归表达结构清晰。 -* 循环通常更高效、更安全。 - -## 进一步阅读 - -理解递归能帮助你理解树、图、分治等算法。 diff --git a/docs/reference/glossary/safety-and-security.md b/docs/reference/glossary/safety-and-security.md deleted file mode 100644 index de07f4c..0000000 --- a/docs/reference/glossary/safety-and-security.md +++ /dev/null @@ -1,19 +0,0 @@ -# 安全 - -在计算机科学中,安全代表着两个不同但相关的概念:safety(安全性)和security(安全保障)。虽然它们经常被混淆使用,但它们在含义和应用上存在一些区别。 - -可以说,safety(安全性)更注重系统的内部能力,而security(安全保障)则更关注系统对外的能力。 - -## 安全性(Safety) - -安全性关注的是系统或软件本身的性质,确保其在正常操作下不会对用户、数据或环境造成伤害或危险。安全性侧重于预防和减轻事故或意外事件的潜在危害,并确保系统在异常情况下能够进入安全状态。 - -例如,对于一辆自动驾驶汽车而言,安全性意味着它能够遵循交通规则、正确感知周围环境、有效地应对紧急情况,并保证乘客和其他道路使用者的安全。在软件开发中,安全性可以涉及对输入数据的验证、错误处理、边界检查和异常处理等方面,以防止潜在的崩溃、数据损坏或系统故障。 - -## 安全保障(Security) - -安全保障则更加关注系统或软件的保护和防御,以确保其免受恶意攻击、未经授权的访问、数据泄露或损坏等威胁。安全保障旨在确保系统的机密性、完整性和可用性,以及保护用户的隐私和敏感信息。 - -在计算机系统中,安全保障可以包括诸如身份验证和访问控制、加密和解密、防火墙和入侵检测系统(IDS)等措施。这些措施旨在防止恶意用户的未经授权访问、数据泄露、网络攻击或病毒感染等安全威胁。 - -总结起来,安全性(safety)强调系统的健壮性和对意外情况的适应能力,而安全保障则强调系统的保护性和对恶意威胁的防御能力。它们是计算机科学中维护系统完整性、可靠性和保护用户利益的重要方面,常常需要综合考虑来确保计算机系统的全面安全。 diff --git a/docs/reference/glossary/serialization.md b/docs/reference/glossary/serialization.md deleted file mode 100644 index 14e26f7..0000000 --- a/docs/reference/glossary/serialization.md +++ /dev/null @@ -1,26 +0,0 @@ -# 序列化(Serialization) - -序列化是把内存中的对象变成“可传输/可存储”的格式(通常是字符串或字节流)。 - -## 为什么需要 - -* 网络传输 -* 保存到磁盘 -* 跨进程通信 - -## JavaScript 中的常见方式 - -```javascript -let obj = { x: 1, y: 2 }; -let text = JSON.stringify(obj); // 序列化 -let obj2 = JSON.parse(text); // 反序列化 -``` - -## 常见限制 - -* JSON 不支持函数、`undefined`、循环引用。 -* 日期对象会变成字符串。 - -## 进一步阅读 - -序列化是“程序之间交流”的基础。 diff --git a/docs/reference/glossary/stack-and-heap.md b/docs/reference/glossary/stack-and-heap.md deleted file mode 100644 index f5b2b37..0000000 --- a/docs/reference/glossary/stack-and-heap.md +++ /dev/null @@ -1,32 +0,0 @@ -# 栈与堆(Stack & Heap) - -程序运行时,内存大致可以分为两类:**栈**和**堆**。 - -## 栈(Stack) - -* 存放函数调用的临时信息:参数、局部变量、返回地址。 -* “后进先出”(LIFO)。 -* 分配和释放很快。 - -递归太深会导致“栈溢出”。 - -## 堆(Heap) - -* 存放对象、数组等动态数据。 -* 空间更大,但管理更复杂。 -* 需要垃圾回收器释放不再使用的对象。 - -## 一个直观比喻 - -* 栈像“叠盘子”:最后放的最先拿。 -* 堆像“仓库”:物品放哪儿都行,需要登记和清理。 - -## 为什么你需要知道它 - -* 理解递归和栈溢出。 -* 理解为什么对象需要 GC。 -* 理解“引用”和“生命周期”。 - -## 进一步阅读 - -深入理解栈与堆,会让你对性能、内存泄漏有更直观的判断。 diff --git a/docs/reference/glossary/sync-async.md b/docs/reference/glossary/sync-async.md deleted file mode 100644 index 31acae4..0000000 --- a/docs/reference/glossary/sync-async.md +++ /dev/null @@ -1,35 +0,0 @@ -# 同步与异步(Sync & Async) - -## 同步(Synchronous) - -同步表示“按顺序执行”:上一件事没完成,下一件事就不能开始。 - -```javascript -console.log('A'); -console.log('B'); -``` - -这两句一定按顺序输出。 - -## 异步(Asynchronous) - -异步表示“先登记,稍后回来处理”。 - -```javascript -console.log('A'); -setTimeout(() => console.log('B'), 0); -console.log('C'); -``` - -输出顺序通常是:A C B。 - -## 容易混淆的点 - -* 同步/异步说的是“什么时候拿到结果”。 -* 阻塞/非阻塞说的是“等待时能不能干别的事”。 - -二者不同,但经常同时出现。 - -## 进一步阅读 - -理解同步/异步有助于你理解事件循环、Promise 和并发问题。 diff --git a/docs/reference/glossary/tail-call-optimization.md b/docs/reference/glossary/tail-call-optimization.md deleted file mode 100644 index fe83b32..0000000 --- a/docs/reference/glossary/tail-call-optimization.md +++ /dev/null @@ -1,28 +0,0 @@ -# 尾调用优化(Tail Call Optimization) - -尾调用优化(TCO)是一种编译器/运行时优化:当函数的最后一步是调用另一个函数时,可以复用当前栈帧,避免栈增长。 - -## 什么是“尾调用” - -```javascript -function f(x) { - return g(x); // 最后一步是调用 g -} -``` - -因为 `f` 的工作已经完成,所以理论上可以直接把控制权交给 `g`,不必保留 `f` 的栈帧。 - -## 为什么重要 - -* 递归可以像循环一样节省栈空间。 -* 能避免深度递归导致的栈溢出。 - -## JavaScript 的现实情况 - -ECMAScript 曾经规范化过 TCO,但多数运行时并未开启,所以在 JS 中不能依赖它。 - -因此,实际工程里深递归往往需要改写成循环或显式栈。 - -## 进一步阅读 - -理解 TCO 有助于你判断“递归是否安全”。 diff --git a/docs/reference/glossary/testing.md b/docs/reference/glossary/testing.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/reference/glossary/transaction.md b/docs/reference/glossary/transaction.md deleted file mode 100644 index a917010..0000000 --- a/docs/reference/glossary/transaction.md +++ /dev/null @@ -1,27 +0,0 @@ -# 事务(Transaction) - -事务是“一组操作要么全部成功,要么全部失败”的机制。 - -## 为什么需要 - -在涉及资金、库存、订单等场景时,部分失败会造成数据不一致。 - -## ACID(简述) - -* **Atomicity 原子性**:要么全部成功,要么全部失败。 -* **Consistency 一致性**:数据满足约束。 -* **Isolation 隔离性**:并发事务互不干扰。 -* **Durability 持久性**:成功后结果可持久保存。 - -## 简单类比 - -转账: - -* A 扣钱成功 -* B 加钱失败 - -如果没有事务,就会出现“钱少了、但没加到对方”的问题。 - -## 进一步阅读 - -事务是数据库与并发控制的核心概念。 diff --git a/docs/reference/glossary/unicode-utf8.md b/docs/reference/glossary/unicode-utf8.md deleted file mode 100644 index a0572be..0000000 --- a/docs/reference/glossary/unicode-utf8.md +++ /dev/null @@ -1,25 +0,0 @@ -# Unicode / UTF-8 - -Unicode 是一个字符集,定义了“字符与编号(码点)”的映射。UTF-8 是一种编码方式,负责把码点变成字节序列。 - -## 一个直观例子 - -* `A` 的码点是 U+0041 -* 用 UTF-8 编码时,就是一个字节:`0x41` - -而 `中` 的码点是 U+4E2D,用 UTF-8 编码时需要 3 个字节。 - -## 为什么 UTF-8 流行 - -* **兼容 ASCII**:英文字符仍然是 1 字节。 -* **节省空间**:对英文友好,对中文等多字节字符也能支持。 -* **互联网事实标准**:Web 世界几乎都用它。 - -## 常见误解 - -* **Unicode 不是编码**:Unicode 只是“码点表”。 -* **UTF-8 不是字符集**:UTF-8 只是编码方式。 - -## 进一步阅读 - -理解 Unicode / UTF-8 有助于避免乱码和文件编码问题。 diff --git a/docs/reference/glossary/value-reference.md b/docs/reference/glossary/value-reference.md deleted file mode 100644 index 1e474ce..0000000 --- a/docs/reference/glossary/value-reference.md +++ /dev/null @@ -1,39 +0,0 @@ -# 值语义与引用语义(Value vs. Reference) - -## 值语义 - -“值语义”表示:变量存的是数据本身,拷贝会得到独立的值。 - -```javascript -let a = 1; -let b = a; -b = 2; -a; // 1 -``` - -## 引用语义 - -“引用语义”表示:变量存的是对象地址,拷贝只复制地址。 - -```javascript -let obj1 = { x: 1 }; -let obj2 = obj1; -obj2.x = 2; -obj1.x; // 2 -``` - -## 浅拷贝与深拷贝 - -* **浅拷贝**:只复制第一层。 -* **深拷贝**:递归复制所有层。 - -```javascript -let u1 = { profile: { city: 'SH' } }; -let u2 = { ...u1 }; // 浅拷贝 -u2.profile.city = 'BJ'; -u1.profile.city; // 'BJ' -``` - -## 进一步阅读 - -理解值语义/引用语义是理解“共享状态”和“意外修改”的基础。 diff --git a/docs/reference/rag-deep-dive.md b/docs/reference/rag-deep-dive.md deleted file mode 100644 index b1bdfca..0000000 --- a/docs/reference/rag-deep-dive.md +++ /dev/null @@ -1,145 +0,0 @@ -# RAG 技术详解:向量检索与混合搜索 - -本文是[高级 Prompting 与推理增强](../agentic-ai/reasoning-logic.md)的延伸阅读,详细讲解 RAG 背后的技术原理。 - -## 向量嵌入(Vector Embedding) - -向量嵌入是 RAG 的基础。它的作用是把文本转换成一组数字(向量),使得语义相近的文本在向量空间中距离也近。 - -比如: - -- "怎么退货" → [0.23, -0.45, 0.87, ...] -- "退款流程是什么" → [0.21, -0.42, 0.85, ...] -- "今天天气真好" → [-0.67, 0.12, -0.33, ...] - -前两个句子在向量空间里很接近(因为语义相似),第三个句子距离很远(因为语义不同)。 - -常见的 Embedding 模型: - -- **OpenAI text-embedding-3-small / 3-large**:商业模型,效果好,按 Token 计费。 -- **Cohere embed-v3**:支持多语言,中文效果好。 -- **BGE 系列(BAAI)**:开源模型,中文效果优秀,可以本地部署。 -- **Jina Embeddings**:开源,支持超长文本。 - -选择 Embedding 模型时要考虑:维度大小(影响存储和检索速度)、多语言支持、最大文本长度。 - -## 文档分块(Chunking) - -把长文档直接转换成一个向量,效果通常不好。更好的做法是切成小块,每块独立转换。 - -常见的分块策略: - -- **固定长度分块**:每 500 个字一块,重叠 50 个字。简单但粗暴。 -- **按段落分块**:每个段落一块。保留了自然边界,但块大小不均匀。 -- **按语义分块**:用模型判断文本的语义边界,在合适的位置切分。效果最好,但计算成本高。 -- **递归分块**:先按大块切,如果某块太长,再按更小的单位切。LangChain 的 RecursiveCharacterTextSplitter 就是这个思路。 - -重叠(Overlap)很重要。如果两个块之间没有重叠,一个完整的概念可能被切成两半,每半都不完整。通常重叠 10-20% 的块长度。 - -## 向量索引:HNSW - -当向量数据库里有几百万甚至几亿个向量时,逐一比较相似度太慢。HNSW(Hierarchical Navigable Small World)是最常用的近似最近邻(ANN)索引算法。 - -HNSW 的核心思想是"多层跳表": - -- 最底层包含所有向量。 -- 往上每一层包含越来越少的向量,但连接跨度越来越大。 -- 检索时从最顶层开始,快速定位到大致区域,然后逐层往下精确查找。 - -这就像找人:先在世界地图上定位到国家,再定位到城市,再定位到街道,最后找到门牌号。比从头到尾遍历所有地址快得多。 - -HNSW 的参数: - -- **M**:每个节点的连接数。M 越大,检索越精确,但占用内存越多。 -- **ef_construction**:构建索引时的搜索范围。越大索引质量越高,但构建越慢。 -- **ef_search**:查询时的搜索范围。越大检索越精确,但查询越慢。 - -常见的向量数据库: - -- **Chroma**:轻量级,适合本地开发和小规模应用。 -- **FAISS(Meta)**:高性能,适合大规模向量检索,但需要自己管理持久化。 -- **Pinecone**:云服务,开箱即用,适合不想自己运维的场景。 -- **Weaviate**:支持混合搜索(向量 + 关键词),功能丰富。 -- **Qdrant**:Rust 实现,性能好,支持过滤和多向量。 - -## 稠密索引 vs 稀疏索引 - -向量检索有两种主要方式: - -### 稠密索引(Dense Index) - -就是上面讲的 Embedding + 向量相似度搜索。每个文档块被压缩成一个固定维度的向量,检索时计算向量之间的余弦相似度。 - -优点:能捕捉语义,"退货"和"退款"能互相找到。 - -缺点:对精确匹配不擅长。如果用户搜索一个具体的型号"iPhone 15 Pro Max",稠密检索可能返回"手机推荐"这种泛泛的结果。 - -### 稀疏索引(Sparse Index) - -传统的关键词搜索,比如 BM25 算法。每个文档被表示为一个高维稀疏向量(大部分维度是 0),每个维度对应一个词。 - -优点:精确匹配效果好。搜索"iPhone 15 Pro Max"就是会找到包含这个词的文档。 - -缺点:不理解语义。"退货"找不到"退款",因为它们是不同的词。 - -### 为什么需要两种? - -实际场景中,用户的查询有时需要语义理解("怎么把东西退回去"),有时需要精确匹配("订单号 12345 的状态")。单一索引很难同时满足。 - -## 混合搜索与 RRF - -混合搜索(Hybrid Search)把稠密检索和稀疏检索的结果合并起来,取长补短。 - -最常用的合并策略是 **RRF(Reciprocal Rank Fusion)**。它的原理很简单: - -1. 分别用稠密检索和稀疏检索,各返回一个排序列表。 -2. 对每个文档,计算它在两个列表中的排名。 -3. 用一个公式合并排名:`RRF_score = 1/(k + rank_dense) + 1/(k + rank_sparse)`,其中 k 通常取 60。 -4. 按合并后的分数重新排序。 - -举个例子: - -| 文档 | 稠密排名 | 稀疏排名 | RRF 分数 | -|---|---|---|---| -| 文档 A | 1 | 5 | 1/61 + 1/65 = 0.0318 | -| 文档 B | 3 | 1 | 1/63 + 1/61 = 0.0323 | -| 文档 C | 2 | 8 | 1/62 + 1/68 = 0.0309 | - -文档 B 虽然在稠密检索中排名不高,但因为在稀疏检索中排名第一,合并后总分最高。 - -RRF 的好处是不需要调权重。它只看排名,不看具体的相似度分数,所以两种检索的分数尺度不同也不影响结果。 - -## RAG 的实际部署考量 - -### 检索数量(Top-K) - -检索多少个文档块给模型?太少可能遗漏关键信息,太多会浪费 Token 且引入噪声。通常 3-10 个是常见范围,具体取决于文档块的大小和模型的上下文窗口。 - -### 重排序(Reranking) - -先用向量检索拿到 Top-20 个候选,再用一个更精确的模型(比如 Cohere Reranker、BGE Reranker)重新排序,取 Top-5 给模型。这一步能显著提升检索质量。 - -### 上下文拼接 - -检索到的文档块怎么拼到 Prompt 里?常见做法: - -- 按相关度排序,最相关的放在最前面。 -- 每个块加上来源标注(文档名、页码),方便引用和追溯。 -- 如果总长度超出限制,优先保留相关度最高的。 - -### 评估指标 - -怎么衡量 RAG 系统的效果? - -- **检索精度(Precision@K)**:Top-K 个结果中有多少是真正相关的。 -- **检索召回(Recall@K)**:所有相关文档中有多少被检索到了。 -- **回答质量**:最终回答是否准确、完整、有依据。可以用人工评估或 LLM-as-Judge。 - -## 小结 - -- 向量嵌入把文本变成数字,让语义相似的文本在向量空间中距离近。 -- 文档分块是 RAG 的关键预处理步骤,分块策略直接影响检索质量。 -- HNSW 是最常用的向量索引算法,用"多层跳表"加速检索。 -- 稠密索引擅长语义理解,稀疏索引擅长精确匹配,两者互补。 -- RRF 是最常用的混合搜索合并策略,简单且不需要调权重。 -- 实际部署时,重排序、检索数量、上下文拼接都会影响最终效果。 diff --git a/docs/res/code.html b/docs/res/code.html deleted file mode 100644 index 1ff663d..0000000 --- a/docs/res/code.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Introduction to Programming - - - - - \ No newline at end of file diff --git a/docs/res/images/cons-cells.png b/docs/res/images/cons-cells.png deleted file mode 100644 index b4995e1..0000000 Binary files a/docs/res/images/cons-cells.png and /dev/null differ diff --git a/docs/public/favicon.ico b/favicon.ico similarity index 100% rename from docs/public/favicon.ico rename to favicon.ico diff --git a/hashmap.json b/hashmap.json new file mode 100644 index 0000000..9b7f186 --- /dev/null +++ b/hashmap.json @@ -0,0 +1 @@ +{"summary.md":"6e51fa23","ch01_environment.md":"ad374fcf","ch06_data.md":"8876a824","ch10_object.md":"a4f3746e","intro.md":"ca4ae51e","glossary.md":"9584a1ea","ch05_sequence.md":"e9066e5e","ch11_concurrency.md":"2b82afad","ch09_closure.md":"a7117936","stream.md":"a4405d7c","index.md":"a7ecfcb0","ch07_state.md":"5efbe2c2","ch04_encoding.md":"6d49b0e8","ch02_computation.md":"d79955df","ch03_procedure.md":"2caba3b4","ch08_reference.md":"ea346374"} diff --git a/index.html b/index.html new file mode 100644 index 0000000..c5c087f --- /dev/null +++ b/index.html @@ -0,0 +1,24 @@ + + + + + + 编程导引 | 编程导引 + + + + + + + + + + + + +
Skip to content

编程导引

Introduction to Programming

从零开始你的计算机编程之旅

+ + + + \ No newline at end of file diff --git a/intro.html b/intro.html new file mode 100644 index 0000000..09f54dc --- /dev/null +++ b/intro.html @@ -0,0 +1,24 @@ + + + + + + 引言 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page

引言

编程

编程(Programming)是一项创造性的活动,如同创作(Composing)、绘画(Painting)等一样。我们常常会通过一系列的步骤把所需要的部件进行组合,得到另一个期望的结构,或者是解决一个实际性的问题。表达和构建这些步骤的过程,就是编程(当然,在脱离程序语言以后,也可以把它叫做规划(Programme))。

我们会简单地把一件件小事按顺序做,这样一件件小事的组合以后就可以完成一件更大的事情。比如我们会切菜,生火,炒菜,煮饭,这样我们就能够做出一顿午餐。

当然在这个过程中可能会设计一些计算。我们会考虑,需要放几勺盐和几把米,以适合最后吃午餐的人的营养均衡。在特殊的时候,甚至需要精确到毫克级别的营养规划,以及不同人的营养结构的差异等。我们会在第二节探讨关于计算,以及计算的组合等相关问题。

在规划和解决问题的时候可能并不是一帆风顺的,有时候我们会面临各种抉择。比如,考虑明天下午吃素还是吃荤,吃鸡肉和还是鱼肉,吃宫保鸡丁还是鱼香肉丝等等。有比如,在我们准备吃鱼香肉丝的时候发现没有鱼了,又该怎么办。

在任何需要做选择的时候,作为一个程序员(规划师),你不应该出现选择困难症。因为任何一个涵待解决的问题都必须得到解决之后才能让这个程序正常地执行。所以,我们在第三章中会探讨关于过程设计,以及如何利用循环和选择等手法来设计一个完善的程序的相关问题。

但是做饭的时候你是怎么区分盐和砂糖、醋和酱油的呢?当然你会说每个都尝一遍,但是这样既有可能浪费,又不卫生。所以习惯性地我们会关注这些材料瓶上面的标签,从上面知道我们用的是什么东西。当然对于读不懂标签的计算机来说,这些材料作为商品的时候都会有固定的条形码,这个条形码也能够让计算机知道一个被它看到的东西代表着什么。因为它知道这些条码是如何编码(Encoding)的。

当你遇到大量具有相同组件的时候,可以考虑用序列(Sequence)或者列表(List)来安排这些组件。比如做一道菜的每一个步骤对应在菜谱上的每一项。或者是在处理具有层级结构以及更复杂的关联的结构的时候采用(Tree)和(Graph)来组织。比如六度关系理论,实际上就是一张非常大的(Graph)。

有时候会考虑到一部分组件是可变的。比如时间总是流逝的,人总会长大的。当我们尝试描述一个组件改变的时候,就引入了状态(State)。这种时候我们更希望能够把改变的数据和相应的操作做一个绑定,这样子绑定了组件的操作就可以看成一个闭包(Closure)。简单地比如一个计数器。当在组件上面绑定更多操作的时候,就可以把它看作一个对象(Object)。

可变的状态可能会带来的一个问题就是,如果有多个人尝试同时去修改这个状态的时候,会造成结果的不一致性。比如,一个没有加入事务处理的银行账户,两个人同时在进行操作,账户中原有1000元,甲存入了200块,但在甲存入200块还未完成的同时乙取出了200块。这种时候会有几种可能的结果,第一种就是甲先于乙完成操作,结果就是账户余额变成了800块,凭空损失了200块;另外一种就是乙先于甲完成了操作,结果账户余额就变成了甲操作的结果,1200块,凭空多出来400块。这种问题就属于并发(Concurrency)。

JavaScript

本书选择使用JavaScript来讲述编程中的各种思想和概念。

我们并不是在讲述JavaScript这门编程语言,只是通过JavaScript和其提供的丰富的方便特性,来讲述关于“程序设计”的内容。任何与JavaScript细节相关的东西,可以参考Mozilla Developer Network,对于特定的主题,我也会专门列出可以参阅的链接。StackOverflow上也有丰富的资源来解答你的疑惑。

作为入门书籍,为了直观地表述各种概念或者原理,本书可能会使用一些非专业词汇,或者与准确定义有偏差的理解。JavaScript作为一种生产力工具,其准确定义和行为以ECMA 262国际标准为准。对专业人员如有任何困惑或者争议的点,请参考标准文档的描述。同样,如果你通过本书对编程和相关的东西产生了兴趣,希望深入了解和学习,也请使用专业的书籍和资料。本书只是作为入门的引导,不对专业内容做解释和定义。

更新状态

这本书从2015年8月开始编写,中间经过了多次的重写过程,包括把编程语言从偏硬核且专业的C++转向为JavaScript、引入更多资料和练习、增补和调整章节顺序等,目前内容仍处于不稳定的状态,请参考下面的状态来确定这一章是否值得学习:

  • 第一章 环境 (rewriting)
  • 第二章 计算 Computation (outdated)
  • 第三章 过程 Procedural (outdated)
  • 第四章 编码 Encoding (outdated)
  • 第五章 序列 Sequence (outdated)
  • 第六章 数据 Data (outdated)
  • 第七章 状态 State (prepared)
  • 第八章 引用 Reference
  • 第九章 闭包 Closure
  • 第十章 对象 Object (prepared)
  • 第十一章
  • 第十二章
  • more...

目录

参与贡献

本书的源码托管在github:kenpusney/i2p-the-book仓库,你可以提交issue或者pull request贡献和改进本书。

支持我

关注我的公众号获取更新:

微信公众号

资助我创作:

微信二维码:

Buy me a coffee

或点击我的 Paypal 链接给予帮助。

特别感谢以下朋友的资助
+ + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 7bbe14b..0000000 --- a/package-lock.json +++ /dev/null @@ -1,2791 +0,0 @@ -{ - "name": "intro", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "intro", - "devDependencies": { - "markdown-it-mathjax3": "^4.3.2", - "vitepress": "^2.0.0-alpha.16" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@docsearch/css": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.5.4.tgz", - "integrity": "sha512-gzO4DJwyM9c4YEPHwaLV1nUCDC2N6yoh0QJj44dce2rcfN71mB+jpu3+F+Y/KMDF1EKV0C3m54leSWsraE94xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@docsearch/js": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-4.5.4.tgz", - "integrity": "sha512-jEBsIklNTevznLZouB0y6SZcaT897gRHnGTzCcH32ibPZRVj/9FyuAM2LUTk61sFdOY79LH4V9rYsIdOe6Wq2g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@docsearch/sidepanel-js": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/@docsearch/sidepanel-js/-/sidepanel-js-4.5.4.tgz", - "integrity": "sha512-f4KE4cG+P09gJHQNfttfMNy+3gAGj8U0YEgiOOso0YCFI5nGoVvJQpxNMSPgXs4sG34A/oCfKhYwHJiqgHhxPw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@iconify-json/simple-icons": { - "version": "1.2.69", - "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.69.tgz", - "integrity": "sha512-T/rhy5n7pzE0ZOxQVlF68SNPCYYjRBpddjgjrJO5WWVRG8es5BQmvxIE9kKF+t2hhPGvuGQFpXmUyqbOtnxirQ==", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "@iconify/types": "*" - } - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", - "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.0.tgz", - "integrity": "sha512-dnxczajOqt0gesZlN5pGQ1s1imQVrsmCw5G2Ci4oM+0WvNz3pyRnlWrT7McoZIb8VlFwCawdmbWRmxRn7HI+VQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.0.tgz", - "integrity": "sha512-Bp3JpGP00Vu3f238ivRrjf7z3xSzVPXqCmaJYA9t2c+c8vKYvOzmXF7LkkeUalTEGd6cZcSWe+PFIP3Vy48fRg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.0.tgz", - "integrity": "sha512-zaYIpr670mUmmZ1tVzUFplbQbG7h3Gugx3L5FoqhsC2m/YnLlR1a7zVLmXNPy+iY1tFPEbNG+HHBXZGyId0G5w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.0.tgz", - "integrity": "sha512-+P49fvkv2dSoeevUW+lgZ/I2JHSsJCK1Lyjj7Cu6E4UHG4tS9XIefzIjo5qhgELjAclnen1rLzK2PMKJdo+Dyg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.0.tgz", - "integrity": "sha512-l3FAAOyKJXH2ea6KNFN+MMgC/rnE94YGLXs2ehYqDcCoHt1DpvgWX75BhUJxN38XojP7Ul+4H8PRn7EdyqSDrw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.0.tgz", - "integrity": "sha512-VokPN3TSctKj65cyCNPaUh4vMFA8awxOot/0sp+4J7ZlNRKQEhXhawqPwajoi8H5ZFt61i0ugZJuTKXBjGJ17Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.0.tgz", - "integrity": "sha512-DxH0P3wxm+Yzs/p3zrk9dw1rURu8p0Nv5+MRK/L7OtnLNg5rLZraSBFZ8iUXOd9f2BlhJyEpIZUH/emjq4UJ4g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.0.tgz", - "integrity": "sha512-T6ZvMNe84kAz6TBWHC7hGAoEtzP1LWYw/AqayGWEF6uISt3Abk/st06LqRD9THd7Xz3NxzurUpzAuEAUbZf+nw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.0.tgz", - "integrity": "sha512-q/4hzvQkDs8b4jIBab1pnLiiM0ayTZsN2amBFPDzuyZxjEd4wDwx0UJFYM3cOZzSf5Kw8fnWSprJzIBMkcR44Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.0.tgz", - "integrity": "sha512-vvYWX3akdEAY6km+9wAqFDnk6pQsbJKVnj7xawcvs/+fdlYBGp+U+Qq/lLfpIxYIZvZLHMAKD9HLdacSx/r3dw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.0.tgz", - "integrity": "sha512-DePa5cqOxDP/Zp0VOXpeWaGew5iIv5DXp9NYbzkX5PFQyWVX9184WCTh3hvr/7lhXo8ZVlbFLkz8+o/q1dU6gA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.0.tgz", - "integrity": "sha512-LV8aWMB8UChglMCEzs7RkN0GsH29RJaLLqwm9fCIjlqwxQTiWAqNcc7wjBkH31hV0PU/yVxGYvrYsgfea2qw6g==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.0.tgz", - "integrity": "sha512-QoNSnwQtaeNu5grdBbsL0tt1uyl5EnS8DA8Mr3nluMXbhdQNyhN+G4tBax7VCdxLKj8YJ0/4OO9Ho84jMnJtKA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.0.tgz", - "integrity": "sha512-/zZp5MKapIIApE8trN8qLGNSiRN9TUoaUZ1cmVu4XnVdd5LQLOXTtyi+vtfUbNnT3iyjzpPqYeKXmvJ+gJGYWw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.0.tgz", - "integrity": "sha512-RbrzcD3aJ1k3UbtMRRBNwojdVVyXjuVAFTfn/xPa6EEl6GE9Sm/akPgFTb9aAC9pMKGJ6CtWxaGrqWcabH+ySg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.0.tgz", - "integrity": "sha512-ZF+onDsBso8PJf1XaG9lB+O9RnBpKGnY6OrzC4CSHrtC1jb6jWLTKK4bRqdoCXHd22gyr2hiYmEAm8Wns/BOCw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.0.tgz", - "integrity": "sha512-Atk0aSIk5Zx2Wuh9dgRQgLP0Koc8hOeYpbWryMXyk8G8/HmPkwPPkMqIIDhrXHHYqfUzSJA/I7IWSBv8xSmRBA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.0.tgz", - "integrity": "sha512-0uMOcf3eZ5K+K4cYHkdxShFMPlPXCOdfDFEFn9dNYAEEd2cVvmOfH7zFgRVoDgmtQ1m9k5q7qfrHzyMAubKYUA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.0.tgz", - "integrity": "sha512-mvFtE4A/t/7hRJ7X8Ozmu8FsIkAUat2nzl12pgU337BRmq87AQUJztwHz2Zv5/tjo9/C95E66CK03SI/ToEDJw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.0.tgz", - "integrity": "sha512-z9b9+aTxvt8n2rNltMPvyaUfB8NJ+CVyOrGK/MdIKHx7B+lXmZpm/XbRsU7Rpf3fRqJ2uS6mBJiJveCtq8LHDg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.0.tgz", - "integrity": "sha512-jXaXFqKMehsOc+g8R6oo33RRC6w07G9jDBxAE5eAKX7mOcCbZloYIPNhfG9Wl+P9O9IWHFO4OJgPi1Ml2qkt7w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.0.tgz", - "integrity": "sha512-OXNWVFocS2IA4+QplhTZZ2a+8hPZR7T8KuozsNmJKK8y7cp83StHvGksfHzPG3wczWTczyWHVQuqeiTUbjiyBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.0.tgz", - "integrity": "sha512-AlAbNtBO637LxSldqV43z0FfXoGfl2TW1DgAg/bs7aQswFbDewz2SJm3BUhiGfbOVtW571xbc9p+REdxhyN/Eg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.0.tgz", - "integrity": "sha512-QRSrQXyJ1M4tjNXdR0/G/IgV6lzfQQJYBjlWIEYkY2Xs86DRl/iEpQ4blMDjJxSl7n19eDKKXMg0AmuBVYy8pQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.0.tgz", - "integrity": "sha512-tkuFxhvKO/HlGd0VsINF6vHSYH8AF8W0TcNxKDK6JZmrehngFj78pToc8iemtnvwilDjs2G/qSzYFhe9U8q+fw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/core": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.22.0.tgz", - "integrity": "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.22.0.tgz", - "integrity": "sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.22.0.tgz", - "integrity": "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.22.0.tgz", - "integrity": "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.22.0.tgz", - "integrity": "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0" - } - }, - "node_modules/@shikijs/transformers": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.22.0.tgz", - "integrity": "sha512-E7eRV7mwDBjueLF6852n2oYeJYxBq3NSsDk+uyruYAXONv4U8holGmIrT+mPRJQ1J1SNOH6L8G19KRzmBawrFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.22.0", - "@shikijs/types": "3.22.0" - } - }, - "node_modules/@shikijs/types": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.22.0.tgz", - "integrity": "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/markdown-it": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", - "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/linkify-it": "^5", - "@types/mdurl": "^2" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/web-bluetooth": { - "version": "0.0.21", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", - "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@vitejs/plugin-vue": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.4.tgz", - "integrity": "sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rolldown/pluginutils": "1.0.0-rc.2" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", - "vue": "^3.2.25" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.27.tgz", - "integrity": "sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.5", - "@vue/shared": "3.5.27", - "entities": "^7.0.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-core/node_modules/entities": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", - "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.27.tgz", - "integrity": "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.5.27", - "@vue/shared": "3.5.27" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.27.tgz", - "integrity": "sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.5", - "@vue/compiler-core": "3.5.27", - "@vue/compiler-dom": "3.5.27", - "@vue/compiler-ssr": "3.5.27", - "@vue/shared": "3.5.27", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.21", - "postcss": "^8.5.6", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.27.tgz", - "integrity": "sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.27", - "@vue/shared": "3.5.27" - } - }, - "node_modules/@vue/devtools-api": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.0.6.tgz", - "integrity": "sha512-+lGBI+WTvJmnU2FZqHhEB8J1DXcvNlDeEalz77iYgOdY1jTj1ipSBaKj3sRhYcy+kqA8v/BSuvOz1XJucfQmUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-kit": "^8.0.6" - } - }, - "node_modules/@vue/devtools-kit": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.6.tgz", - "integrity": "sha512-9zXZPTJW72OteDXeSa5RVML3zWDCRcO5t77aJqSs228mdopYj5AiTpihozbsfFJ0IodfNs7pSgOGO3qfCuxDtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-shared": "^8.0.6", - "birpc": "^2.6.1", - "hookable": "^5.5.3", - "mitt": "^3.0.1", - "perfect-debounce": "^2.0.0", - "speakingurl": "^14.0.1", - "superjson": "^2.2.2" - } - }, - "node_modules/@vue/devtools-shared": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.6.tgz", - "integrity": "sha512-Pp1JylTqlgMJvxW6MGyfTF8vGvlBSCAvMFaDCYa82Mgw7TT5eE5kkHgDvmOGHWeJE4zIDfCpCxHapsK2LtIAJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "rfdc": "^1.4.1" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.27.tgz", - "integrity": "sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/shared": "3.5.27" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.27.tgz", - "integrity": "sha512-fxVuX/fzgzeMPn/CLQecWeDIFNt3gQVhxM0rW02Tvp/YmZfXQgcTXlakq7IMutuZ/+Ogbn+K0oct9J3JZfyk3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.27", - "@vue/shared": "3.5.27" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.27.tgz", - "integrity": "sha512-/QnLslQgYqSJ5aUmb5F0z0caZPGHRB8LEAQ1s81vHFM5CBfnun63rxhvE/scVb/j3TbBuoZwkJyiLCkBluMpeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.27", - "@vue/runtime-core": "3.5.27", - "@vue/shared": "3.5.27", - "csstype": "^3.2.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.27.tgz", - "integrity": "sha512-qOz/5thjeP1vAFc4+BY3Nr6wxyLhpeQgAE/8dDtKo6a6xdk+L4W46HDZgNmLOBUDEkFXV3G7pRiUqxjX0/2zWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-ssr": "3.5.27", - "@vue/shared": "3.5.27" - }, - "peerDependencies": { - "vue": "3.5.27" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.27.tgz", - "integrity": "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vueuse/core": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.2.0.tgz", - "integrity": "sha512-tpjzVl7KCQNVd/qcaCE9XbejL38V6KJAEq/tVXj7mDPtl6JtzmUdnXelSS+ULRkkrDgzYVK7EerQJvd2jR794Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/web-bluetooth": "^0.0.21", - "@vueuse/metadata": "14.2.0", - "@vueuse/shared": "14.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "vue": "^3.5.0" - } - }, - "node_modules/@vueuse/integrations": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-14.2.0.tgz", - "integrity": "sha512-Yuo5XbIi6XkfSXOYKd5SBZwyBEyO3Hd41eeG2555hDbE0Maz/P0BfPJDYhgDXjS9xI0jkWUUp1Zh5lXHOgkwLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vueuse/core": "14.2.0", - "@vueuse/shared": "14.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "async-validator": "^4", - "axios": "^1", - "change-case": "^5", - "drauu": "^0.4", - "focus-trap": "^7 || ^8", - "fuse.js": "^7", - "idb-keyval": "^6", - "jwt-decode": "^4", - "nprogress": "^0.2", - "qrcode": "^1.5", - "sortablejs": "^1", - "universal-cookie": "^7 || ^8", - "vue": "^3.5.0" - }, - "peerDependenciesMeta": { - "async-validator": { - "optional": true - }, - "axios": { - "optional": true - }, - "change-case": { - "optional": true - }, - "drauu": { - "optional": true - }, - "focus-trap": { - "optional": true - }, - "fuse.js": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "jwt-decode": { - "optional": true - }, - "nprogress": { - "optional": true - }, - "qrcode": { - "optional": true - }, - "sortablejs": { - "optional": true - }, - "universal-cookie": { - "optional": true - } - } - }, - "node_modules/@vueuse/metadata": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.2.0.tgz", - "integrity": "sha512-i3axTGjU8b13FtyR4Keeama+43iD+BwX9C2TmzBVKqjSHArF03hjkp2SBZ1m72Jk2UtrX0aYCugBq2R1fhkuAQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/shared": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.2.0.tgz", - "integrity": "sha512-Z0bmluZTlAXgUcJ4uAFaML16JcD8V0QG00Db3quR642I99JXIDRa2MI2LGxiLVhcBjVnL1jOzIvT5TT2lqJlkA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "vue": "^3.5.0" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/birpc": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", - "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true, - "license": "ISC" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", - "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cheerio-select": "^1.5.0", - "dom-serializer": "^1.3.2", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.6.0.tgz", - "integrity": "sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "css-select": "^4.3.0", - "css-what": "^6.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.3.1", - "domutils": "^2.8.0" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cheerio/node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/copy-anything": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", - "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-what": "^5.2.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" - } - }, - "node_modules/escape-goat": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", - "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/focus-trap": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", - "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tabbable": "^6.4.0" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hookable": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/htmlparser2": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", - "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^3.3.0", - "domutils": "^2.4.2", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/fb55/htmlparser2?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/domhandler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", - "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.0.1" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/is-what": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", - "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/juice": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/juice/-/juice-8.1.0.tgz", - "integrity": "sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cheerio": "1.0.0-rc.10", - "commander": "^6.1.0", - "mensch": "^0.3.4", - "slick": "^1.12.2", - "web-resource-inliner": "^6.0.1" - }, - "bin": { - "juice": "bin/juice" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/mark.js": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", - "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/markdown-it-mathjax3": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/markdown-it-mathjax3/-/markdown-it-mathjax3-4.3.2.tgz", - "integrity": "sha512-TX3GW5NjmupgFtMJGRauioMbbkGsOXAAt1DZ/rzzYmTHqzkO1rNAdiMD4NiruurToPApn2kYy76x02QN26qr2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "juice": "^8.0.0", - "mathjax-full": "^3.2.0" - } - }, - "node_modules/mathjax-full": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.2.tgz", - "integrity": "sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esm": "^3.2.25", - "mhchemparser": "^4.1.0", - "mj-context-menu": "^0.6.1", - "speech-rule-engine": "^4.0.6" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mensch": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", - "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/mhchemparser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/mhchemparser/-/mhchemparser-4.2.1.tgz", - "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", - "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/minisearch": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", - "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", - "dev": true, - "license": "MIT" - }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" - }, - "node_modules/mj-context-menu": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz", - "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/oniguruma-parser": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", - "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz", - "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.1", - "regex": "^6.0.1", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/perfect-debounce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", - "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", - "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "dev": true, - "license": "MIT" - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/rollup": { - "version": "4.61.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.0.tgz", - "integrity": "sha512-T9mWdbWfQtp0B5lv/HX+wrhYsmXRlcWnXXmJbXqKJhlRaoS6KMhq0gpyzW4UJfclcxrEdLnTgjT2NjruLONu0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.61.0", - "@rollup/rollup-android-arm64": "4.61.0", - "@rollup/rollup-darwin-arm64": "4.61.0", - "@rollup/rollup-darwin-x64": "4.61.0", - "@rollup/rollup-freebsd-arm64": "4.61.0", - "@rollup/rollup-freebsd-x64": "4.61.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.61.0", - "@rollup/rollup-linux-arm-musleabihf": "4.61.0", - "@rollup/rollup-linux-arm64-gnu": "4.61.0", - "@rollup/rollup-linux-arm64-musl": "4.61.0", - "@rollup/rollup-linux-loong64-gnu": "4.61.0", - "@rollup/rollup-linux-loong64-musl": "4.61.0", - "@rollup/rollup-linux-ppc64-gnu": "4.61.0", - "@rollup/rollup-linux-ppc64-musl": "4.61.0", - "@rollup/rollup-linux-riscv64-gnu": "4.61.0", - "@rollup/rollup-linux-riscv64-musl": "4.61.0", - "@rollup/rollup-linux-s390x-gnu": "4.61.0", - "@rollup/rollup-linux-x64-gnu": "4.61.0", - "@rollup/rollup-linux-x64-musl": "4.61.0", - "@rollup/rollup-openbsd-x64": "4.61.0", - "@rollup/rollup-openharmony-arm64": "4.61.0", - "@rollup/rollup-win32-arm64-msvc": "4.61.0", - "@rollup/rollup-win32-ia32-msvc": "4.61.0", - "@rollup/rollup-win32-x64-gnu": "4.61.0", - "@rollup/rollup-win32-x64-msvc": "4.61.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/shiki": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.22.0.tgz", - "integrity": "sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.22.0", - "@shikijs/engine-javascript": "3.22.0", - "@shikijs/engine-oniguruma": "3.22.0", - "@shikijs/langs": "3.22.0", - "@shikijs/themes": "3.22.0", - "@shikijs/types": "3.22.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/slick": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", - "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", - "dev": true, - "license": "MIT (http://mootools.net/license.txt)", - "engines": { - "node": "*" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/speakingurl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", - "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/speech-rule-engine": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz", - "integrity": "sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "commander": "9.2.0", - "wicked-good-xpath": "1.3.0", - "xmldom-sre": "0.1.31" - }, - "bin": { - "sre": "bin/sre" - } - }, - "node_modules/speech-rule-engine/node_modules/commander": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz", - "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || >=14" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dev": true, - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/superjson": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", - "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "copy-anything": "^4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tabbable": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", - "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", - "dev": true, - "license": "0BSD" - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/valid-data-url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", - "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz", - "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vitepress": { - "version": "2.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-2.0.0-alpha.16.tgz", - "integrity": "sha512-w1nwsefDVIsje7BZr2tsKxkZutDGjG0YoQ2yxO7+a9tvYVqfljYbwj5LMYkPy8Tb7YbPwa22HtIhk62jbrvuEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@docsearch/css": "^4.5.3", - "@docsearch/js": "^4.5.3", - "@docsearch/sidepanel-js": "^4.5.3", - "@iconify-json/simple-icons": "^1.2.68", - "@shikijs/core": "^3.21.0", - "@shikijs/transformers": "^3.21.0", - "@shikijs/types": "^3.21.0", - "@types/markdown-it": "^14.1.2", - "@vitejs/plugin-vue": "^6.0.3", - "@vue/devtools-api": "^8.0.5", - "@vue/shared": "^3.5.27", - "@vueuse/core": "^14.1.0", - "@vueuse/integrations": "^14.1.0", - "focus-trap": "^7.8.0", - "mark.js": "8.11.1", - "minisearch": "^7.2.0", - "shiki": "^3.21.0", - "vite": "^7.3.1", - "vue": "^3.5.27" - }, - "bin": { - "vitepress": "bin/vitepress.js" - }, - "peerDependencies": { - "markdown-it-mathjax3": "^4", - "oxc-minify": "*", - "postcss": "^8" - }, - "peerDependenciesMeta": { - "markdown-it-mathjax3": { - "optional": true - }, - "oxc-minify": { - "optional": true - }, - "postcss": { - "optional": true - } - } - }, - "node_modules/vue": { - "version": "3.5.27", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.27.tgz", - "integrity": "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.27", - "@vue/compiler-sfc": "3.5.27", - "@vue/runtime-dom": "3.5.27", - "@vue/server-renderer": "3.5.27", - "@vue/shared": "3.5.27" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/web-resource-inliner": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", - "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.1", - "escape-goat": "^3.0.0", - "htmlparser2": "^5.0.0", - "mime": "^2.4.6", - "node-fetch": "^2.6.0", - "valid-data-url": "^3.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wicked-good-xpath": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", - "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/xmldom-sre": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/xmldom-sre/-/xmldom-sre-0.1.31.tgz", - "integrity": "sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==", - "dev": true, - "license": "(LGPL-2.0 or MIT)", - "engines": { - "node": ">=0.1" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 8e12216..0000000 --- a/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "scripts": { - "build": "vitepress build docs", - "dev": "vitepress dev docs" - }, - "devDependencies": { - "markdown-it-mathjax3": "^4.3.2", - "vitepress": "^2.0.0-alpha.16" - } -} diff --git a/practices/index.md b/practices/index.md deleted file mode 100644 index ee1fa00..0000000 --- a/practices/index.md +++ /dev/null @@ -1,22 +0,0 @@ -# 软件工程实践 - -- 免责声明 -- 概览 -- 开始你的第一个项目 - - 编程语言和平台的选择 - - 编程团队的构建 -- 需求与设计 - - 用户故事 - - 验收条件 - - 实例化需求 - - 领域专家 - - 项目制 -- 研发交付 - - 脚手架 - - 12 factors - - 代码规范 - - -- 质量保证与验收 -- 持续运维与改进 -- 节奏、工作空间、业余活动 -- 复杂项目实践 diff --git a/skills-lock.json b/skills-lock.json deleted file mode 100644 index c7b968e..0000000 --- a/skills-lock.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 1, - "skills": { - "avoid-ai-writing-zh": { - "source": "git@github.com:icimik/avoid-ai-writing-zh.git", - "sourceType": "git", - "skillPath": "SKILL.md", - "computedHash": "9551de6a50c30a7f8df7f8dd4b894d01b8b92a2bc1453ed9db85023493bf83e9" - } - } -} diff --git a/stream.html b/stream.html new file mode 100644 index 0000000..efcf4b9 --- /dev/null +++ b/stream.html @@ -0,0 +1,24 @@ + + + + + + 流 | 编程导引 + + + + + + + + + + + + +
Skip to content
On this page
+ + + + \ No newline at end of file