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

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.

主站蜘蛛池模板: 长寿区| 务川| 来凤县| 潼关县| 贺兰县| 仙桃市| 海门市| 黄大仙区| 阜康市| 新田县| 通化市| 屯昌县| 克什克腾旗| 石柱| 荆门市| 青龙| 桂东县| 荣昌县| 敦煌市| 文水县| 荃湾区| 长治县| 焦作市| 罗平县| 温泉县| 彰武县| 鄂州市| 安阳市| 冷水江市| 台江县| 哈密市| 云林县| 东丰县| 留坝县| 五峰| 商城县| 江阴市| 德化县| 鄂州市| 尚志市| 湘西|