Исходный код
function find7(S:string):integer;
var
MyArray : TStringList;
n,i,sum, Resultat : integer;
MyInt : String;
begin
MyArray := TStringList.Create;
Trim(S); n:= 0;
while(Length(s)>0) do begin
Trim(S);
if (Pos(' ',S)=0) then begin
MyArray.Insert(n,Copy(S,1,Length(S)));
break;
end else begin if(Copy(S,1,Pos(' ',S))=' ') then begin
Delete(S, 1, (Pos(' ',S)));
end else begin
MyArray.Insert(n,Copy(S,1,Pos(' ',S)));
Delete(S,1,(Pos(' ',S)));
n:=n+1;
end;
end;
end;
Resultat := 0;
for i := 0to MyArray.Count-1 do begin
MyInt := MyArray.Strings[i]; Sum := 0;
for n := 1to Length(MyInt) do begin
if (MyInt[n]='7') then Sum := Sum + 1;
end;
if(Sum>=2) then Resultat := Resultat + 1;
end;
Result := Resultat;
end;
Как улучшить исходный код
Форматирование кода
Мне явно не хватает пробелов для удобного чтения кода, особенно отступов — с трудом просматривается структура кода.
function find7(S : string) : integer;
var
MyArray : TStringList;
n, i, sum, Resultat : integer;
MyInt : String;
begin
MyArray := TStringList.Create;
Trim(S);
n:= 0;
while (Length(s) > 0) do begin
Trim(S);
if (Pos(' ', S) = 0) then begin
MyArray.Insert(n, Copy(S, 1, Length(S)));
break;
end else begin
if (Copy(S, 1, Pos(' ', S)) = ' ') then begin
Delete(S, 1, Pos(' ', S));
end else begin
MyArray.Insert(n,Copy(S, 1, Pos(' ',S)));
Delete(S, 1, Pos(' ', S));
n := n + 1;
end
end
end
Resultat := 0;
for i := 0 to MyArray.Count - 1 do begin
MyInt := MyArray.Strings[i];
Sum := 0;
for n := 1 to Length(MyInt) do begin
if (MyInt[n] = '7') then Sum := Sum + 1;
end
if (Sum >= 2) then Resultat := Resultat + 1;
end
Result := Resultat;
end
Разделение ответственностей (разделение знаний)
После форматирования вижу, что алгоритм функции состоит из двух крупных блоков: разбора входной строки, в процессе которого строка превращается в массив, а затем поиск чего-то в полученном массиве. Блоки совершенно самостоятельные, они «знают» о том, как делать две разные не связанные между собой вещи — это железный аргумент в пользу их извлечения в отдельные функции.
function find7(S : string) : integer;
function parseString(S : string) : TStringList
var
MyArray : TStringList;
n : integer;
begin
MyArray := TStringList.Create;
Trim(S);
n:= 0;
while (Length(s) > 0) do begin
Trim(S);
if (Pos(' ', S) = 0) then begin
MyArray.Insert(n, Copy(S, 1, Length(S)));
break;
end else begin
if (Copy(S, 1, Pos(' ', S)) = ' ') then begin
Delete(S, 1, Pos(' ', S));
end
else begin
MyArray.Insert(n, Copy(S, 1, Pos(' ', S)));
Delete(S, 1, Pos(' ', S));
n := n + 1;
end;
end;
end;
Result := MyArray;
end
function findSevens(MyArray : TStringList) : integer
var
i, Sum, Resultat : integer;
MyInt : String;
begin
Resultat := 0;
for i := 0 to MyArray.Count - 1 do begin
MyInt := MyArray.Strings[i];
Sum := 0;
for n := 1 to Length(MyInt) do begin
if (MyInt[n] = '7') then Sum := Sum + 1;
end;
if (Sum >= 2) then Resultat := Resultat + 1;
end;
Result := Resultat;
end
var
MyArray : TStringList;
begin
MyArray = parseString(S);
Result := findSevens(MyArray);
end;
Алгоритм функции parseString
После краткого анализа кода функции становится понятно, что она делает одну простую вещь: разбивает строку на слова по пробелам.
Это можно сделать разными способами: с помощью функции SplitString
с последующим удалением пустых слов, либо вот так, вручную ища пробелы внутри строки и нарезая строку на подстроки по найденным пробелам и удаляя найденные слова и пробелы из исходной строки.
Допустим, мы хотим оставить исходный алгоритм с ручным поиском пробелов. Как можно его ещё улучшить? Тут есть некоторые вещи, без которых можно обойтись: например, можно не хранить размер результирующего массива в переменной n
, а вставлять новые слова в начало массива, так как порядок слов в итоге не важен. Можно запоминать позицию очередного пробела в отдельной переменной, чтобы не вызывать так много раз Pos(' ', S)
.
Не очень понятна необходимость проверки if (Copy(S, 1, Pos(' ', S)) = ' ')
— она проверяет наличие пробела в начале строки, но с их поиском и удалением успешно справляется вызов Trim(S)
в начале каждой итерации.
Не очень хорошо выглядят два вызова MyArray.Insert
— стоит избегать такого дублирования.
В цикле есть два способа выхода: когда перестанет срабатывать условие цикла Length(s) > 0
, и когда будет обнаружено, что в оставшейся строке уже нет пробелов if (Pos(' ', S) = 0)
— тогда выход произойдёт через break
. Желательно оставить только один способ. Поскольку проверять, что исходная строка не пустая, всё равно придётся, то есть смысл оставить только выход при обнаружении, что слово в строке — последнее.
function parseString(S : string) : TStringList
var
MyArray : TStringList;
WordEnd : integer;
begin
MyArray := TStringList.Create;
Trim(S);
if (Length(S) > 0) then begin
while (true) do begin
WordEnd := Pos(' ', S);
if (WordEnd = 0) then begin
MyArray.Insert(1, Copy(S, 1, Length(S)));
break;
end
MyArray.Insert(1, Copy(S, 1, WordEnd));
Delete(S, 1, WordEnd);
Trim(S);
end
end
Result := MyArray;
end
К сожалению, избавиться от двух вызовов MyArray.Insert
в этом варианте не получилось. Попробую изменить тактику.
function parseString(S : string) : TStringList
var
MyArray : TStringList;
WordEnd : integer;
begin
MyArray := TStringList.Create;
Trim(S);
while (Length(S) > 0) dobegin
WordEnd := Pos(' ', S);
if (WordEnd = 0) then WordEnd := Length(S);
MyArray.Insert(1, Copy(S, 1, WordEnd));
Delete(S, 1, WordEnd);
Trim(S);
end
Result := MyArray;
end
Теперь слова и пробелы удаляются из исходной строки до тех пор, пока она полностью не опустеет — тогда цикл закончится.
Алгоритм поиска семёрок
Здесь мне не нравятся названия переменных: их названия не отражают их смысла. MyInt
— это очередное рассматриваемое слово, Sum
— это количество семёрок в слове, а вовсе никакая не сумма, Resultat
— это количество слов, в которых найдено две и больше семёрок.
Заодно можно скорректировать и название самой функции.
Также в этой функции снова обнаружилиь два знания: как подсчитать количество семёрок в слове, и как подсчитать слова с нужным количеством семёрок.
function countWordsWithTwoSevens(MyWords : TStringList) : integer
function countSevens(word : string) : integer
var
i, sevensCount : integer
begin
sevensCount := 0;
for i := 1to Length(word) do begin
if (word[i] = '7') then sevensCount := sevensCount + 1;
end;
Result := sevensCount;
end
var
i, sevensCount, wordsCount : integer;
MyInt : String;
begin
wordsCount := 0;
for i := 0 to MyArray.Count - 1 do begin
sevensCount := countSevens(MyArray.Strings[i]);
if (sevensCount >= 2) then wordsCount := wordsCount + 1;
end
Result := wordsCount;
end
Теория
- Принцип единственного знания
- Рефакторинг «извлечение метода»