Это третья статья из серии о программировании и композиции программ. Серия началась с Этюды в программной композиции. Последняя статья была Исследования в составе программы: новый проект — гольф.

Начиная

В последней статье предлагался пасьянс (терпение для читателей из Великобритании) игра «Гольф» как интересный программный проект. В этой статье основное внимание уделяется первым шагам проекта.

Это далеко не первый подобный проект, который я когда-либо делал. Около 2 лет назад я начал проект Golf. Я откопал это, когда снова подумал об этом, и обнаружил, что у него есть по крайней мере одна существенная ошибка — это «поедание» карт, что облегчает программе «выигрыш» игр. Вместо того, чтобы отлаживать простой скетч, который я уже написал, я решил начать сначала. Это также позволило мне рассмотреть некоторые другие аспекты программирования и попытаться найти окончательные решения для некоторых из них при создании этой программы. Примеры этих других «аспектов» появятся со временем.

Для американских телезрителей персонаж Джетро Гиббс из сериала NCIS известен тем, что у него есть неписаная книга пронумерованных правил поведения. У меня также есть некоторые неписаные (хотя и не обязательно пронумерованные) правила для моего собственного программирования. Ради нашего (вероятно, никогда не показываемого по телевидению) сериала я могу их пронумеровать.

27. Всегда четко сформулируйте проблему, которую вы пытаетесь решить, прежде чем приступить к ее решению. [Никто так и не смог выяснить, было ли у Гиббса правило для всех чисел. То же самое с моим.]

Правило 27 — это частный случай правила проекта: Всегда имейте план. Плану не нужно следовать рабски; его можно изменить в полете. Однако при наличии плана невозможно узнать, как продвигается проект. Аналогично с программой; без формулировки проблемы, как вы узнаете, закончили ли вы?

Это мое заявление для программы гольфа:

Напишите программу, которая создаст стандартную колоду карт, перетасует их в случайном порядке, раздаст начальный расклад для гольфа и найдет завершение игры.

Заявление далеко не достаточно. Например, что означает случайно? Какой будет результат поиска: я выиграл; Я потерял; этот поиск занимает слишком много времени, и я выхожу за (электронным эквивалентом) пива? Поскольку это эксперимент по программированию, утверждение более конкретное, чем мое первое утверждение из прошлой статьи:

Какой процент игр в гольф можно выиграть при условии лучшей игры?

На данный момент я хочу только иметь программу, которая будет играть в одну игру в гольф за раз и при этом будет смотреть, можно ли выиграть конкретную игру. Также помните, что цель проекта может меняться по мере его роста. Скорее всего, он станет более подробным. Если этот проект не сможет успешно сыграть даже в одну игру в гольф, проведение более сложных экспериментов, безусловно, будет безнадежным.

Пока я писал этот раздел и обдумывал постановку задачи, я понял, что допустил ошибку в терминологии и описании самой игры Гольф. Это приводит к другому правилу и следствию из правила 27. Новое правило

1. [Принцип нежелтушного глазного яблока] Если у вас возникла проблема или вы думаете, что закончили, объясните программу кому-нибудь еще.

Это первое правило, потому что я отметил его на последней странице Этюдов в описании того, как была найдена сложная ошибка. Это также заявление о том, почему во время разработки у нас есть проверки кода, парное программирование и другие социальные инструменты. Математики часто делают это, работая над сложными задачами. Эрдёш был странствующим не только по личному выбору, но и потому, что он мог постоянно показывать свою работу новым глазам. Очень часто объяснение, которое я даю, прерывается, потому что, просто пытаясь объяснить, я осознаю неудачу в своих мыслях еще до того, как мое предложение (или абзац, или рисунок, или…) будет завершено. Так что глазное приложение может быть удаленным во времени (когда вы читаете эту статью) или мнимым (когда я как корректор читаю эту статью). До тех пор, пока другие (возможно, перепрофилированные уже активированные глазные яблоки) работают.

Следствием правила 27 является то, что «четкое заявление» должно использовать абсолютно точный язык. В комментарии к правилу отмечалось, что random определено нечетко, и я согласился исправить это позже. Пока я просматривал эту статью (глазами редактора, а не автора), я понял, что в моем объяснении игры в гольф также была серьезная ошибка. Эта ошибка будет исправлена ​​при запуске программного кода. Возможно, внимательные читатели, играющие в карты, смогут понять это раньше. Как ни странно, у меня было неприятное чувство, что я сделал что-то не так, когда я кодировал первую версию Golf, и чувство было примерно в этом месте. Подсказка: в задаче используются точные термины, используемые для определения частей пасьянса.

Дополнительные правила программирования

