- •1. Стиль 10
- •3. Проектирование и реализация 63
- •4. Интерфейсы 85
- •5. Отладка 115
- •6. Тестирование 134
- •7. Производительность 157
- •8. Переносимость 180
- •9. Нотация 203
- •Введение
- •Брайан в. Керниган
- •1.1. Имена
- •1.2. Выражения
- •Упражнение 1 -6
- •1.3. Стилевое единство и идиомы
- •1.4. Макрофункции
- •1.5. Загадочные числа
- •1.6. Комментарии
- •1.7. Стоит ли так беспокоиться?
- •Дополнительная литература
- •2.1. Поиск
- •2.2. Сортировка
- •2.3. Библиотеки
- •2.4. Быстрая сортировка на языке Java
- •2.5. "О большое"
- •2.6. Динамически расширяемые массивы
- •2.7. Списки
- •Упражнение 2-8
- •2.8. Деревья
- •Упражнение 2-15
- •2.10. Заключение
- •Дополнительная литература
- •Проектирование и реализация
- •3.1. Алгоритм цепей Маркова
- •3.2. Варианты структуры данных
- •3.3. Создание структуры данных в языке с
- •3.4. Генерация вывода
- •3.5.Java
- •Into the air. When water goes into the air it
- •3.7. Awk и Perl
- •3.8. Производительность
- •3.9. Уроки
- •Дополнительная литература
- •4. Интерфейсы
- •4.1. Значения, разделенные запятой
- •4.2. Прототип библиотеки
- •4.3. Библиотека для распространения
- •Упражнение 4-4
- •4.5 Принципы интерфейса
- •4.6. Управление ресурсами
- •4.7. Abort, Retry, Fail?
- •4.8. Пользовательские интерфейсы
- •Дополнительная литература
- •5. Отладка
- •5.1. Отладчики
- •5.2. Хорошие подсказки, простые ошибки
- •5.3, Трудные ошибки, нет зацепок
- •5.4. Последняя надежда
- •5.5. Невоспроизводимые ошибки
- •5.6. Средства отладки
- •5.7. Чужие ошибки
- •5.8. Заключение
- •Дополнительная литература
- •6. Тестирование
- •6.1. Тестируйте при написании кода
- •6.2. Систематическое тестирование
- •6.3. Автоматизация тестирования
- •6.4. Тестовые оснастки
- •6.5. Стрессовое тестирование
- •6.6. Полезные советы
- •6.7. Кто осуществляет тестирование?
- •6.8. Тестирование программы markov
- •6.9. Заключение
- •Дополнительная литература
- •7.Производительность
- •7.1. Узкое место
- •7.2. Замеры времени и профилирование
- •7.3. Стратегии ускорения
- •7.4. Настройка кода
- •7.5. Эффективное использование памяти
- •7.6. Предварительная оценка
- •7.7. Заключение
- •Дополнительная литература
- •8. Переносимость
- •8.1. Язык
- •8.2. Заголовочные файлы и библиотеки
- •8.3. Организация программы
- •8.4. Изоляция
- •8.5. Обмен данными
- •8.6. Порядок байтов
- •8.7. Переносимость и внесение усовершенствований
- •8.8. Интернационализация
- •8.9. Заключение
- •Дополнительная литература
- •9.1. Форматирование данных
- •9.2. Регулярные выражения
- •Упражнение 9-12
- •9.3. Программируемые инструменты
- •9.4. Интерпретаторы, компиляторы и виртуальные машины
- •9.5. Программы, которые пишут программы
- •9.6. Использование макросов для генерации кода
- •9.7. Компиляция "налету"
- •Дополнительная литература
- •Интерфейсы
- •Отладка
- •Тестирование
- •Производительность
- •Переносимость
Дополнительная литература
В книге Брайана Кернигана и Роба Пайка "The Unix Programming Environment" (Brian Kernighan, Rob Pike. The Unix Programming Environment. Prentice Hall, 1984) широко обсуждается инструментальный подход к программированию, который так хорошо поддерживается в Unix. В восьмой главе ее содержится полная реализация — от грамматики уасс до выполнимого кода — простого языка программирования.
Дон Кнут в своей книге "ТЕХ: The Program" (Don Knuth. TEX: The Program. Addison-Wesley, 1986) описывает этот сложнейший текстовый процессор, приводя всю программу целиком — около 13 000 строк на Pascal — в стиле "грамотного программирования"21, при котором пояснения комбинируются с текстом программы, а для форматирования документации и выделения соответствующего кода используются соответствующие программы. То же самое для компилятора ANSI С проделано у Криса Фрейзера и Дэвида Хэнсона в книге "A Retargetable С Compiler" (Chris Eraser, David Hanson. A Retargetable С Compiler. Addison-Wesley, 1995).
Виртуальная машина Java описана в книге Тима Линдхольма и Франка Еллина "Спецификация виртуальной Java-машины", 2-е издание (Tim Lindholm, Frank Yellin. The Java Virtual Machine Specification. 2nd ed. Addison-Wesley, 1999).
Кен Томпсон (Ken Thompson) описал свой алгоритм (это один из самых первых патентов в области программного обеспечения) в статье "Regular Expression Search Algorithm" в журнале Communications of the ACM (11, 6, p. 419-422, 1968). Работа с регулярными выражениями весьма подробно освещена в книге Джеффри Фридла "Mastering Regular Expressions" (Jeffrey Friedl. Mastering Regular Expressions. O'Reilly, 1997).
JIT-компилятор для операций двумерной графики описан в статье Роба Пайка, Барта Локанти (Bart Locanthi) и Джона Рейзера (John Reiser) "Hardware/Software Tradeoffs for Bitmap Graphics on the Blit", опубликованной в Software — Practice and Experience (15, 2, p. 131-152, February 1985).
Эпилог
Если бы люди могли учиться у истории, какие уроки она могла
бы нам преподать! Но страсть и компания слепят наши очи, и
свет, даваемый опытом, — как кормовой огонь, освещает
только волны позади нас.
Сэмюэль Тейлор Колридж. Воспоминания
Компьютерный мир постоянно изменяется, причем во все возрастающем темпе. Программисты вынуждены овладевать новым: новыми языками, новыми инструментами и новыми системами и, конечно, изменениями, не совместимыми со старым. Программы становятся все объемнее, интерфейсы все сложнее, а сроки все жестче.
Однако есть некоторые неизменные моменты, позволяющие осмыслить уроки прошлого, что может помочь в будущем. Основные вопросы, освещенные в этой книге, базируются как раз на этих неизменных концепциях.
Простота и ясность — первые и наиболее важные среди этих основ, поскольку все остальное так или иначе из них следует. Делайте все самым простым способом. Выбирайте наиболее простой алгоритм, который выглядит достаточно быстрым, и наиболее простую структуру данных, позволяющую решить поставленную задачу; объединяйте их простым, понятным кодом. Не усложняйте ничего до тех пор, пока в этом не возникнет настоятельной необходимости, — например, пока замеры производительности не покажут неудовлетворительных результатов. Интерфейсы должны быть ограничены и лаконичны, по крайней мере до тех пор, пока не станет совершенно очевидно, что преимущества от нововведений перевесят недостатки дополнительного усложнения.
Универсальность нередко идет рука об руку с простотой, поскольку является, по сути, возможностью решить проблему раз и навсегда (так сказать, "в общем виде"), а не возвращаться к ней снова и снова для рассмотрения специфических случаев. Зачастую это является и хорошим подспорьем для обеспечения переносимости: лучше найти одно общее решение, которое будет работать во всех системах, чем разбираться в отличительных особенностях каждой системы.
Далее стоит назвать эволюцию. Невозможно написать совершенную программу с первой попытки. Глубокое и всестороннее осознание проблемы рождается только из сочетания размышлений и опыта; с помощью чисто умозрительных заключений не удастся создать хорошей системы, как не удастся сделать этого и чистым хакерством. Очень важна реакция пользователей; наиболее эффективным будет цикл развития системы, включающий в себя создание прототипа, экспериментирование, обратную связь с пользователем и дальнейшие усовершенствования. Программы, которые мы пишем для себя, часто не развиваются достаточным образом; большие программы, которые мы покупаем у других, изменяются слишком быстро без нужного улучшения.
Интерфейсы являются одним из камней преткновения в программировании, и аспекты, связанные с ними, проявляются во многих местах. Наиболее очевидный случай — библиотеки, но существуют еще интерфейсы между программами и между пользователями и программами. В проектировании интерфейсов идеи простоты и универсальности имеют особое значение. Делайте интерфейсы как можно более последовательными, чтобы их можно было без труда выучить и использовать; к этому надо подойти очень тщательно. Эффективным способом является абстрагирование: представьте себе идеальный компонент, библиотеку или программу; постарайтесь, чтобы ваш интерфейс как можно более полно соответствовал такому идеалу; спрячьте детали реализации — от греха подальше.
Роль автоматизации часто недооценивается. Гораздо эффективнее заставить компьютер выполнять вашу работу, чем делать ее вручную. Мы увидели примеры применения автоматизации при тестировании, отладке, анализе производительности и, что особенно важно, в написании кода, когда для решения конкретной задачи программы могут писать программы, которые было бы трудно написать вручную.
Нотация также зачастую недооценивается; и не только в качестве способа, которым программист сообщает компьютеру, что надо сделать. Способ записи представляет собой основу для реализации широкого спектра инструментов, а также обусловливает структуру программы, предназначенной для написания программ. Мы все хорошо владеем большими языками общего назначения, которые служат нам для создания основной части наших программ. Однако, как только задания становятся настолько специализированными и хорошо понятными, что запрограммировать их можно почти механически, стоит задуматься о том, чтобы создать нотацию, в которой задание выражалось бы естественно, и язык, реализующий эту нотацию. Регулярные выражения — один из наших любимых примеров, но возможностей создания рабочих языков для специализированных приложений существует бесчисленное множе ство. Для того чтобы дать хорошие результаты, языки эти вовсе не должны быть сложны.
У рядового программиста может иногда возникнуть ощущение что он является винтиком в огромной машине: ему приходится использовать навязанные ему языки, системы и инструменты, выполняя задания которые должны были бы быть уже сделаны для него и за него. Однако, по большому счету, наша работа оценивается по тому, как много мы можем сделать с помощью того, что у нас есть. Применяя идеи, изложенные в этой книге, вы обнаружите, что с вашим кодом стало удобнее работать, отладка стала гораздо менее болезненным процессом, да и в своих программах вы стали более уверены. Мы надеемся, что эта книга дала вам что-то, что сделает программирование более продуктивным и успешным для вас.
Приложение:
свод правил
Каждая открытая мной истина становилась правилом,
которое служило мне в дальнейшем для поиска других
истин.
Рене Декарт. Рассуждение о методе
Во многих главах были выделены правила или какие-то основные моменты, подводящие итог обсуждению. Для удобства поиска правила собраны здесь воедино. Не забывайте, что в соответствующих частях книги объясняется назначение и способы применения этих правил.
Стиль
Используйте осмысленные имена для глобальных переменных и короткие — для
локальных.
Будьте последовательны.
Используйте активные имена для функций.
Будьте точны.
Форматируйте код, подчеркивая его структуру.
Используйте естественную форму выражений.
Используйте скобки для устранения неясностей.
Разбивайте сложные выражения.
Будьте проще.
Будьте осторожны с побочными эффектами.
Будьте последовательны в применении отступов и фигурных скобок.
Используйте идиомы для единства стиля.
Используйте else-if для многовариантных ветвлений.
Избегайте макрофункций.
Заключайте тело макроса и аргументы в скобки.
Давайте имена загадочным числам.
Определяйте числа как константы, а не как макросы.
Используйте символьные константы, а не целые.
Используйте средства языка для определения размера объекта.
Не пишите об очевидном.
Комментируйте функции и глобальные данные.
Нe комментируйте плохой код, а перепишите его.
Не противоречьте коду.
Вносите ясность, а не сумятицу.
