похоже на стиль литературного программирования, поскольку тут вы можете интерполировать объекты и совсем не думать о том, чтобы разбить ее на подпрограммы.
Кнут: Верно. Вместо того чтобы вызывать подпрограмму, мы как будто имеем встроенные подпрограммы на протяжении всей программы. Идея подпрограммы по-прежнему используется, но в итоговом варианте вашей программы подпрограмм нет. Это больше походит на макросы. Но суть в том, что на уровне понятий механизм вызова подпрограмм не нужен, если у вас есть иной способ реализации этой функции на языке, который вы используете.
Что касается Adventure, то не думаю, что я на самом деле убрал подпрограммы из программы Дона Вудса, написанной на Фортране, - я взял его программу на Фортране и переписал ее на английском и на Си. Но абсолютно верно то, что если вы взглянете на мой код ТеХ, то увидите, что подпрограмм в стеке около 4 -5, тогда как в программе, написанной кем-либо другим - без применения методов литературного программирования, - их может быть и 50, и 100.
Я пытаюсь работать с блоками, которые соотносятся с моим мыслительным процессом, а не подчиняюсь каким-то логическим принципам работы с формальной системой. Я хочу, чтобы мои программы соответствовали моему интуитивному процессу их создания, а не чьей-либо жестко закрепленной схеме.
Естественно, в конечном счете она должна быть перенесена на компьютер, у которого есть жесткие рамки, четкие правила понимания. Но в моем представлении хорошая и правильная программа должна наиболее точно передавать образ моих мыслей, а не принцип работы компьютера. Мне нужно найти способ перехода из одного состояния в другое, но я стремлюсь сохранять исходные тексты ближе к своей голове, чем к компьютеру.
Кроме того, я убежден, что литературное программирование - это очень эффективный способ ведения документации, обеспечивающий связь между группами людей. Многие люди понимали код ТеХ настолько хорошо, что могли создавать сценарии, при которых в программе происходил сбой. Мне кажется, что еще больше людей в какой-то из моментов своей жизни понимали именно эту программу, а не любую другую программу схожего размера.
Сейбел: И тем не менее сталкивались ли вы когда-нибудь с такими ситуациями, в которых люди читали ваш код и все равно задавали вам вопросы, заставлявшие вас думать: “Неужели они действительно тут что-то не поняли?”
Кнут: Конечно. Такое происходит постоянно, но лишь из-за недостаточно высокого качества моих объяснений. Позвольте привести простой пример. В “Искусстве программирования” я пишу о первых применениях побитовых операций, и у меня есть такое предложение: “Компьютер Manchester Mark 1, созданный примерно в то же время, что и EDSAC, использовал не только побитовые операторы И, но также ИЛИ и исключающее ИЛИ. В его первом руководстве программиста, написанном в 1950 году, Алан Тьюринг отмечал, что побитовый оператор НЕ может быть получен с помощью исключающего ИЛИ или в сочетании с несколькими подобными операторами”.
То есть во фразе “В его первом руководстве программиста Алан Тьюринг...” речь идет о первом руководстве программиста для компьютера Manchester Mark 1. Но четверо или пятеро человек, прочитавших это предложение, независимо друг от друга заявили, что поняли фразу в том смысле, что в 1950 году Алан Тьюринг написал свое первое руководство программиста.
На самом деле у него были и другие руководства по программированию, поэтому я написал все правильно, но фраза была неправильно истолкована людьми. Поэтому я исправил ее следующим образом: “В первом руководстве по программированию для компьютера Mark I, написанном в 1950 году, Алан Тьюринг отмечал...”
То же касается и чисто математических моментов - ко мне обращаются люди, которые их не понимают. Поэтому я скажу так: как правило, я все пишу верно, но знаю, что мне тем не менее нужно эти моменты исправлять и улучшать.
Сейбел: Как правило, публикуя литературную программу, вы публикуете ее финальную версию. Кроме того, вам часто приписывается авторство фразы “Преждевременная оптимизация - корень всех зол”. Но на момент создания финальной версии программы уже нельзя говорить о преждевременности - возможно, некоторые части были оптимизированы весьма эффективно. Но не усложняет ли это чтение кода?
Кнут: Нет. Хорошая литературная программа всегда покажет свою историю. Хорошая литературная программа всегда скажет: “Вот очевидный способ выполнения данной задачи - почему бы нам им не воспользоваться?”
Когда вы вводите более тонкие детали в свою программу, литературное программирование показывает себя во всем блеске, потому что это не просто код, выполняющий поставленные задачи, это еще и документация. Вы говорите: “Здесь был использован запрещенный прием, он работает, потому что...” — и расписываете в мельчайших подробностях причины и основные положения.
Я использую запрещенные приемы по двум причинам. Во-первых, если это действительно повысит производительность моей программы и если это повышение производительности действительно необходимо. Или иногда я думаю: “Это решение кажется не совсем чистым. Не могу сегодня ничего с собой поделать - хочется использовать этот запрещенный прием, потому что это так клево”. То есть чисто для своего удовольствия. В любом случае я все это документирую, а не просто использую в программе и все.
Сейбел: То есть это чаще встречается не в коде, а в тексте?
Кнут: Да, речь о текстовой части. Я не показываю код, который исключил из программы. Хотя мог бы.
Сейбел: Есть ли в CWEB возможность включать код, не являющийся частью приложения? Тогда можно не документировать этот момент, а просто делать комментарий: “Это действительно очень простая версия данной функции”.
Кнут: У вас просто есть код, который не используется. Он упоминается в документации с пометкой, что этот код нигде не используется.
Сейбел: То есть на этот фрагмент вы просто нигде не ссылаетесь?
Кнут: Да. Кроме того, у меня есть код, который я могу вызвать из отладчика. Я могу сказать: “Нужно вызвать то-то и то-то с такими-то и такими-то параметрами”. Подпрограмма никогда не вызывается непосредственно из самой программы, но она всегда есть в документации. Поэтому я могу остановить выполнение программы посередине, вызвать эту подпрограмму - она осмотрится, как идут дела, как все обстоит на более масштабном уровне.
Сейбел: И точно таким же образом вы можете написать: “Раздел первый - это простейшая реализация данного алгоритма; раздел второй - слегка модифицированная версия первого раздела; раздел третий - вот его мы действительно используем, но вы его никогда не поймете, если не прочтете первые два раздела”.
Кнут: Именно. У меня есть несколько программ в Интернете, которые решают головоломку “15”. И я использую 3 разные версии. Я говорю: “Прочитайте версию номер один, иначе вы никогда не поймете версию номера два. И прочитайте версию номер два, иначе вы никогда не поймете версию номер три”.
Мне приходилось писать самые разные программы. Иногда я работаю над программой, для которой производительность - последнее дело; важно лишь получить ответ. В таком случае я применяю метод грубой силы - тогда мне совсем не нужно думать, поскольку не требуется придумывать никаких тонких решений, и я точно не перемудрю. При таких обстоятельствах ни о какой преждевременной оптимизации не может быть и речи.
Затем я могу внести кое-какие изменения и посмотреть, согласуется ли новая версия с моим методом грубой силы. После чего могу масштабировать программу и переходить к более крупным случаям. Работа с большинством программ заканчивается на этом этапе, потому что вы не будете запускать ее триллион раз. Делая иллюстрацию для “Искусства программирования”, я могу менять ее несколько раз, и переводчикам моей книги также, может быть, придется переделывать эту программу, но абсолютно не важно то, что я ее рисую с помощью очень небыстрого метода, потому что мне нужно сгенерировать этот файл лишь один раз - после чего я передаю его своему издателю, и он попадает на страницы книги.
Но в данный момент я работаю над комбинаторными алгоритмами, которые по определению