Skip to content

Instantly share code, notes, and snippets.

@NIA
Created March 12, 2011 07:29
Show Gist options
  • Select an option

  • Save NIA/867112 to your computer and use it in GitHub Desktop.

Select an option

Save NIA/867112 to your computer and use it in GitHub Desktop.
(* Ваня: я решил набросать свой вариант программы и на нём показать, как всё
делается. Можешь сохранить его на будущее как далеко не идеальный, но неплохой
пример для подражания.*)
(* Ваня: первый совет: в коде очень полезны комментарии. Если ты делаешь
что-то неочевидное в какой-то строчке, поясни это в комментарии рядышком. Я
покажу тебе примеры полезных комментариев ниже. Чтобы ты не путал мои советы с
комментариями к коду, советы я пишу в круглых скобках и начинаю со своего
имени, а комментарии - в фигурных скобках. Так что если захочешь использовать
этот код, советы удаляй, они только для тебя, а комментарии оставь: они
полезны тем, что другой человек (или ты сам через некоторое время) сможет
понять, что ты имел в виду. *)
(* Ваня: объявим функцию, делающую основное действие. Хорошо давать функциям
понятные английские имена, как здесь: clean spaces = почистить пробелы. На
самом деле, может быть всё ещё не понятно, что именно подразумевается под
чисткой, так что лучше всего в комментарии описать её работу, что я и сделал.
*)
{ Функция clean_spaces возвращает строку, из которой удалены все
дублирующиеся пробелы }
function clean_spaces(s : string) : string;
(* Ваня: понятными должны быть и имена переменных: переменная из одной
буквы - очень плохо! Представь, что через месяц ты открываешь свою
программу, и не помнишь, что ты обозначал буквами a, b, c. Придётся
перечитывать весь код и разбираться. Если ты хранишь в переменной массу
чего-то - назови её mass, а не просто m; если цена машины - пусть будет
car_cost, и т.п. В крайнем случае сокращай длинные слова: например, имя
организации может храниться в переменной org_name. То же самое делай, если
имя уже занято: length - это функция, считающая длину строки, поэтому
сохранить эту длину в переменной length ты уже не можешь. Назови, например,
len. Но не просто l и уж тем более не D! Аргумент же функции иногда можно
называть просто по первой букве типа: раз это просто строка, то пусть будет
просто s (от слова string). Другое дело, если аргументом является не любая
строка, а вполне осмысленная, например имя ученика, называй её name. *)
var
len : integer; { длина строки, уменьшается по ходу процесса }
i : integer;
begin
len := length(s);
(* Ваня: Единственное место, где можно делать переменные из одной буквы -
счётчик цикла. Общепринятое имя - i, для вложенных циклов - j, k и т.д. *)
{ Используется цикл while, а не for, потому что при удалении символа не
нужно увеличивать счётчик на единицу: он уже указывает на следующий символ }
i := 2;
while i <= len do
begin
{ для каждого символа, кроме первого, проверяем, был ли перед ним пробел }
if (s[i] = ' ') and (s[i-1] = ' ') then
begin
delete(s, i, 1);
len := len - 1;
end
else
begin
i := i + 1;
end;
end;
clean_spaces := s;
(* Ваня: Что касается отступов, здесь они все одинаково по два пробела. Мне
больше нравится такая величина отступа, потому что код не уезжает далеко
вправо. В Turbo Pascal это настраивается в меню Options - Environment -
Editor, поле Tab Size. *)
(* Ваня: и ещё: не скупись на пробелы. Гораздо красивее, когда операторы
вроде + или := отделены с обоих сторон пробелами от переменных, и после
запятых пробелы тоже явно не помешают (как и в русском языке) *)
end;
(* Ваня: ещё одно правило: каждая процедура делает своё дело. Если тебе нужно
удалить дублирующиеся пробелы, а ещё пробелы в начале и конце - это совсем
разные действия, их лучше делать отдельными функциями. Тогда и тестировать
проще, и использовать можно независимо. Обрати внимание, как я назвал
переменные и как комментариями выделил общую последовательность действий *)
{ Функция cut_spaces возвращает строку с обрезанными пробелами
в начале и в конце }
function cut_spaces(s : string) : string;
var
len : integer; { длина строки, уменьшается по ходу процесса }
start : integer; { позиция первого не-пробельного символа в строке }
finish : integer; { позиция последнего не-пробельного символа в строке }
deleted_len : integer; { длина удаляемого участка }
begin
len := length(s);
{ Находим первый не-пробел }
start := 1;
while (start <= len) and (s[start] = ' ') do
start := start + 1;
{ Удаляем с начала до start, не включая start }
deleted_len := start - 1;
delete(s, 1, deleted_len);
len := len - deleted_len;
{ Находим последний не-пробел }
finish := len;
while (finish >= 1) and (s[finish] = ' ') do
finish := finish - 1;
{ Удаляем от finish+1 до конца (не включая finish) }
deleted_len := len - finish;
delete(s, finish + 1, deleted_len);
cut_spaces := s;
end;
(* Ваня: а теперь давай напишем тесты. Если бы функция была похитрее, тесты
тоже были бы хитрыми: они бы сначала делали какую-то хитрую подготовку, потом
вызывали бы функцию, отдавая ей подготовленные данные, и, наконец, не менее
хитро проверяли правильность результата. Каждый такой тест делался бы
отдельной процедурой и все они вызывались бы по очереди. У нас же проще: нужно
просто подсунуть несколько разных вариантов строк и проверить, что ответ будет
правильным. Сделаем процедуру check_result, которая выполняет эту проверку, и
печатает на экране, был ли ответ правильным. Это небольшой кусок кода, но всё
равно лучше вынести его в процедуру, чем копировать десять раз: ведь если ты
захочешь поменять что-то в нём, ты поменяешь это в одном месте, а не в десяти.
К тому же, кода станет меньше и его будет проще читать. Все тестовые случаи с
разными аргументами можно объединить в процедуру run_tests и вызвать её в
начале программы. Обрати потом внимание, как там всё кратко и понятно за счёт
использования процедуры check_result. *)
{ Процедура check_result сравнивает полученный результат result с правильным
результатом correct_result. Печатает имя теста test_name и успешность
проверки }
procedure check_result(test_name, result, correct_result : string);
begin
write('Тест "', test_name,'": ');
if ( result = correct_result ) then
writeln('ок')
else
writeln('ОШИБКА! Получилось "', result, '" вместо "', correct_result, '"');
end;
procedure run_tests;
begin
writeln('Тестирую clean_spaces...');
check_result('пустая строка', clean_spaces(''), '');
check_result('без лишних пробелов', clean_spaces('а бб ввв '), 'а бб ввв ');
check_result('один лишний пробел', clean_spaces('а б'), 'а б');
check_result('много лишних пробелов', clean_spaces(' а б в '), ' а б в ');
writeln;
writeln('Тестирую cut_spaces...');
check_result('пустая строка', cut_spaces(''), '');
check_result('одни пробелы', cut_spaces(' '), '');
check_result('без крайних пробелов', cut_spaces('а б в'), 'а б в');
check_result('пробелы в начале', cut_spaces(' в начале'), 'в начале');
check_result('пробелы в конце', cut_spaces('в конце '), 'в конце');
check_result('пробелы с обоих сторон', cut_spaces(' везде '), 'везде');
writeln;
end;
var
input_string, result : string;
old_length, new_length : integer;
begin
(* Ваня: Здесь я вызываю все тесты в самом начале программы, а потом
уже запускаю интерактивную часть, как было и у тебя. Можешь ради интереса
попробовать сломать мою функцию: поменяй в ней что-то, чтоб она работала
неправильно, и посмотри, как тесты сообщат тебе об этом. Гораздо лучше, чем
самому проверять кучу случаев, не так ли? *)
run_tests;
(* Ваня: Потом, когда ты закончишь работу над программой, ты можешь убрать
этот вызов run_tests, но саму процедуру run_tests и используемую в ней
check_result лучше при этом не удалять. И вот почему: если ты захочешь
что-то улучшить в программе, тебе наверняка захочется проверить, работает ли
она по-прежнему правильно. Тут-то тебе и помогут твои старые тесты, ведь,
если ты позаботился об этом, они уже проверяют все характерные случаи!
Возможно только, надо будет их поправить, если поведение функции изменилось,
и теперь другой результат считается правильным. *)
(* Ваня: Ну вот и всё для начала, надеюсь ты не устал :) Последнее, что я
хочу заметить - это работающая программа, но я даю её тебе не для того,
чтобы ты пошёл и тупо сдал её. Я хочу, чтобы ты научился на её примере и
понял, как делать лучше и удобнее для себя и для преподавателя. Так что в
идеале - разберись, как тут всё работает, спроси меня если не выйдет, а
потом сядь и напиши сам. Помни, что ни у какой задачи не бывает
единственного правильного решения, так что ничего страшного, если твоё
решение отличается от моего или от решения в учебнике. Другое дело, что
совсем круто - это сравнить решения, понять чьё лучше и почему, и взять на
заметку на будущее. Впрочем, это уже касается не только программирования ;)
*)
writeln('Введите строку');
readln(input_string);
old_length := length(input_string);
result := clean_spaces(cut_spaces(input_string));
new_length := length(result);
writeln('Введено: "', input_string, '", длина: ', old_length);
writeln('Лишних пробелов: ', old_length - new_length);
writeln('Результат: "', result, '", длина: ', new_length);
readln;
end.
@cypok

cypok commented Mar 12, 2011

Copy link
Copy Markdown

Соответствующие задачи в курсе ОПК: http://opk.lab9.ru/tasks/23 и http://opk.lab9.ru/tasks/24

А вообще, если это оформить в скринкаст - было бы гораздо лучше, и думаю интереснее.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment