官术网_书友最值得收藏!

Extracting common expressions

This next tip will sound obvious, but it will nicely introduce us to the next topic. Plus, it is a real problem frequently found in production code.

The ExtractCommonExpression demo creates a list box with a mere 1,000 entries, all in the form Author–Title. A click on the Complicated expression button runs a short code which reverts the order of Author and Title in the list box so that it shows entries in the form Title–Author:

procedure TfrmCommonExpression.Button1Click(Sender: TObject);
var
i: Integer;
sw: TStopwatch;
begin
ListBox1.Items.BeginUpdate;
try
sw := TStopwatch.StartNew;
for i := 0 to ListBox1.Count - 1 do
ListBox1.Items[i] :=
Copy(ListBox1.Items[i], Pos('-', ListBox1.Items[i]) + 1,
Length(ListBox1.Items[i]))
+ '-'
+ Copy(ListBox1.Items[i], 1, Pos('-', ListBox1.Items[i]) - 1);
sw.Stop;
Button1.Caption := IntToStr(sw.ElapsedMilliseconds);
finally ListBox1.Items.EndUpdate; end;
end;

The code goes over the list and for each entry finds the '-' character, extracts the first and second part of the entry and combines them back together, reversed. It does that, however, in a terrible copy-and-paste way. The code refers to ListBox1.Items[i] five times while calculating the result. It also calls Pos('-', ListBox1.Items[i]) twice.

In a language with a really good compiler, you could expect that both subexpressions mentioned in the previous paragraph would be calculated only once. Not with Delphi's compiler, though. It has some optimization built in, but Delphi's optimization is far from the level required for such tricks to work. That leaves a burden of optimization on us, the programmers.

The second button in this demo executes the code shown next. This implementation of the same algorithm is not only more readable, but accesses ListBox1.Items[i] only once. It also calculates the position of '-' inside the string only once:

procedure TfrmCommonExpression.Button2Click(Sender: TObject);
var
i: Integer;
s: string;
p: Integer;
sw: TStopwatch;
begin
ListBox1.Items.BeginUpdate;
try
sw := TStopwatch.StartNew;
for i := 0 to ListBox1.Count - 1 do begin
s := ListBox1.Items[i];
p := Pos('-', s);
ListBox1.Items[i] := Copy(s, p + 1, Length(s)) + '-' +
Copy(s, 1, p - 1);
end;
sw.Stop;
Button2.Caption := IntToStr(sw.ElapsedMilliseconds);
finally ListBox1.Items.EndUpdate; end;
end;

Comparing both approaches shows a definite improvement in the second case. The first method uses around 40 ms and the second one around 30 ms, which is 25% faster. The code only times the inner for loop, not the updating of the list box itself, which takes the same time in both cases.

I can recap all this with a simple statement—Calculate every subexpression only once. Good advice but, still, don't exaggerate. Simple expressions, such as i+1 or 2*i are so cheap (in computing time) that extracting them in a subexpression won't speed up the code.

主站蜘蛛池模板: 长宁县| 正蓝旗| 陈巴尔虎旗| 离岛区| 富民县| 平果县| 资溪县| 枣强县| 平阳县| 沙坪坝区| 汽车| 定结县| 保康县| 弥勒县| 宁海县| 马尔康县| 盐源县| 渭南市| 墨江| 玉树县| 昭苏县| 贞丰县| 高台县| 浏阳市| 阜阳市| 揭阳市| 隆安县| 渭南市| 聂拉木县| 象州县| 西平县| 绍兴县| 深水埗区| 城固县| 偃师市| 颍上县| 商都县| 上犹县| 车致| 界首市| 祁连县|