ARTS 1025: 唯一字符与同构字符串、省词可能增加阅读负担与重新估算软件的实现成本

每周完成一个 ARTS: 至少做一个 leetcode 的算法题、阅读并点评至少一篇英文技术文章、学习至少一个技术技巧、分享一篇有观点和思考的技术文章。(也就是 Algorithm、Review、Tips、Share 简称 ARTS)

Algorithm

这周因为都是简单难度的,所以来写两道算法题目:

1)第一道:LeetCode 387. 字符串中的第一个唯一字符

image-20261025153942993

这道题目住要的办法就是,先用 Map 拿到每一个 Charecter 出现的次数,然后根据 map 记录的内容进行返回 index。这有一点需要注意:HashMap 不保证元素的遍历顺序与插入顺序一致。如果需要按照插入顺序遍历元素,可以使用 LinkedHashMap

▼
java
复制代码
class Solution { public int firstUniqChar(String s) { Map<Character, Integer> map = new HashMap<>(); char[] chars = s.toCharArray(); for (char c : chars) { map.merge(c, 1, Integer::sum); } for (int i = 0; i < chars.length; i++) { if (map.getOrDefault(chars[i], null) == null || map.get(chars[i]) > 1) { continue; } return i; } return -1; } }

2)第二道:LeetCode 205. 同构字符串

image-20261025154541343

有几点需要注意:

  • 错误把“出现次数相同”当成了“同构”。不能只是单纯的判断,字母出现的数量进行对比。
  • 只检查了映射的一致性,漏掉了映射的唯一性。如果一个 a -> b a -> a 这样也不是同构的,所以这里我们使用 Set 进行判断。
