Created
March 12, 2011 07:29
-
-
Save NIA/867112 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (* Ваня: я решил набросать свой вариант программы и на нём показать, как всё | |
| делается. Можешь сохранить его на будущее как далеко не идеальный, но неплохой | |
| пример для подражания.*) | |
| (* Ваня: первый совет: в коде очень полезны комментарии. Если ты делаешь | |
| что-то неочевидное в какой-то строчке, поясни это в комментарии рядышком. Я | |
| покажу тебе примеры полезных комментариев ниже. Чтобы ты не путал мои советы с | |
| комментариями к коду, советы я пишу в круглых скобках и начинаю со своего | |
| имени, а комментарии - в фигурных скобках. Так что если захочешь использовать | |
| этот код, советы удаляй, они только для тебя, а комментарии оставь: они | |
| полезны тем, что другой человек (или ты сам через некоторое время) сможет | |
| понять, что ты имел в виду. *) | |
| (* Ваня: объявим функцию, делающую основное действие. Хорошо давать функциям | |
| понятные английские имена, как здесь: 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. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Соответствующие задачи в курсе ОПК: http://opk.lab9.ru/tasks/23 и http://opk.lab9.ru/tasks/24
А вообще, если это оформить в скринкаст - было бы гораздо лучше, и думаю интереснее.