Skip to content

Commit af1be5b

Browse files
da-liiiclaude
andauthored
[1188] lolly replace 小修:整段拷贝 + 空模式守卫 (#4255)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 947b6b3 commit af1be5b

3 files changed

Lines changed: 59 additions & 6 deletions

File tree

devel/1188.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# 1188 lolly replace 小修:整段拷贝 + 空模式守卫
2+
3+
## 背景
4+
5+
lolly 的 `replace``lolly/Kernel/Types/analyze.cpp:899`)经
6+
`src/Scheme/L2/glue_lolly.lua``string-replace` 暴露给 Scheme,
7+
mogan 全项目约 120 个调用点。现状两个问题:
8+
9+
1. 未命中区段逐字符 `r << s[i]` 拷贝,命中之间的长区段没有批量拷贝;
10+
2. `what` 为空串时 `i += N (what)` 步进为 0,死循环(目前靠调用方自觉不传空串)。
11+
12+
## 2026-08-08 实现
13+
14+
### What
15+
16+
- `replace``what` 时原样返回 `s`
17+
- 未命中区段改为记录起点、命中时整段 `s(start, i)` 拷贝。
18+
19+
### Why
20+
21+
消除逐字符 append 的常数开销;堵死空模式死循环隐患。
22+
23+
### How
24+
25+
- 行为契约变化仅一处:`replace (s, "", by)` 由死循环变为返回 `s`
26+
(与 `contains (s, "") == true` 的现状不冲突,替换语义选「空模式不匹配任何位置」)。
27+
- 先补 `lolly/tests/Kernel/Types/analyze_test.cpp``replace` 用例
28+
(无命中、首尾命中、相邻命中、空替换串、空模式守卫),再改实现。
29+
- 注意:空模式用例在旧实现上会死循环,无法用于「改前红」验证,只能改后验证。
30+
31+
### 涉及文件
32+
33+
- `lolly/Kernel/Types/analyze.cpp`
34+
- `lolly/tests/Kernel/Types/analyze_test.cpp`

lolly/Kernel/Types/analyze.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -897,17 +897,20 @@ overlapping (string s1, string s2) {
897897

898898
string
899899
replace (string s, string what, string by) {
900-
int i, n= N (s);
900+
int i, n= N (s), k= N (what);
901+
// 空模式按「不匹配任何位置」处理,否则下方 i+= k 步进为 0 会死循环
902+
if (k == 0) return s;
901903
string r;
904+
int start= 0;
902905
for (i= 0; i < n;)
903906
if (test (s, i, what)) {
907+
r << s (start, i);
904908
r << by;
905-
i+= N (what);
906-
}
907-
else {
908-
r << s[i];
909-
i++;
909+
i+= k;
910+
start= i;
910911
}
912+
else i++;
913+
r << s (start, n);
911914
return r;
912915
}
913916

lolly/tests/Kernel/Types/analyze_test.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,22 @@ TEST_CASE ("contains/occurs") {
163163
TEST_CASE ("replace") {
164164
CHECK_EQ (replace ("a-b", "-", "_") == "a_b", true);
165165
CHECK_EQ (replace ("a-b-c", "-", "_") == "a_b_c", true);
166+
// 无命中:内容不变
167+
CHECK_EQ (replace ("abc", "-", "_") == "abc", true);
168+
CHECK_EQ (replace ("", "-", "_") == "", true);
169+
// 首尾命中与相邻命中
170+
CHECK_EQ (replace ("-a-", "-", "_") == "_a_", true);
171+
CHECK_EQ (replace ("a--b", "-", "_") == "a__b", true);
172+
// 替换串比模式长/短/为空
173+
CHECK_EQ (replace ("a-b", "-", "<->") == "a<->b", true);
174+
CHECK_EQ (replace ("a<->b", "<->", "-") == "a-b", true);
175+
CHECK_EQ (replace ("a-b", "-", "") == "ab", true);
176+
// 多字符模式不重叠匹配:从左到右,命中后跳过整个模式
177+
CHECK_EQ (replace ("aaaa", "aa", "b") == "bb", true);
178+
CHECK_EQ (replace ("aaa", "aa", "b") == "ba", true);
179+
// 空模式守卫:原样返回,不死循环
180+
CHECK_EQ (replace ("abc", "", "_") == "abc", true);
181+
CHECK_EQ (replace ("", "", "_") == "", true);
166182
}
167183

168184
TEST_CASE ("tokenize") {

0 commit comments

Comments
 (0)