▼
java
复制代码
class Solution { public boolean isIsomorphic(String s, String t) { if (s.length() != t.length()) { return false; } Map<Character, Character> charMap = new HashMap<>(); Set<Character> usedTargets = new HashSet<>(); for (int i = 0; i < s.length(); i++) { char sC = s.charAt(i); char tC = t.charAt(i); Character mapped = charMap.get(sC); if (mapped == null) { // tC 已经被其他字符使用,不能重复映射 if (!usedTargets.add(tC)) { return false; } charMap.put(sC, tC); } else if (mapped != tC) { return false; } } return true; } }

Review

论文:Why is AI-generated language so hard to understand?

作者提出一个假说:训练中压缩信息的压力,可能让模型更偏爱紧凑、省词的表达。这些表达在语法上说得通,却可能让读者花更多精力判断词义、记住前文、补出句子里的关系。

1、阅读时,读者需要不断预测后文、记住前文。

看到一个词时,读者已经在结合上下文判断它的意思。论文引用的心理语言学研究发现,越难预测的词,通常需要越长的阅读时间。词语搭配不常见,读者就更难顺着前文理解下去。

记忆也会影响阅读。如果主语和对应的动词隔得很远,读到动词时,就得找回前面的主语,再把两者联系起来。相互关联的词之间隔了多远,称为「依存距离」。一句话里需要暂时记住的信息越多,理解起来就越费力。所以,判断句子是否好读,不能只看字数,还要看读到哪里才能确定它在说什么。

2、论文检查了六类可能增加阅读负担的表达。

下面结合例子说明,其中的中文例子是为帮助理解补充的。

罕见的词语搭配,可能让人每个词都认识,连起来却不熟悉。例如,论文开头的 reader-patience budget,字面上是「读者耐心预算」。读者需要先理解这里把耐心比作一种会被消耗的预算,才能明白整个词组。写成「读者愿意花多少耐心读下去」,含义就直接一些。

长距离依存,会让读者更久地记住还没读完整的内容。例如,「负责处理缓存失效后大量请求同时访问数据库问题的服务需要重启」,读到「服务」时,才知道前面一大段都在修饰它,接着还得把它和「需要重启」联系起来。可以拆成:「这个服务负责处理缓存失效后大量请求同时访问数据库的问题。现在需要重启它。」先交代服务的用途,再说要做什么,读者就不用把整串修饰语一直记到句尾。

宾语关系从句中的省略,可能让句子少一些结构提示。例如,The patch the engineer reviewed failed. 的意思是「工程师审查过的补丁失败了」。句子的主干是 The patch failed.,中间的从句说明补丁被谁审查过,the patch 是 reviewed 的宾语。写成 The patch that the engineer reviewed failed.,多出的 that 标出了从句的开始。省去它也符合语法,但读者需要更依赖后文来判断结构。相关研究发现,人类会根据后续内容是否容易预测,决定要不要省略这类词。

名词堆叠,会省去概念之间的关系。例如,「缓存失效回源并发保护策略」把几个概念连在一起,读者需要自己判断哪些是条件、动作和目的。可以展开成:「缓存失效后,请求会访问数据库。为避免大量请求同时回源,可以限制同时回源的请求数。」多出来的文字,把原来需要读者推断的关系写了出来。

名词转动词,也能把原本需要几个词说明的动作缩短。例如,把表示日历的 calendar 当作动词,就可以表达「把某件事记入日历」。这类用法依赖说话者和读者对情境的共同理解。如果上下文不够充分,读者就得先猜这个词在当前句子里指什么动作。

还有一类是让没有生命的事物去做通常由人或动物完成的动作。例如,论文中的 this asymmetry bites,需要把「咬」理解成「造成问题」。在技术说明中,仅写「这个不对称性会反咬一口」,读者还得继续追问它具体影响什么。直接说明它会导致什么错误,更方便判断影响范围。名词转动词和拟人表达都可以正常使用,是否难读,还取决于读者对这种用法的熟悉程度,以及上下文是否充分。

3、训练目标可能强化了这些表达偏好。

作者推测,数学、代码等后训练任务可以奖励答案正确,却未必奖励读者是否容易理解。再加上上下文长度和 token 成本的限制,模型可能逐渐偏向用更少的词表达更多信息。

放到文档里看,省掉连接词、把动作写成名词、把背景放进一长串修饰语,确实可能缩短输出。但条件和因果关系不会因此消失,它们只是需要读者自己还原。对模型来说省下了词,对读者来说却可能多了一步推断。

人类的表达会随语境调整:容易预测的内容可以省略,新信息多的地方则需要更多提示。前面是否保留 that 的例子,就说明了这种取舍。省略是否合适,要看读者能不能顺着上下文理解下去。

对我们的启发就是,在写技术笔记时,可以留意那些需要读者自己补全的地方:主语是否明确,修饰语能否拆开,几个名词之间是什么关系,隐喻具体指什么行为和结果。让 AI 润色时,也可以要求保留条件和因果关系,再根据读者已有的知识,决定哪些内容可以省略。

Tips

1、macOS 使用 FineTune 可以管理不同软件得声音,非常实用。开源地址: https://github.com/ronitsingh10/FineTune

2、epub 阅读器,可以使用这个开源项目:https://github.com/readest/readest

3、继续学习 Rust 相关的内容,学到 Lifetimes 了,继续冲

Share

文章:We should be more ambitious with the software we build

Dan Luu 提出了一个值得重新考虑的问题:有了 AI 编程工具之后,哪些过去因为成本太高而放弃的事情,现在已经值得做了?他更倾向于说「可以更有野心」,因为工具能力的变化,让开发者有机会尝试以前做不起的软件。

1、判断一个项目值不值得做,需要重新估算实现成本。

过去,嫌编译慢就自己改编译器,嫌搜索慢就自己写正则引擎,通常意味着投入大量时间,还可能耽误原本要完成的工作。因此,优先使用现成工具、避免重复造轮子,是很合理的选择。

但 Dan Luu 发现,有些判断已经需要更新。他针对自己的使用场景,尝试了正则引擎、ripgrep 分支和 Rust 编译加速等项目,投入的个人时间比过去少了很多。开发者可能还沿用着没有编程 Agent 时的成本估计,连试一下都觉得不划算。

这里也要保留作者给出的范围:这些工具主要达到了「自己能用」的程度,做成可靠的公共项目仍然需要更多工作,文中也没有提供完整、严谨的性能评测。这些经历更适合用来说明尝试的成本发生了变化,具体收益还要在实际场景中测量。

2、值得尝试的软件,也可以只解决一个很小的需求。

文章中有个例子很有意思:Dan Luu 一直想玩一款还原小说中太空战斗方式的游戏,但这种玩法很小众。过去,花十几个小时开发,再玩一两个小时,并不划算;当他只需要花几分钟安排 Agent 实现时,这件事就值得做了。

这种变化也适用于日常工具。比如,一个脚本只能省下每次几分钟的手动操作,过去因为开发和维护太麻烦,一直没有人写。现在可以先做一个范围很小的版本,用几次看看是否方便,再决定要不要继续完善。软件只供几个人使用,也可能有足够的价值。

3、项目能做得更大,验证也得跟得上。

Dan Luu 用 pgrust 数据库项目说明,有质量问题的 AI 项目很多,但认真测试的项目仍然能产生价值。按他的介绍,pgrust 的测试过程甚至找出了 PostgreSQL 本身的 Bug,即使这个项目最终没有成功,这些发现也能帮助已有软件改进。

不过,作者并没有得出「软件开发已经解决了」的结论。他发现,模型能实现的东西越来越多,但在缺少人工指导时,能够有效验证的范围没有同步跟上。尝试更复杂的项目之后,人仍然需要判断实现是否符合预期。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
leikooo
作者分享
让 AI 改代码,有时候会遇到一个问题:需求完成了,但多了几个 if;再补一个需求,又加了几个特殊判断。改的时候都说得通,回头看,代码已经绕得有点看不懂了😭。 最近发现 Cursor 的 cursor-team-kit 里有个 skill,叫 thermo-nuclear-code-quality-review,用下来效果挺不错。名字挺狠,可以翻译成「热核级代码质量审查」。它会让 AI 更严格地检查代码,看看哪些地方越改越乱,有没有更简单的写法。 平时让 AI 做 code review,我们往往更关心有没有 bug,边界情况有没有漏掉。这个 skill 会把后续维护也作为审查重点:功能没问题,但下次加需求是不是又得四处补判断?别人接手时,能不能看懂这段代码?如果能在不改变功能的前提下减少分支、去掉多余的封装,它就会要求把这些地方改得更简单。 它查得比较细:有没有到处补条件判断,有没有封装了一层却什么也没简化,业务代码是不是放错了地方。如果这次修改让一个文件从不到 1000 行涨到超过 1000 行,也会要求检查是否该拆分,有充分理由才保留。 安装可以直接交给 AI。在支持安装 skill 的编程工具里,把 GitHub 地址发过去就行: > 帮我安装这个 skill:https://github.com/cursor/plugins/blob/main/cursor-team-kit/skills/thermo-nuclear-code-quality-review/SKILL.md 装好之后,写完一轮代码,让它用这个 skill 检查一下。这时最好说清楚审查范围,比如: > 用 thermo-nuclear-code-quality-review 检查当前分支相对 main 的改动,保存到 docs 目录下面的文件中 最后,如果条件允许,改完还可以换另一家的模型复查一次,把修改后的代码和需求一起给它,看看有没有遗漏,或者为了简化代码改掉了原本的行为。多次校验一下,这样上线才更稳定一些。
8
ARTS 1101: 最长回文串与子序列匹配、一次 URL 请求的来回之旅与如何验证 AI 的交付
6
IP 数据库体积缩小 31%,关键竟然是合并重复子树
4
提到用 Rust 重写项目,很多人的第一反应往往是性能提升。以 Bun 为例,它最初主要采用 Zig 开发,后来在 2026 年发布的 v1.4 版本中将核心代码迁移到了 Rust。用 Rust 重写,性能、安全性往往是大家想要的,但是有没有想过用 Rust 重写还能发现 bug 呢? pgrust 这个项就是,他用 Rust 重新实现 PostgreSQL,保持通信协议和 SQL 方言兼容。为了确认新系统的行为和原版一致,开发者需要对着 PostgreSQL 的实际返回逐个比对。结果查着查着,有些异常不是新代码的锅,PostgreSQL 自己就是这样的。pgrust 开发者 Michael Malis 把其中几个报告给了 PostgreSQL 社区。 大概的问题: 1)SP-GiST 空间索引里有个函数判断点的方位,代码认为上、下、同一水平线三种情况已经穷尽了,末尾放了句「impossible case」兜底。结果在特定浮点边界附近,三种判断同时不成立,程序真走进了这个「不可能」的分支 2)日期格式缓存复用时,一个解析模式的标记没重置,导致同一条 JSONPath 查询在同一个会话里,换个执行顺序结果就不一样。 3)pgcrypto 的 crypt() 在某些输入下截断了多字节字符,返回的文本编码不合法。能存进去,但 pg_dump 导出时就炸了。 一个用 Rust 重写的新项目,测着测着,把运行了几十年的 PostgreSQL 里埋着的东西翻了出来。Rust 重写的价值,不一定只在跑得更快、更安全这一件事上。为了做到真正的兼容,开发者得把前人没怎么碰过的边界全走一遍,这个过程本身就在帮原版排雷。
4
🎉 1024 程序员节快乐! 祝编程导航里的每一位程序员:offer 多多,薪资翻倍,面试顺利,早日拿下心仪 offer!💻
17
下载 APP