Поскольку это экспериментальный проект, моей первой версией будет программа командной строки, а не какое-то блестящее мобильное приложение или веб-сервис. Что позволит мне сконцентрироваться на проблемах Гольфа, не заботясь о внешнем виде. Есть два правила, которые определяют мой выбор.

14. Все программы запускаются как приложения командной строки.

Это правило исходит из моей долгой карьеры, связанной с использованием вариаций Unix, а до этого — других операционных систем, которые (во многих случаях) допускали только приложения командной строки. Появление веб-приложений и мобильных приложений не изменило моего мнения. Эти технологии в гораздо большей степени связаны с подключением пользователей, чем с сутью процесса, и, начиная с пользователей, я просто трачу время на несущественные вещи еще до того, как узнаю, есть ли разумный подход к более глубокой проблеме. Да, есть фреймворки, шаблоны и платформы, которые помогут. Это очень похоже на приготовление пищи; Мне не нужно знать, как сервировать еду, прежде чем я научусь готовить без сырых кусочков, подгоревших пятен, пересола, недосола или просто несъедобных результатов.

Это правило Кнута [перефразируя: я могу найти цитату, если хотите)] помогает:

7. [Кнут] Все новые программы начинаются там, где уже есть какое-то понимание, и растут до тех пор, пока компоненты не начинают соединяться.

Из этого Кнут сделал вывод, что ни сверху вниз, ни снизу вверх структурированное программирование — основные соперники в то время — не были способом разработки реальных программ. Наоборот, оригинальные идеи — это что-то вроде Lego, и может потребоваться некоторое время и, возможно, несколько новых кирпичиков, чтобы превратить их в узнаваемую структуру, которая служит какой-то цели. Как только программа станет достаточно компетентной, Кнут предложил пересмотреть сверху вниз, чтобы программа была четко организована, снова пересмотреть цикл для восходящих изменений, которые улучшат производительность и мощность, снова пересмотреть сверху вниз до основной четкой структуры, снова снизу вверх, промойте и повторяйте до тех пор, пока ничего не будет получаться или пока не наступит усталость.

Философские максимы часто имеют два (или более) лица. Многие программисты соблюдают правило прототипа, возможно, действующую версию правила Кнута. Это правило прототипа напрямую применимо к эксперименту с гольфом.

66. Планируйте с самого начала выбросить первый (второй, третий, …) прототип и переписать с нуля.

Структура программы

Всякий раз, когда я начинаю новый проект, правило ввода-вывода говорит мне, как начать.

42. Все программы имеют вид ввод ➞ процесс ➞ вывод.

Это может быть истолковано как догма Все программы функциональны. Мой опыт показывает, что это в значительной степени верно. В частности, я разрабатываю дизайн только в том случае, если это оказывается необходимым. Последствия для гольфа

  1. Будет отдельный компонент, который считывает входные данные и настраивает любую необходимую структуру на основе входных данных.
  2. Второй компонент будет посвящен игре в гольф без учета ввода или вывода.
  3. Третий компонент выполнит любой необходимый вывод.

Опять же, правила часто имеют несколько лиц. На практике очень важным правилом является

43. Программа — это река: немного данных льется дождем, а поток данных вытекает.

Как ни странно, эти правила нумеруются последовательно. Возможно, потому что они связаны в моем сознании. Я использовал это правило на протяжении всей своей жизни, особенно до того, как фреймворки модульного тестирования стали популярными, чтобы убедиться, что даже прототипы улучшаются по мере выполнения дополнительной работы. В конкретных,

  • Каждая программа уведомляет о том, что она запущена и что ей требуется ввод (хотя в начале может не быть разумного ввода).
  • Каждая программа указывает, когда обработка началась — даже если обработка может быть «ничего не делать».
  • Каждая программа указывает, что она успешно завершила свою работу, даже если никакой работы не было выполнено.
  • Все остальное является ошибкой, останавливает программу и сообщает чисто.

В результате каждая программа начинает выглядеть как придуманный Hello World, прежде чем происходит что-то еще. В моем последнем предпенсионном проекте я разработал библиотечный компонент, который по требованию должен был быть полностью функциональным без каких-либо побочных эффектов. Еще до того, как я запустил саму библиотеку, я написал обвязку командной строки, подобную той, которую я только что описал. Обвязка написала что-то вроде Done, когда была установлена ​​пустая библиотека. Первое, что я написал в библиотеке, — это главная точка входа, которая, в свою очередь, содержала только утверждение о том, что она не реализована. Как это помогло мне? Это продемонстрировало, что настройка среды была в порядке, команды make (ну, то, что делала базовая система) были в порядке, и основные принципы построения библиотеки соблюдались. Тогда может начаться настоящая работа.

В следующем выпуске будет начата настоящая работа над Golf.