VIII.6. О СТИЛЕ ПРОГРАММИРОВАНИЯ [57] Форме дай щедрую дань Временем: важен в поэме Стиль, отвечающий теме. Стих, как монету, чекань Строго, отчетливо,честно, Правилу следуй упорно: Чтобы словам было тесно, Мыслям просторно. Н. Некрасов Трудно дать исчерпывающее определение понятия "с т и л ь программиро- вания". Попытаемся охарактеризовать его, рассмотрев с различных точек зре- ния. Главный тезис состоит в том, что с т и л ь п р о г р а м м и р о - в а н и я - э т о с т и л ь м ы ш л е н и я, проявляющийся в умении переводить алгоритм решения задачи на конкретный язык программирования. Не существует совокупности правил написания программ, следуя которым Вы могли бы создавать качественные и не содержащие ошибок программы.Стиль программирования не сводится к хорошему знанию конкретного языка програм- мирования, знанию его возможностей и правил записи программ, хотя он все это и предполагает. Скорее эти знания помогут отличить программу, написан- ную в хорошем стиле от программы, написанной в плохом стиле. Многие доста- точно хорошо знают какой-либо иностранный язык, чтобы без особых затруд- нений читать специальную или художественную литературу на этом языке. Но лишь немногие могут с такой же легкостью написать на иностранном языке да- же небольшую статью. Таким образом, от знания языка до владения языком ле- жит "дистанция огромного размера". Но даже овладев языком программирова- ния в совершенстве, мы лишь незначительно приблизимся к обладанию стилем программирования. К сожалению, взгляд на стиль программирования как на стиль мышления еще не всеми и не до конца осознан. Очень часто стиль программирования сводится к технологии программирования. За последние годы мы пережили не- сколько вспышек увлечения модными методологиями и технологиями программи- рования, такими, например, как структурное программирование. Но в главном все они ориентируются на внешние факторы, характеризующие программу. Кста- ти сказать, жертвой структурного программирования чуть было не стали FORTRAN и BASIC с их неструктурными операторами. Но очевидно, что плохо разработанная программа, записанная по правилам структурного программиро- вания, так и останется плохой программой. В этом плане переход от языка BASIC к таким языкам структурного программирования, как Pascal и Ada,мало что даст. Подводя итог сказанному, приведем некоторые рекомендации, которые, как мы надеемся, при осознанном и неформальном их использовании помогут Вам выработать свой стиль программирования. Большинство рекомендаций имеет до- статочно общий характер и могут быть использованы при программировании не только на BASIC, но и на многих других языках программирования. Эти реко- мендации не являются исчерпывающими. Много подобных советов читатель мо- жет почерпнуть из книг по разработке программ и структурному программиро- ванию [52, 53, 54, 55]. ┌────────────────────────────────────────────────────────────┐ 1. │ В основе алгоритма решения задачи лежит м а т е м а т и - │ │ ч е с к а я м о д е л ь. Не нужно жалеть времени на раз- │ │ работку и изучение свойств этой модели! │ └────────────────────────────────────────────────────────────┘ Это поможет лучше понять задачу и найти наиболее естественный путь ее решения. 2. Максимально используйте "изобразительные" возможности языка програм- мирования. В частности, ┌─────────────────────────────────────────────────────────────┐ │ старайтесь располагать только о д и н оператор на строке │. └─────────────────────────────────────────────────────────────┘ Размещение нескольких операторов на одной физической строке противоречит правилу структурного программирования, требующему сдвигать оператор по строке в соответствии с уровнем его вложенности. 3. Относитесь с должным вниманием и аккуратностью к написанию даже очень простых частей программы. Помните, что программа - это единый орга- низм, работоспособность которого определяется качеством всех ее частей, а не каких-то отдельных компонентов. Просчет в каком-то одном месте может привести к неудаче в целом. 4. Не пытайтесь сразу написать эффективную программу. Это может при- вести к противоположному результату. Помните о следующем эмпирическом фак- те: 75% времени выполнения программы приходится на 25% ее текста. Лишь те- стирование первого варианта Вашей программы сможет выявить эти 25%, кото- рые действительно требуют тщательной проработки. Экономия на мелочах не- редко приводит к проигрышу в целом. ┌─────────────────────────────────────────────────────────┐ 5. │ Помните об отладке с самого начала создания программы │. └─────────────────────────────────────────────────────────┘ Для этого активно используйте отладочную печать, вставляя соответствую- щие операторы еще при написании программы. Во всех местах, где происходит ветвление процесса вычислений, необходимо распечатывать данные, определяю- щие выбор варианта. В местах слияния ветвей решения следует печатать мар- кер этого места и информацию о том, по какой из ветвей прошло решение. 6. Не забывайте о надлежащей организации операций ввода-вывода данных. Не жалейте усилий на разработку средств, обеспечивающих наглядность пред- ставления вводимых и выводимых данных. Все вводимые данные должны распе- чатываться и проверяться на корректность. Печать данных так же, как и представление вводимых данных, должна быть содержательной, отражающей структуру данных и их интерпретацию,а не быть хаотичной грудой цифр. Используйте такие формы отображения, как таблицы и графики. 7. Если Вы хотите достигнуть определенных высот в программировании, то процесс создания программы не должен заканчиваться для Вас получением ра- ботающей программы. ┌─────────────────────────────────────────────────────────────────────┐ │ Не спешите расстаться с Вашим детищем, принесшим Вам столько хлопот │ │ и мучений. Еще раз внимательно просмотрите программу. Постарайтесь │ │ извлечь максимум пользы на будущее. Оцените идеи и методы, использо-│ │ ванные Вами с точки зрения их применимости в других ситуациях и пы-│ │ тайтесь выработать шаблоны, обобщающие эти идеи и методы. │ └─────────────────────────────────────────────────────────────────────┘ По мере того, как число таких шаблонов будет увеличиваться,будет расти и Ваше мастерство, умение оценивать все особенности конкретной задачи и пользоваться наиболее адекватными средствами для их отражения. Сказанное не следует понимать как призыв к шаблонному мышлению в программировании. Скорее наоборот, применение того или иного шаблона требует глубокого пред- варительного анализа имеющейся ситуации с целью определения, какой именно шаблон можно применить в данной ситуации. И чем больше шаблонов находится в Вашем распоряжении, тем более детальным должен быть этот анализ. Резуль- татом такого анализа может быть рождение новой оригинальной идеи или мето- да. Таким образом, использование шаблонов освободит Вас от "изобретения велосипеда" в тех ситуациях, когда можно добраться до цели в роскошной го- ночной машине,и позволит сосредоточить основное внимание на решении новых для Вас проблем. 8. Помните, что наилучшей документацией для любой программы является сама эта программа. Поэтому программа должна содержать в явном виде исчер- пывающую информацию, представленную в виде комментариев. 9. Чтобы снизить погрешность результатов при выполнении вычислений с действительными числами, следует: ∗ избегать вычитания близких чисел, ∗ избегать деления больших по модулю чисел на малые числа, особенно если последние имеют невысокую точность, ∗ сложение (вычитание) длинной последовательности чисел начинать с меньших чисел, ∗ стремиться уменьшить число операций, ∗ использовать алгоритмы,для которых известны оценки ошибок, ∗ не использовать сравнение на равенство действительных чисел в опера- торе IF. 10. Программисты в своих программах не всегда явно присваивают началь- ные значения используемым переменным, полагаясь на то, что интерпретатор присвоит им вполне определенные значения. Это может привести к неверной работе программы. Чтобы избежать такой ошибки, следует явным образом при- сваивать начальные значения всем используемым в программе переменным. Та- кие "сокращения" предназначены для дилетантов или тех, кто программирует от случая к случаю, но не программистов-профессионалов. ┌──────────────────────────────────────────────────────────────────┐ │ Профессиональный программист должен явно определять или объявлять│ │ все переменные в самом начале программы или подпрограммы. │ └──────────────────────────────────────────────────────────────────┘ 11. При использовании в записи идентификатора цифр помещайте их в кон- це идентификатора, так как цифры 0, 1, 2, 3, 4, 5 в середине идентификато- ра можно воспринять как буквы O, I, Z, З,Ч, S. Никогда не используйте переменную более чем для одной цели. Распростра- ненный прием экономии лишнего слова памяти или лишнего оператора для опи- сания типа состоит в повторном использовании переменной в различных усло- виях. Программист вполне может решить: "Я закончил работать с TIME для расчетов времени, поэтому теперь буду использовать эту переменную как про- межуточную при вычислении даты". Такая практика увеличивает шансы внесе- ния ошибки при модификации программы. 12. В тексте программы старайтесь явно не употреблять константы, за ис- ключением, быть может, нуля и единицы. Для констант лучше вводить имена, которые и использовать в программе. Это приводит к лучшей "читабельности" программы и к уменьшению числа возможных ошибок. 13. Избегайте употреблять арифметические выражения,содержащие перемен- ные разных типов. Пользуйтесь функциями CINT() и CSNG() для преобразова- ния значений переменных одного типа в значения другого типа данных. 14. Предусматривайте "фразу" ELSE для каждой "фразы" THEN. В операторе условного перехода должно быть п о р о в н у "фраз" THEN и ELSE. Даже если не нужно ничего делать в случае "фразы" ELSE, следует предусмотреть пустой оператор. Это подскажет читателю, что случай "пустого" ELSE также рассматривается, и поможет понять последовательность действий. 15. Не пишите изменяющих себя программ. 16. Изучите и активно используйте возможности языка программирования: в результате программы становятся короче и исключаются определенные типы ошибок. ┌─────────────────────────────────────────────────────────────────────┐ │ Внимательно прочтите раздел о "подводных камнях" в руководстве по │ │ языку программирования, на котором Вы программируете. │ │ Экономьте время, учитесь на ошибках других! │ └─────────────────────────────────────────────────────────────────────┘ 17. Другими факторами, оказывающими влияние на организацию программ, являются ограничения на память и время выполнения. Учет этих факторов мо- жет вступать в противоречие с требованиями методов структурного программи- рования при разработке программ. Если ограничения на память существенны, возможно, придется пожертво- вать наглядностью программы и комментариями для экономии памяти. Конечно, программу можно разделить так, чтобы результат получался поэтапно с помо- щью не одной, а нескольких программ. Можно также уменьшить размеры про- граммы, ограничивая использование повторяющихся операторов или сегментов программы. Для этого может потребоваться объединить модули или сегменты программы, что в определенной степени нарушит модульную структуру програм- мы. Кодирование нескольких операторов в одной строке, удаление необязате- льных пробелов в операторах, а также исключение операторов REM - все эти приемы позволяют сэкономить память, но сделают программу более трудной для понимания, отладки и модернизации. Объем вычислений можно уменьшить различными методами. Прежде всего не- обходимо исключить лишние вычисления. Следует убедиться, что одно и то же значение не вычисляется в программе многократно. Вот,что иногда можно уви- деть в программе: 10 FOR I=1 TO N:FOR J=1 TO 1000 20 K=I+3 ··· 80 NEXT J:NEXT I В этой программе выражение K=I+3 вычисляется 1000 раз для каждого из N значений параметра цикла I. Чтобы устранить ошибку, оператор 20 следует записать перед оператором FOR J=1 TO 1000(произвести "ч и с т к у" цикла). На вычисление тригонометрических и экспоненциальных функций затрачива- ется много машинного времени. Довольно часто мы можем пересмотреть алго- ритм и обойтись без этих функций. Вообще сведение всех вычислений только к операциям сложения и вычитания ц е л ы х чисел существенно увеличивает скорость выполнения программы. Уменьшение размерности массивов или их исключение также дают выигрыш во времени (например, для движущихся изображений это означает сокращение числа перемещаемых элементов рисунка и увеличение шага каждого перемеще- ния), поскольку адрес элемента двухмерного массива вычисляется дольше,чем одномерного. Кроме того, время доступа к элементу одномерного массива бо- льше, чем к простой переменной. ┌─────────────────────────────────────────────────────────────────┐ │ Однако основной метод уменьшения времени выполнения состоит в │ │ кодировании программы на языке ассемблера или на машинном языке!│ └─────────────────────────────────────────────────────────────────┘ Тем не менее, игнорируйте все предложения по повышению эффективности, пока программа не будет правильной.Худшее,что может быть сделано,- это на- чать беспокоиться о быстродействии программы до того, как она начнет рабо- тать правильно. Быстродействующая, но неправильная программа бесполезна; медленнодействующая, но правильная всегда имеет некоторую ценность, а мо- жет оказаться и вполне удовлетворительной. "1. Если программа неверна, то ее быстродействие не имеет значения. Убе- дитесь в ее правильности, прежде, чем Вы начнете ее "улучшать". 2. Сохраняйте программу ясной и понятной, не пытайтесь повысить ее бы- стродействие в процессе кодирования. Преждевременная оптимизация - корень всех бед. 3. Не стремитесь к оптимизации каждого простого вычисления. Пусть тран- слятор делает это за Вас. 4. Беспокойтесь об алгоритме, а не о деталях программы. Помните, что структура данных может существенно повлиять на то,как будет реализован ал- горитм..." Керниган Б., Плоджер Ф. Вейнберг [68] рассказывает забавную историю о новой программе, которая из-за слишком большой сложности оказалась совершенно ненадежной. Был вы- зван новый программист, который нашел лучшее решение и за две недели сде- лал новую, надежную версию программы. При демонстрации ее работы он отме- тил, что его программе требуется 10 секунд на каждую перфокарту. Один из разработчиков первоначального варианта, торжествуя, заявил: "А моей про- грамме требуется только одна секунда на перфокарту". Ответ программиста стал классическим: ┌────────────────────────────────────────────────────────────────────────┐ │"Но Ваша программа не работает. Если программа не должна работать, я │ │ могу написать такую, которой хватает одной миллисекунды на перфокарту".│ └────────────────────────────────────────────────────────────────────────┘ VIII.7. НЕДОСТАТКИ ЯЗЫКА ПРОГРАММИРОВАНИЯ BASIC [59] Меня вы научили говорить Взирая на солнце,прищурь глаза свои На вашем языке. Теперь я знаю, и ты смело разглядишь в нем пятна. Как проклинать, - спасибо и за это. Козьма Прутков Пусть унесет чума обоих вас И ваш язык. У.Шекспир. Буря (пер.М.Донского) Школьный курс информатики преследует две цели: 1. О б щ е т е о р е т и ч е с к а я ц е л ь . Овладение алгоритмическим стилем мышления, который включает в себя уме- ние четко сформулировать заданные условия и требуемые результаты, разбие- ние большой задачи на малые, поиск решения в виде последовательности дей- ствий, выбор которых может зависеть от конкретных условий. Алгоритмичес- кое мышление в таком понимании применимо (и необходимо!) практически в лю- бой сфере человеческой деятельности. 2. П р а к т и ч е с к а я ц е л ь . Понимание принципиальных возможностей современных компьютеров, предста- вление о круге решаемых ими задач. Умение выделить "человеческую" и "ком- пьютерную" части конкретной задачи, построение модели и формализация "ком- пьютерной" части, доведение ее до уровня программы для ЭВМ. Заметим, что программирование занимает и здесь подчиненное место. Глав- ное - понять, что можно, а чего нельзя запрограммировать, и формализовать выбранную для программирования задачу. Итак, цели поставлены, теперь надо выбрать соответствующие им средства. И тут программирование заслуженно занимает ведущее место. Именно в нем на- иболее полно проявляются алгоритмические закономерности, поэтому програм- мирование становится о с н о в н ы м средством овладения алгоритмичес- ким мышлением. ┌─────────────────────────────────────────────────────────────────────┐ │ Итак, алгоритмическое мышление - цель, программирование - средство! │ └─────────────────────────────────────────────────────────────────────┘ Возникает вопрос: на каком языке записывать составляемые в процессе обучения алгоритмы и программы? Исходя из поставленных целей, определим требования к языку программирования. 1. Язык должен быть п р о с т ы м. Главная трудность должна заключать- ся в составлении алгоритма, а не в его записи (это требование немедленно исключает такие языки, как ЛИСП, Ассемблер, Си и язык программируемого ми- крокалькулятора). 2. Язык должен допускать естественное представление основных алгорит- мических конструкций (отпали FORTRAN и BASIC). 3. Язык должен быть у н и в е р с а л ь н ы м, т.е. допускать естест- венную обработку величин различных типов (Aлгол и FORTRAN не содержат до- статочно развитых средств работы со строковыми (литерными) величинами). Не кажутся обязательными такие требования, как распространенность язы- ка или наличие эффективной реализации. Скорее,наоборот, выбранный для мас- сового обучения язык имеет хорошие шансы стать широко распространенным. Таким образом, существующие языки программирования не пригодны для мас- сового обучения. Для решения этой проблемы были созданы специальные языки: Робик и Рапира Г.А.Звенигородского, алгоритмический язык А.П.Ершова. По своим идеям,да и по внешней форме эти языки достаточно близки, главное их отличие заключается в трактовке понятия типа величины: в алгоритмическом языке тип связан с именем, в Рапире - со значением. Отметим особенности школьного алгоритмического языка, которые делают его особенно ценным для использования при обучении. 1. Это прежде всего р у с с к а я нотация. 2. Свободный синтаксис записи выражений, который дает ученику возмож- ность сосредоточиться не на форме записи алгоритма, а на его сути. 3. Алгоритмический язык обладает основными фундаментальными конструкци- ями, присущими структурному программированию: командой условного перехода "если..., то..., иначе...все", ──── ── ───── ─── циклами "для" и "пока", командой "выбор" . ─── ──── ───── 4. Нет конструкции "идти к..." (аналог оператора GOTO) ────── (впрочем, иногда это создает определенные трудности при составлении алго- ритма!). Поэтому ┌────────────────────────────────────────────────────────────────────┐ │ главной задачей, стоящей при изучении языка программирования в │ │ школе, является - показать, как фундаментальные алгоритмические │ │ конструкции (конструкции школьного алгоритмического языка) при- │ │ обретают свойственную конкретному языку программирования форму, │ │ сохраняя свое содержание │ . └────────────────────────────────────────────────────────────────────┘ Программа преподавания информатики предоставляет учителю свободу выбо- ра языка программирования, но большинство останавливается на BASIC и Ра- пире. Именно поэтому мы поговорим о недостатках языка программирования BASIC. 1. В MSX-BASIC отсутствует естественное представление алгоритмической конструкции - цикл "пока". Приведенные в школьном учебнике операторы ──── WHILE-WEND относятся к весьма редкой версии BASIC,не встречающейся на име- ющихся сегодня (1990г.) компьютерах. 2. Нет аналога вспомогательным алгоритмам. Конструкция подпрограмм не имеет таких важнейших свойств вспомогательных алгоритмов, как независи- мость имен, передача параметров и возможность организации библиотек. Та- ким образом, эта конструкция не является эффективным средством разбиения большой задачи на малые и сведения нерешенной задачи к решенным (важней- шие навыки алгоритмизации!). Других средств для этого BASIC не предлагает. 3. Выбор имен в подавляющем большинстве версий BASIC (MSX-BASIC, конеч- но, не в счет!) ограничен формулой "буква + цифра". Использование содержа- тельных мнемонических имен запрещено,что очень затрудняет разработку и по- нимание программ, выходящих за рамки примитива. Таким образом, BASIC может быть инструментом программирования (хотя и очень несовершенным), но он не дает адекватных средств для развития мышле- ния. В то же время из-за отсутствия естественного представления основных конструкций его очень неудобно использовать в качестве иллюстрации. Особенно опасно изучение BASIC в компьютерном курсе. Слабые ученики плохо понимают язык, путаются в многочисленных ограничениях при программи- ровании алгоритмических конструкций, теряют интерес к предмету и к компью- теру. На программирование у них уходит столько сил,что на мышление уже не остается. Сильные же ученики,видя,что компьютер прекрасно понимает их без "алгоритмических излишеств" типа заголовка алгоритма и продуманной струк- туры программы, пренебрегают этими деталями, не хотят писать на школьном алгоритмическом языке задачи,которые можно сразу сделать на BASIC.В итоге они получают навык программирования, а не мышления, и главная цель остает- ся не достигнутой. Почему же, несмотря на столь серьезную опасность BASICa, он так популя- рен? Перечислим основные аргументы, выдвигаемые его зашитниками и попытаем- ся их прокомментировать. 1) BASIC прост в изучении. Это единственное достоинство языка как тако- вого. Но это та самая простота, которая хуже воровства, "простота орудий каменного века". BASIC прост, потому, что в нем нет сложных элементов (в первую очередь вспомогательных алгоритмов и некоторых алгоритмических кон- струкций), без овладения которыми поставленные цели не достигаются. 2) При работе на BASIC диалог с компьютером происходит быстро и просто. Это достоинство не языка, а его реализации. 3) BASIC имеется на многих компьютерах, он широко распространен.Однако это не достоинство, а большая беда. Фактически мы сталкиваемся здесь с осужденным в промышленности диктатом производителя. Изготовители ЭВМ и программного обеспечения рекламируют BASIC, так как BASIC-системы проще в изготовлении и могут быть перенесены с западных компьютеров. ┌───────────────────────────────────────────────────────────────────┐ │ Итак, необходимо, чтобы поставляемые в школы компьютеры имели │ │ систему программирования не на BASIC, a на другом языке програм- │ │ мирования высокого уровня! │ └───────────────────────────────────────────────────────────────────┘