真实项目与「别扭点清单」(主线 C)
主线 C 的定位是路线的事实来源:语言缺什么,不是坐在桌前想出来的, 而是写真实程序时撞出来的。本文件记录三件事—— 五个场景各有什么项目、行数比基线是多少、写的时候哪里别扭。
一、五个场景
| 场景 | 项目 | 干什么 | 用到的语言面 |
|---|---|---|---|
| 数据处理 | 成绩分析/ | 读 CSV,出总分排名、各科平均与最高、不及格名单 | 表格、文本、闭包、推导式、排序 |
| 数据处理 | 日志统计/ | 解析访问日志,出请求数、独立 IP、状态码分布、最热路径、404 IP | 文件、字典计数、排序、格式化 |
| 网页采集 | 网页采集/ | 抓网页 → html.取链接 提链接 → 存 CSV → 域名分布 | 网络、html、正则、表格 |
| CLI 工具 | 待办清单/ | 增删改查 + JSON 持久化 + 命令行参数 + 出错给人话 | 系统.参数、json、文件、时间 |
| 算法练习 | 排序跑分/ | 三种排序(冒泡/插入/快排)+ 用内建排序当裁判 + 规模-耗时表 | 递归、推导式、随机、时间 |
| 教学演示 / 站点生成 | 基石自述站/ | 用基石读仓库真数据,生成介绍基石的静态站 | 文件、路径(递归列出 / 按模式找)、文本、f-string |
另有 examples/tutorial/ 十课作为教学演示的入门线(01 你好 → 10 综合), 不与上表混算行数比。
每个项目都能直接跑,跑法见各自 README.md;tests/test_m15_projects.py 与 tests/test_m39_selfsite.py 把它们钉在测试里(含输入喂给、产物断言)。
二、行数比基线(M40 起点 → 现在)
口径:每个项目里 .jsh 是基石实现,参考实现.py 是只用标准库的 Python 实现,两者同功能、同输出口径;有效行 = 去空行、去纯注释行。 比值 = 基石有效行 ÷ Python 有效行,越小越「方便实现意图」。 表由 python tools/loc_ratio.py --markdown 现算,不是手抄的。
① M40 起点(留着看「降了多少」):
| 项目 | 场景 | 基石(有效行) | Python(有效行) | 行数比 | 总行比 |
|---|---|---|---|---|---|
| 基石自述站 | 教学演示 / 站点生成 | 299 | 238 | 1.26 | 1.26 |
| 待办清单 | CLI 工具 | 113 | 91 | 1.24 | 1.38 |
| 成绩分析 | 数据处理 | 63 | 52 | 1.21 | 1.17 |
| 排序跑分 | 算法练习 | 93 | 67 | 1.39 | 1.45 |
| 日志统计 | 数据处理 | 70 | 45 | 1.56 | 1.62 |
| 网页采集 | 网页采集 | 50 | 49 | 1.02 | 1.06 |
合计:基石 688 行 / Python 542 行 → 加权比值 1.27;逐项平均 1.28。
② 现在(2026-10-11,M108 时点):
| 项目 | 场景 | 基石(有效行) | Python(有效行) | 行数比 | 总行比 |
|---|---|---|---|---|---|
| 基石自述站 | 教学演示 / 站点生成 | 287 | 238 | 1.21 | 1.24 |
| 待办清单 | CLI 工具 | 108 | 91 | 1.19 | 1.33 |
| 成绩分析 | 数据处理 | 63 | 52 | 1.21 | 1.17 |
| 排序跑分 | 算法练习 | 82 | 67 | 1.22 | 1.29 |
| 日志统计 | 数据处理 | 53 | 45 | 1.18 | 1.26 |
| 网页采集 | 网页采集 | 48 | 49 | 0.98 | 1.03 |
合计:基石 641 行 / Python 542 行 → 加权比值 1.18(起点 1.27); 逐项平均 1.16(起点 1.28)。
📌 降幅最大的正是 M40 复盘里点名的两个:「不是中文不好写,而是缺几件标准库的 顺手工具」—— 日志统计 1.56 → 1.18、排序跑分 1.39 → 1.22。
这个数字不好看,但它是真的,而且是可复核的:口径写在上面,工具在 tools/loc_ratio.py,谁都能重算或换口径重算。它同时给出了 M40 起「逐档下降」 的起点——没有起点就没法说「下降了」。
三个值得注意的点:
网页采集已经破 1.0(0.98):M107 把「正则硬挖链接」换成了html.取链接,
基石这一侧反而比 Python 参考实现(html.parser 手写状态机)还短一行。
- 离得最远的是
日志统计(1.56)与排序跑分(1.39),原因很具体,见下节的
别扭点 3/4/5——不是「中文不好写」,而是缺几件标准库的顺手工具。
基石自述站从 1.26 降到 1.21:M108 把「数文件 / 按后缀找文件」那几段
手写循环换成了 路径.递归列出 / 路径.按模式找(少了 10 行有效行)。 它的行数里模板与 CSS 占大头(两边逐字相同),所以这一项更接近「逻辑行」的比较。
为了让基线诚实,tools/loc_ratio.py --check 会检查每个项目是否都配了 Python 参考实现;缺了就报错——避免「只挑对自己有利的项目算」。
三、别扭点清单(写完这批项目攒出来的)
每条都给结论:→ 语法(走准入三关)/ → 标准库 / → 工具 / 明确不做。
| # | 别扭点 | 现场 | 结论 | |
|---|---|---|---|---|
| 1 | ~~导入 文本 打断所有 f-string~~(✅ M40 已修:插值改用内部保留名 $文本):插值脱糖成 文本(值),模块「文本」正好遮蔽这个内建 | 写 日志统计 时想用 文本.左对齐,一导入 f-string 全报错「模块「文本」不能直接调用」 | → 语言(✅ M40 已修):给插值一个不会被遮蔽的内部转换(三宿主同步)。在那之前用 导入 文本 为 别名 | |
| 2 | ~~转换失败抛「类型错误」,而 Python 是 ValueError~~(✅ M41 已修:文本转不动抛值错误、类型不对才抛类型错误) | 整数("abc") 抛类型错误 → 写 捕获 值错误 接不住(成绩分析/分析.jsh 里就是这么写的,只是样例数据从没触发) | → 语言(✅ M41 已修):内建 整数/小数 与 "abc".转整数() 归到同一类;待办清单 的 捕获 异常 已收窄成 捕获 值错误 | |
| 3 | ~~缺 Counter 式的计数字典~~(✅ M41 已修:容器.计数 / 容器.最多 / 容器.按值排序) | 日志统计:Python Counter(...) 一行,基石要手写「有就加一、没有就置一」 + 自己排序 | → 标准库(✅ M41 已修):模块名是「容器」不是「集合」——集合 是内建函数名,同名模块会把内建遮蔽(正是 #1 那类坑) | |
| 4 | ~~缺 round 与格式化规格~~(✅ 核实:数学.四舍五入 与 文本.格式化("{:.2f}") 早就有了,是写项目时不知道) | 排序跑分 要自己写 小数位()(整数(x*100+0.5)/100);Python 一个 round(x, 2) 或 {x:.2f} | → 能力已有但不可发现:排序跑分 已改用标准库(删掉自己写的 小数位());真正缺的是「怎么知道有这个函数」 | |
| 5 | 下标目标不能解包赋值:结果[j], 结果[j+1] = … 解析失败 | 冒泡交换要写三行(临时变量) | → 语法候选(先记下,不急着做):属于「多目标赋值」的扩展,要走准入三关;目前三行写法不算难读 | |
| 6 | ~~没有递归列目录~~(✅ M41 已加 路径.递归列出(目录, 后缀),五路同宽) | 基石自述站 数 examples/ 下的 .jsh 要手写递归(Python 一个 rglob) | → 标准库(✅ M41 已加) | |
| 7 | ~~命令行「子命令 + 参数」要手写~~(✅ M41 已加 参数 模块) | 待办清单 自己解析 系统.参数()、自己打印用法 | → 标准库(✅ M41 已加):选项式解析(--名字 值 / --名字=值 / --标志)交给模块;待办清单 那种子命令式仍要自己分发(模块只管选项,不管子命令) | |
| 8 | ~~字符串对齐/补零在 文本 模块里很好用,却撞 #1~~(✅ 可用:排序跑分 已改用 文本.左对齐,用别名导入避开遮蔽提示) | 排序跑分 的表格对齐只好手写 对齐() | ✅ 已用上 | |
| 9 | 字典.获取(键, 默认) 很好用(真实项目里反复用) | — | 保持:这是正面例子,写进语言卡 | |
| 10 | 如果 … 否则 … 表达式(条件表达式)在数据整理里很顺手 | 令 标记 = "√" 如果 完成 否则 " " | 保持:M36 的脱糖路线收益在这里体现出来了 |
十条里只有 2 条与语法相关(#1 是「既有语法的正确性」、#5 是候选), 其余都是标准库能解决的——这与「语法面已冻结、新需求先问能不能用 标准库」的结论一致。
M41 收口后的复盘
十条里 7 条已解决(#1/#2/#3/#4/#6/#7/#8),1 条转成「能力已有但不可发现」 (#4/#8:数学.四舍五入、文本.左对齐、集合 的交并差早就存在,是写项目 时不知道),只有 #5(下标目标解包赋值)还留在语法候选。
这暴露了一类新问题:真实项目里卡住的未必是「语言缺能力」,也可能是 「有但找不到」。M42–M45 的两个外功方向(IDE 补全 / AI 语言事实)正好治它。
顺带补上的两条(清单之外)
遍历支持多变量解包(遍历 名字, 年龄 在 对们:):走解析期脱糖
(临时循环变量 + 循环体首句解包赋值),三执行器与两宿主零改动。 原先只有 令 甲, 乙 = … 支持解包,遍历 不支持——examples/ai/few-shot.md 的示例 34 就是这么写错的(见下条)。
examples/ai/few-shot.md48 段示例的体检:这些示例是喂给 AI 的「正确
写法」样本,但从没人跑过。逐段执行发现 4 段真错(用了不存在的语法或 API),已修;并加了 tests/test_m41_stdlib.py 的逐段测试钉住——片段 不自包含的按「环境缺失」放行,语法/API 错误必须报出来。
三之二、绕道探针与台账(M105 起,2026-10-11)
上面那十条是 M40 时代的清单 —— 写法是「写完项目再回头总结」。M105 起换了做法: 条目写在撞到它的那个地方(项目自己的 README.md 里),台账由脚本扫出来。 这样它不会与代码脱节(补了没更新、删了还留着)。
| 东西 | 在哪 | 说明 |
|---|---|---|
| 探针项目 | examples/绕道探针/ | 用现有标准库尽力写的真实小程序;不要求功能完整,所以不放在 examples/projects/,也不参与行数比 |
| 台账 | docs/绕道台账.md | 扫出来的,别手改 |
| 生成 / 自检 | python tools/detour_ledger.py | 加 --check 只自检、不写文件 |
| 判据 | tests/test_m105_detour_ledger.py | 台账与探针一致 · 自检真的会红 · 探针真能跑 |
⚠️ 台账会「过期报红」:某条写着「缺 文本.左对齐」,而那个函数已经有了时会自检失败 —— 逼着人去把条目消掉或改写,而不是让台账慢慢烂掉。 (同 AGENTS.md 的纪律:「豁免必须显式、可复核、会过期报红」。)
探针当前扫出 7 条,撞出来的东西与「值多少」都写在 docs/绕道台账.md 里。
四、怎么跑
bash# 各项目(在项目目录里跑,数据文件也在那儿)
jishi 分析.jsh # 成绩分析:用 成绩.csv
jishi 统计.jsh # 日志统计:用 访问.log
jishi 采集.jsh # 网页采集:会问网址
jishi 待办.jsh 列出 # 待办清单
jishi 排序.jsh # 排序跑分
jishi 生成.jsh # 基石自述站:生成到 site/基石自述/
# 行数比基线(本项目根目录)
python tools/loc_ratio.py
python tools/loc_ratio.py --markdown # 贴文档用的 Markdown 表
python tools/loc_ratio.py --check # 检查每个项目都配了 Python 参考实现
三个执行器都能跑这些项目(jishi 脚本.jsh --执行器 vm);基石自述站 的产物 在三个执行器下逐字节相同(tests/test_m39_selfsite.py)。