Включаем программу логического мышления!
Я в волшебном шаре вижу, что для тестирования нам понадобится функция генерации строки определённой длины:
Public Function genString(ByVal Length As Long) As String
Dim s As String
s = "123456789_"
Do While (Len(s) < Length)
s = s & s
Loop
genString = Left$(s, Length)
End Function
Первое что приходит в голову - стандартная конкатенация:
Public Function Replace1(ByVal Expression As String, ByVal Find As _
String, ByVal Replace As String, Optional ByVal Compare As _
VbCompareMethod = vbBinaryCompare) As String
Dim l As Long
Dim p As Long
Dim s As String
l = Len(Find)
If (l = 0) Then
Replace1 = Expression
Exit Function
End If
s = vbNullString
p = InStr(1, Expression, Find, Compare)
Do While (p)
s = s & Left$(Expression, p - 1) & Replace
Expression = Mid$(Expression, p + l)
p = InStr(1, Expression, Find, Compare)
Loop
Replace1 = s & Expression
End Function
Но! Функция InStr() использует параметр Start. Тогда, усложнив код, можно исключить модификацию строки Expression.
Public Function Replace2(ByVal Expression As String, ByVal Find _
As String, ByVal Replace As String, Optional ByVal Compare As _
VbCompareMethod = vbBinaryCompare) As String
Dim l As Long
Dim p1 As Long
Dim p2 As Long
Dim s As String
l = Len(Find)
If (l = 0) Then
Replace2 = Expression
Exit Function
End If
s = vbNullString
p1 = 1
p2 = InStr(p1, Expression, Find, Compare)
Do While (p2)
s = s & Mid$(Expression, p1, p2 - p1) & Replace
p1 = p2 + l
p2 = InStr(p1, Expression, Find, Compare)
Loop
Replace2 = s & Mid$(Expression, p1)
End Function
Проверим какой из вариантов работает быстрее. В качестве точки отсчёта используем стандартную функцию в VB6 Replace из стандарного же модуля VBA. Время выполнения наших функций сравнивается со временем выполнения стандартной функции.
Public Function test1(ByVal Length As Long, ByVal Find As String,_
ByVal Replace As String)
Dim s0$, s1$, s2$
Dim t0!, t1!, t2!
Dim Expression$
Expression = genString(Length)
t0 = Timer()
s0 = VBA.Replace(Expression, Find, Replace)
t0 = Timer() - t0
t1 = Timer()
s1 = Replace1(Expression, Find, Replace)
t1 = Timer() - t1
t2 = Timer()
s2 = Replace2(Expression, Find, Replace)
t2 = Timer() - t2
Debug.Print s0 = s1, s1 = s2, s2 = s0
Debug.Print t0, t1, t2
Debug.Print 1, t1 / t0, t2 / t0
End Function
Immediate:
?test1(100000, "456", "&&&&&&")
True True True
0,014875 4,14025 2,905625
1 278,3362 195,3361
?test1(100000, "456", "&&&")
True True True
0,015375 2,26475 1,421125
1 147,3008 92,43089
?test1(100000, "456", "&")
True True True
0,015125 1,4215 0,718625
1 93,98347 47,5124
В результате нехитрого тестирования получаем, что функция Replace2() выполняется примерно в полтора-два раза быстрее чем Replace1(), но в десятки и сотни раз медленнее чем стандарная VBA.Replace()
Далее в качестве базовой функции берём Replace2(). Зная о том, что Mid$() работает много быстрее конкатенации, усложняем алгоритм, надеясь на хоть какой-нибудь выигрыш во времени.
Public Function Replace3(ByVal Expression As String, ByVal Find As _
String, ByVal Replace As String, Optional ByVal Compare As _
VbCompareMethod = vbBinaryCompare) As String
Dim l As Long
Dim lenR As Long
Dim p1 As Long
Dim p2 As Long
Dim p21 As Long
Dim s As String
l = Len(Find)
If (l = 0) Then
Replace3 = Expression
Exit Function
End If
lenR = Len(Replace)
If (lenR > l) Then
s = Space$(Len(Expression) + (Len(Expression) \ l) * (lenR - l))
Else
s = Space$(Len(Expression))
End If
p21 = 1
p1 = 1
p2 = InStr(p1, Expression, Find, Compare)
Do While (p2)
Mid$(s, p21) = Mid$(Expression, p1, p2 - p1)
p21 = p21 + p2 - p1
Mid$(s, p21) = Replace
p21 = p21 + lenR
p1 = p2 + l
p2 = InStr(p1, Expression, Find, Compare)
Loop
Mid$(s, p21) = Mid$(Expression, p1)
p21 = p21 + Len(Mid$(Expression, p1))
s = Left$(s, p21 - 1)
Replace3 = s
End Function
Проверяем что получилось
Public Function test2(ByVal Length As Long, ByVal Find As String, _
ByVal Replace As String)
Dim s0$, s2$, s3$
Dim t0!, t2!, t3!
Dim Expression$
Expression = genString(Length)
t0 = Timer()
s0 = VBA.Replace(Expression, Find, Replace)
t0 = Timer() - t0
t2 = Timer()
s2 = Replace2(Expression, Find, Replace)
t2 = Timer() - t2
t3 = Timer()
s3 = Replace3(Expression, Find, Replace)
t3 = Timer() - t3
Debug.Print s0 = s2, s2 = s3, s3 = s0
Debug.Print t0, t2, t3
Debug.Print 1, t2 / t0, t3 / t0
End Function
Immediate:
?test2(100000, "456", "&&&&&&")
True True True
0,0155 2,936875 0,015375
1 189,4758 0,9919356
?test2(100000, "456", "&&&")
True True True
0,014875 1,46825 0,0155
1 98,70588 1,042017
?test2(100000, "456", "&")
True True True
0,015 0,890375 0,01475
1 59,35834 0,9833333
!!!!!! Вау!!! Ёпрст!!! Результат превзошёл все ожидания! Наша функция выполняется быстрее стандарной или почти так же! Но! Мы же помним о замене строк одинаковой длины. И не останавливаясь на достигнутом, ещё раз усложняем алгоритм, обрабатывая исключительную ситуацию совпадения длины замещаемой и замещающей строк.
Код:
Public Function Replace3_1(ByVal Expression As String, ByVal _
Find As String, ByVal Replace As String, Optional ByVal Compare _
As VbCompareMethod = vbBinaryCompare) As String
Dim l As Long
Dim lenR As Long
Dim p1 As Long
Dim p2 As Long
Dim p21 As Long
Dim s As String
l = Len(Find)
If (l = 0) Then
Replace3_1 = Expression
Exit Function
End If
lenR = Len(Replace)
If (lenR = l) Then
p1 = 1
p2 = InStr(p1, Expression, Find, Compare)
Do While (p2)
Mid$(Expression, p1) = Mid$(Expression, p1, p2 - p1)
Mid$(Expression, p2) = Replace
p1 = p2 + l
p2 = InStr(p1, Expression, Find, Compare)
Loop
Replace3_1 = Expression
Exit Function
ElseIf (lenR > l) Then
s = Space$(Len(Expression) + (Len(Expression) \ l) * (lenR - l))
Else
s = Space$(Len(Expression))
End If
p21 = 1
p1 = 1
p2 = InStr(p1, Expression, Find, Compare)
Do While (p2)
Mid$(s, p21) = Mid$(Expression, p1, p2 - p1)
p21 = p21 + p2 - p1
Mid$(s, p21) = Replace
p21 = p21 + lenR
p1 = p2 + l
p2 = InStr(p1, Expression, Find, Compare)
Loop
Mid$(s, p21) = Mid$(Expression, p1)
p21 = p21 + Len(Mid$(Expression, p1))
s = Left$(s, p21 - 1)
Replace3_1 = s
End Function
Быстренько тестируем
Код:
Public Function test3(ByVal Length As Long, ByVal Find As String, _
ByVal Replace As String)
Dim s0$, s3$, s4$
Dim t0!, t3!, t4!
Dim Expression$
Expression = genString(Length)
t0 = Timer()
s0 = VBA.Replace(Expression, Find, Replace)
t0 = Timer() - t0
t3 = Timer()
s3 = Replace3(Expression, Find, Replace)
t3 = Timer() - t3
t4 = Timer()
s4 = Replace3_1(Expression, Find, Replace)
t4 = Timer() - t4
Debug.Print s0 = s3, s3 = s4, s4 = s0
Debug.Print t0, t3, t4
Debug.Print 1, t3 / t0, t4 / t0
End Function
Длины строки в сто тысяч уже не достаточно для проверки достоверности результата поэтому, внимание!!!, увеличиваем длину до миллиона.
?test3(1000000, "456", "&&&&&&")
True True True
0,281 0,12475 0,12475
1 0,4439502 0,4439502
?test3(1000000, "456", "&&&")
True True True
0,29625 0,109375 0,078
1 0,3691983 0,2632912
?test3(1000000, "456", "&")
True True True
0,280625 0,109375 0,109
1 0,389755 0,3884187
Да, упорство и труд - всё перетрут! Как и ожидалось, функция работает при замене строк одинаковой длины быстрее! и почти в три раза быстрее стандарной!!! А если ещё оптимизировать функцию InStr()? Тогда не в два-три раза, а, быть может, в четыре-пять можно енто дело ускорить?
Вот так, решая совершенно левую задачу для уже почти мёртвой версии языка, получили результат превосходящий по скорости стандарные функции. Программистам в MS ещё есть чему поучиться.