Skip to content

Commit a9e5fcc

Browse files
author
linzhijun
committed
fix
1 parent 74234bf commit a9e5fcc

12 files changed

Lines changed: 198 additions & 101 deletions

File tree

‎JavaScript/src/Internals/Functions/Csharp/Function_LOOKCEILING.js‎

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,25 @@ export class Function_LOOKCEILING extends Function_2 {
3232
FunctionUtil.F_base_GetList(args2.ArrayValue, list);
3333
if (list.length === 0) { return this.parameterError(2); }
3434

35-
list.sort((a, b) => b - a);
35+
list.sort((a, b) => a - b);
3636
let value = args1.NumberValue;
37-
let result = list[0];
38-
if (result === value) { return args1; }
3937

40-
for (let i = 1; i < list.length; i++) {
41-
let val = list[i];
42-
if (val > value) {
43-
result = val;
44-
} else if (val === value) {
45-
return args1;
38+
// 二分查找第一个 >= value 的位置
39+
let lo = 0, hi = list.length;
40+
while (lo < hi) {
41+
const mid = (lo + hi) >> 1;
42+
if (list[mid] < value) {
43+
lo = mid + 1;
4644
} else {
47-
break;
45+
hi = mid;
4846
}
4947
}
50-
return Operand.Create(result);
48+
if (lo < list.length && list[lo] === value) { return args1; }
49+
if (lo === list.length) {
50+
// 所有元素都小于 value,返回最大值
51+
return Operand.Create(list[list.length - 1]);
52+
}
53+
return Operand.Create(list[lo]);
5154
}
5255

5356

‎JavaScript/src/Internals/Functions/Csharp/Function_LOOKFLOOR.js‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,23 @@ export class Function_LOOKFLOOR extends Function_2 {
3434

3535
list.sort((a, b) => a - b);
3636
let value = args1.NumberValue;
37-
let result = list[0];
38-
if (result === value) { return args1; }
3937

40-
for (let i = 1; i < list.length; i++) {
41-
let val = list[i];
42-
if (val < value) {
43-
result = val;
44-
} else if (val === value) {
45-
return args1;
38+
// 二分查找第一个 >= value 的位置
39+
let lo = 0, hi = list.length;
40+
while (lo < hi) {
41+
const mid = (lo + hi) >> 1;
42+
if (list[mid] < value) {
43+
lo = mid + 1;
4644
} else {
47-
break;
45+
hi = mid;
4846
}
4947
}
50-
return Operand.Create(result);
48+
if (lo < list.length && list[lo] === value) { return args1; }
49+
if (lo === 0) {
50+
// 所有元素都大于 value,返回最小值
51+
return Operand.Create(list[0]);
52+
}
53+
return Operand.Create(list[lo - 1]);
5154
}
5255

5356

‎JavaScript/src/Internals/Functions/String/Function_SEARCH.js‎

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,41 +39,82 @@ class Function_SEARCH extends Function_3 {
3939

4040
/**
4141
* 在指定起始位置起做大小写不敏感的通配符查找,支持 Excel 的 ? 与 * 及 ~ 转义。
42+
* 使用手写匹配器替代正则,避免每次调用都编译正则的开销。
4243
*/
4344
wildcardIndexOf(text, pattern, startIndex) {
44-
const regex = new RegExp(this.wildcardToRegex(pattern), 'ig');
45-
regex.lastIndex = startIndex;
46-
const match = regex.exec(text);
47-
if (!match) { return -1; }
48-
return match.index;
45+
if (startIndex < 0) { startIndex = 0; }
46+
if (startIndex > text.length) { return -1; }
47+
48+
const tokens = this.parseWildcardPattern(pattern);
49+
if (tokens.length === 0) { return startIndex; }
50+
51+
for (let start = startIndex; start <= text.length; start++) {
52+
if (this.matchWildcard(text, tokens, start)) {
53+
return start;
54+
}
55+
}
56+
return -1;
4957
}
5058

51-
wildcardToRegex(pattern) {
52-
const sb = [];
59+
parseWildcardPattern(pattern) {
60+
const list = [];
5361
for (let i = 0; i < pattern.length; i++) {
5462
const c = pattern[i];
5563
if (c === '~') {
5664
if (i + 1 < pattern.length && (pattern[i + 1] === '?' || pattern[i + 1] === '*' || pattern[i + 1] === '~')) {
57-
sb.push(this.escapeRegExpChar(pattern[i + 1]));
65+
list.push({ kind: 'literal', value: pattern[i + 1].toUpperCase() });
5866
i++;
5967
} else {
60-
sb.push(this.escapeRegExpChar('~'));
68+
list.push({ kind: 'literal', value: '~' });
6169
}
6270
} else if (c === '*') {
63-
sb.push('[\\s\\S]*');
71+
list.push({ kind: 'anySeq' });
6472
} else if (c === '?') {
65-
sb.push('[\\s\\S]');
73+
list.push({ kind: 'anyOne' });
6674
} else {
67-
sb.push(this.escapeRegExpChar(c));
75+
list.push({ kind: 'literal', value: c.toUpperCase() });
6876
}
6977
}
70-
return sb.join('');
78+
return list;
7179
}
7280

73-
escapeRegExpChar(c) {
74-
return /[.*+?^${}()|[\]\\]/.test(c) ? '\\' + c : c;
81+
matchWildcard(text, tokens, start) {
82+
let ti = 0;
83+
let si = start;
84+
let starToken = -1;
85+
let starMatch = 0;
86+
87+
while (ti < tokens.length) {
88+
if (si < text.length) {
89+
const tok = tokens[ti];
90+
if (tok.kind === 'literal') {
91+
if (text[si].toUpperCase() === tok.value) {
92+
ti++;
93+
si++;
94+
} else if (starToken !== -1) {
95+
ti = starToken + 1;
96+
si = ++starMatch;
97+
} else {
98+
return false;
99+
}
100+
} else if (tok.kind === 'anyOne') {
101+
ti++;
102+
si++;
103+
} else {
104+
starToken = ti;
105+
starMatch = si;
106+
ti++;
107+
}
108+
} else {
109+
break;
110+
}
111+
}
112+
113+
while (ti < tokens.length && tokens[ti].kind === 'anySeq') {
114+
ti++;
115+
}
116+
return ti === tokens.length;
75117
}
76118
}
77119

78120
export { Function_SEARCH };
79-

‎JavaScript/src/Internals/Functions/String/Function_SUBSTITUTE.js‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class Function_SUBSTITUTE extends Function_4 {
1818
let args3 = this.getText_3(work, tempParameter);
1919
if (args3.IsError) { return args3; }
2020
if (this.d === null || this.d === undefined) {
21+
if (args2.TextValue.length === 0) {
22+
return Operand.Create(args1.TextValue);
23+
}
2124
return Operand.Create(args1.TextValue.replace(new RegExp(args2.TextValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), args3.TextValue));
2225
}
2326
let args4 = this.getNumber_4(work, tempParameter);
@@ -31,6 +34,9 @@ class Function_SUBSTITUTE extends Function_4 {
3134
if (oldtext.length === 0) {
3235
return Operand.Create(text);
3336
}
37+
if (index < 1) {
38+
return this.parameterError(4);
39+
}
3440

3541
let foundCount = 0;
3642
let searchPos = 0;

‎csharp/ToolGood.Algorithm.Test/AlgorithmEngine/StringTest.cs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,15 @@ public void SubstituteBoundary_Test()
624624

625625
t2 = engine.TryEvaluate("SUBSTITUTE('aabaa', 'a', 'b', 2)", "");
626626
Assert.AreEqual(t2, "abbaa");
627+
628+
// old_text 为空字符串时返回原文本
629+
t2 = engine.TryEvaluate("SUBSTITUTE('abc', '', 'x')", "");
630+
Assert.AreEqual(t2, "abc");
631+
632+
// instance_num 小于 1 时返回 #VALUE! 错误
633+
t2 = engine.TryEvaluate("SUBSTITUTE('abc', 'a', 'x', 0)", "");
634+
Assert.AreEqual(t2, "");
635+
Assert.IsNotNull(engine.LastError);
627636
}
628637

629638
#endregion 边界值测试

‎csharp/ToolGood.Algorithm/Internals/Functions/String/Function_SUBSTITUTE.cs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public override Operand Evaluate(AlgorithmEngine engine, Func<AlgorithmEngine, s
2525
var args3 = GetText_3(engine, tempParameter);
2626
if (args3.IsErrorOrNone) { return args3; }
2727
if (func4 == null) {
28+
if (args2.TextValue.Length == 0) {
29+
return Operand.Create(args1.TextValue);
30+
}
2831
return Operand.Create(args1.TextValue.Replace(args2.TextValue, args3.TextValue));
2932
}
3033
var args4 = GetNumber_4(engine, tempParameter);
@@ -37,8 +40,11 @@ public override Operand Evaluate(AlgorithmEngine engine, Func<AlgorithmEngine, s
3740
if (oldtext.Length == 0) {
3841
return Operand.Create(text);
3942
}
43+
if (replaceIndex < 1) {
44+
return ParameterError(4);
45+
}
4046

41-
int estimatedCapacity = Math.Max(text.Length, text.Length + (newtext.Length - oldtext.Length));
47+
int estimatedCapacity = Math.Max(0, text.Length + newtext.Length - oldtext.Length);
4248
var sb = new StringBuilder(estimatedCapacity);
4349
int currentIndex = 0;
4450
int foundCount = 0;
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
1-
using Antlr4.Runtime;
2-
using System.IO;
3-
4-
namespace ToolGood.Algorithm.Internals.Visitors
1+
namespace ToolGood.Algorithm.Internals.Visitors
52
{
63

7-
internal sealed class AntlrErrorData //: IAntlrErrorListener<int>
4+
internal sealed class AntlrErrorData
85
{
96
public bool IsError { get; set; }
107
public string ErrorMsg { get; set; }
11-
12-
//public void SyntaxError(TextWriter output, IRecognizer recognizer, int offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e)
13-
//{
14-
// IsError = true;
15-
// ErrorMsg = msg;
16-
//}
178
}
189
}

‎csharp/ToolGood.Algorithm/Internals/Visitors/AntlrErrorListener.cs‎

Lines changed: 0 additions & 14 deletions
This file was deleted.

‎java/toolgood.algorithm/src/main/java/toolgood/algorithm/internals/functions/csharp/Function_LOOKCEILING.java‎

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,16 @@ public Operand Evaluate(AlgorithmEngine engine, BiFunction<AlgorithmEngine, Stri
5151
Collections.sort(list);
5252

5353
BigDecimal value = args1.NumberValue();
54-
BigDecimal result = list.get(list.size() - 1);
55-
if (result.compareTo(value) == 0) {
54+
int index = Collections.binarySearch(list, value);
55+
if (index >= 0) {
5656
return args1;
5757
}
58-
for (int i = list.size() - 2; i >= 0; i--) {
59-
BigDecimal val = list.get(i);
60-
if (val.compareTo(value) > 0) {
61-
result = val;
62-
} else if (val.compareTo(value) == 0) {
63-
return args1;
64-
} else {
65-
break;
66-
}
58+
index = -index - 1; // 第一个大于 value 的索引
59+
if (index == list.size()) {
60+
// 所有元素都小于 value,返回最大值
61+
return Operand.Create(list.get(list.size() - 1));
6762
}
68-
return Operand.Create(result);
63+
return Operand.Create(list.get(index));
6964
}
7065

7166
@Override

‎java/toolgood.algorithm/src/main/java/toolgood/algorithm/internals/functions/csharp/Function_LOOKFLOOR.java‎

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,16 @@ public Operand Evaluate(AlgorithmEngine engine, BiFunction<AlgorithmEngine, Stri
5151
Collections.sort(list);
5252

5353
BigDecimal value = args1.NumberValue();
54-
BigDecimal result = list.get(0);
55-
if (result.compareTo(value) == 0) {
54+
int index = Collections.binarySearch(list, value);
55+
if (index >= 0) {
5656
return args1;
5757
}
58-
for (int i = 1; i < list.size(); i++) {
59-
BigDecimal val = list.get(i);
60-
if (val.compareTo(value) < 0) {
61-
result = val;
62-
} else if (val.compareTo(value) == 0) {
63-
return args1;
64-
} else /*if (val > value)*/ {
65-
break;
66-
}
58+
index = -index - 1; // 第一个大于 value 的索引
59+
if (index == 0) {
60+
// 所有元素都大于 value,返回最小值
61+
return Operand.Create(list.get(0));
6762
}
68-
return Operand.Create(result);
63+
return Operand.Create(list.get(index - 1));
6964
}
7065

7166
@Override

0 commit comments

Comments
 (0)