В спешке не нашёл функцию которая возвращала бы подстроку по абсолютной адресации (с позиции А до Б) и пришлось делать срезами:
function f1(s:string;a,b:cardinal):=s[a:b+1];
а на днях проверил через Substring:
function f2(s:string;a,b:cardinal):=(b<a)or(a<1)or(b>s.Length)?'':s.Substring(a-1,b-a+1);
и не смотря на корявость, на 100 000 замерах она оказалась минимум в четыре раза быстрее.
Это так и есть или скорее нюанс Milliseconds|Delta ?
Summary
var s:='1234567890';
var xx:string;
var a,b:cardinals; //вынес из циклов для объективности
// loop 100 do begin
milliseconds; loop 100000 do for a := 1 to s.Length do for b := 1 to s.Length do xx := f1(s, a, b);
print(MillisecondsDelta, ':');
milliseconds; loop 100000 do for a := 1 to s.Length do for b := 1 to s.Length do xx := f2(s, a, b);
println(MillisecondsDelta);
//end;
uses System;
uses System.Diagnostics;
function f1(s: string; a, b: integer) := s[a:b + 1];
function f2(s: string; a, b: integer) := (b < a) or (a < 1) or (b > s.Length) ? '' : s.Substring(a - 1, b - a + 1);
begin
var str := '123456789' * 1000;
var t1 := new Stopwatch();
var t2 := new Stopwatch();
var count := 20;
var value := 5000;
loop count do
begin
t1.Start();
loop value do f1(str, 1, str.Length - 1);
t1.Stop;
t2.Start();
loop value do f2(str, 1, str.Length - 1);
t2.Stop;
end;
Seq(t1, t2).Select(x -> (x.ElapsedTicks / count) / TimeSpan.TicksPerSecond).PrintLines;
$'f2 быстрее f1 в {t1.ElapsedTicks / t2.ElapsedTicks} раз'.Println;
end.
У меня разница в 14 раз в среднем. А в режиме отладки вообще 25, потому что там срезы долго работают.
function CreateSliceFromStringInternal(Self: string; from, step, count: integer): string;
begin
var res := new StringBuilder(count);
loop count do
begin
res.Append(Self[from]);
from += step;
end;
Result := res.ToString;
end;
(я тоже хотел написать, но тут у меня сдох инет… ну, шоб уже зря не пропадало:)
MillisecondsDelta никогда не было сильно точным. Для замеров времени правильно использовать StopWatch.
Это не правильно. f2("abc",-2,2) вернёт пустую строку вместо "ab". А вообще самый правильный вариант - кидать исключение, что уже делает .Substring.
Все особые случаи, как выход индекса за границы - надо просчитывать на стороне, вызывающей f2.
Иначе будете потом ловить неуловимые баги, потому что забыли какую то мелочь, на которую вам сразу показало бы исключением.
Что, собственно, вы тестируете? a=0<1, а значит ваша f2 проверяет всего 2 условия и больше ничего не делает.
function f1(s: string; a, b: cardinal) := s[a:b + 1];
function f2(s: string; a, b: cardinal) := (b < a) or (a < 1) or (b > s.Length) ? '' : s.Substring(a - 1, b - a + 1);
begin
var s := '1234567890';
var a, b: cardinal;
a := 2;
b := 7;
var lc1 := 1000;
var lc2 := 10000;
var sw1 := new System.Diagnostics.Stopwatch;
var sw2 := new System.Diagnostics.Stopwatch;
for var i := 1 to lc1 do
begin
sw1.Start;
loop lc2 do f1(s, a,b);
sw1.Stop;
sw2.Start;
loop lc2 do f2(s, a,b);
sw2.Stop;
System.Console.Title := (i/lc1).ToString('P');
end;
Writeln(sw1.Elapsed);
Writeln(sw2.Elapsed);
Readln;
end.
Ну и, запускать это надо в режиме без компиляции отладки и в Shift+F9.
Теперь результаты точные - f2 быстрее не в 4, а в 5 раз!
А вообще, тестить скорость мало. В первую очередь надо было посмотреть на реализацию срезов:
function SystemSliceStringImpl(Self: string; situation: integer; from, &to: integer; step: integer := 1): string;
begin
var fromv := from - 1;
var tov := &to - 1;
var count := CheckAndCorrectFromToAndCalcCountForSystemSlice(situation, Self.Length, fromv, tov, step);
Result := CreateSliceFromStringInternal(Self, fromv + 1, step, count)
end;
function CheckAndCorrectFromToAndCalcCountForSystemSlice(situation: integer; Len: integer; var from, &to: integer; step: integer): integer;
begin
// situation = 0 - все параметры присутствуют
// situation = 1 - from отсутствует
// situation = 2 - to отсутствует
// situation = 3 - from и to отсутствуют
if step = 0 then
raise new ArgumentException(GetTranslation(PARAMETER_STEP_MUST_BE_NOT_EQUAL_0));
if (situation = 0) or (situation = 2) then
if (from < 0) or (from > Len - 1) then
raise new ArgumentException(GetTranslation(PARAMETER_FROM_OUT_OF_RANGE));
if (situation = 0) or (situation = 1) then
if (&to < -1) or (&to > Len) then
raise new ArgumentException(GetTranslation(PARAMETER_TO_OUT_OF_RANGE));
CorrectFromTo(situation, Len, from, &to, step);
var count: integer;
if step > 0 then
begin
var cnt := &to - from;
if cnt <= 0 then
count := 0
else count := (cnt - 1) div step + 1;
end
else
begin
var cnt := from - &to;
if cnt <= 0 then
count := 0
else count := (cnt - 1) div (-step) + 1;
end;
Result := count;
end;
procedure CorrectFromTo(situation: integer; Len: integer; var from, &to: integer; step: integer);
begin
if step > 0 then
begin
case situation of
1: from := 0;
2: &to := Len;
3: (from, &to) := (0, Len)
end;
end
else
begin
case situation of
1: from := Len - 1;
2: &to := -1;
3: (from, &to) := (Len - 1, -1);
end;
end;
end;
function CreateSliceFromStringInternal(Self: string; from, step, count: integer): string;
begin
var res := new StringBuilder(count);
loop count do
begin
res.Append(Self[from]);
from += step;
end;
Result := res.ToString;
end;
Надеюсь, теперь нет вопроса почему они медленнее?))
Потому что аргументы нужных методов имеют тип int32. А в вашем случае будут ещё лишние преобразования. Хотя, конечно, на скорость это практически не влияет.
А вот это то, что мне в паскале нравится. Если число выходит за диапазон, то не вызывается исключение, а просто счёт идёт заново по диапазону. Конечно, неопытные программисты могут на этом попасться, но лично для меня куда важнее было писать:
Платформа .NET Framework предоставляет классы, которые позволяют осуществлять программный доступ к компилятор языка C#. Это может быть полезно, если требуется написать собственные средства компиляции кода. Эта статья содержит пример кода, который позволяет компилировать код с исходным текстом. Приложение позволяет либо просто сборки исполняемого файла или сборки исполняемого файла и запустить его. На форме отображаются все ошибки, возникающие во время процесса предварительной компиляции.
@Admin, мне нужен API компилятора PascalABC.NET. Требуется работать с ним напрямую.
Мне нужно логгировать список ошибок компиляции .pas файлов и выдавать в виде предупреждения в IDE, такой как Visual Studio/Rider. При каждой сборке проекта NETSquirrel все связанные .pas файлы должны перекомпилироваться.