Главная страница

Э. Дейкстра Дисциплина программирования. Предисловие редактора перевода


Скачать 1.14 Mb.
НазваниеПредисловие редактора перевода
АнкорЭ. Дейкстра Дисциплина программирования.pdf
Дата31.08.2018
Размер1.14 Mb.
Формат файлаpdf
Имя файлаЭ. Дейкстра Дисциплина программирования.pdf
ТипДокументы
#23816
страница2 из 9
1   2   3   4   5   6   7   8   9
while B do S"
определяют как семантику обращения "whiledo(B, S)"
к рекурсивной процедуре (описанной в синтаксисе языка АЛГОЛ 60):
procedure whiledo(условие, оператор);
begin if условие then begin оператор;
whiledo(условие,оператор) end
end
Несмотря на формальную правильность, это мне неприятно, потому, что я не люблю, когда из пушки стреляют по воробьям, вне зависимости от того, насколько эффективно пушка справляется с такой работой. Для поколения теоретиков машинной математики, которые подключались к этой тематике в течение шестидесятых годов, приведенное выше рекурсивное определение часто явля- ется не только "естественным", но даже "самым правильным". Однако ввиду того, что без понятия повторения мы не можем даже описать поведение машины Тьюринга, представляется необходимым произвести некоторое восстановление равновесия.
По поводу отсутствия библиографии я не предлагаю ни объяснений, ни извинений.
Благодарности. Следующие лица оказали непосредственное влияние на разработку этой кни- ги, либо приняв участие в обсуждении ее предполагаемого содержания, либо высказав замечання относительно готовой рукописи или ее частей: К. Брон, Р. Берсталл, У. Фейен, Ч. Хоар, Д. Кнут,
М. Рем, Дж. Рейнольдс, Д. Росс, К. Шолтен, Г. Зигмюллер, Н. Вирт и М. Вуджер. Я считаю честью для себя возможность публично выразить им мою признательность за сотрудничество. Кроме того,
я весьма обязан корпорации Burroughs, создавшей мне благоприятные условия и предоставившей необходимые средства, и благодарен моей жене за неизменную поддержку и одобрение.
Э. В. Дейкстра
Нейен
Нидерланды

6
Э. Дейкстра. ”Дисциплина программирования”
0 АБСТРАКЦИЯ ИСПОЛНЕНИЯ
Абстракция исполнения лежит в основе всего понятия "алгоритма" настолько глубоко, что обыч- но ее считают само собой разумеющейся и оставляют без внимания. Ее назначение в том, чтобы сопо- ставлять между собой различные вычисления. Иначе говоря, она предоставляет нам способ осмыс- ливания конкретного вычисления как элемента большого класса различных вычислений; мы можем отвлечься от взаимных отличий элементов такого класса и, руководствуясь определением класса в целом, высказывать утверждения, применимые к каждому его элементу, а следовательно, справед- ливые и для конкретного вычисления, которое мы хотим рассматривать.
Чтобы разъяснить, что подразумевается под "вычислением", я опишу сейчас невычислитель- ную конструкцию "получения" (я преднамеренно не употребляю термина "вычисление"), например,
наибольшего общего делителя чисел 111 и 259. Она состоит из двух картонных карточек, располо- женных одна поверх другой. На верхней карточке написан текст "НОД(111, 259) =". Чтобы получить от конструкции ответ, мы поднимаем верхнюю карточку и кладем ее слева от нижней, на которой теперь можно прочесть текст "37".
Простота карточной конструкции является большим достоинством, но она омрачается двумя недостатками — мелким и крупным. Мелкий недостаток состоит в том, что хотя эту конструкцию можно в самом деле использовать для получения наибольшего общего делителя чисел 111 и 259, но помимо этого она мало на что пригодна. Однако крупный недостаток в том, что, как бы тщатель- но мы ни проверяли устройство конструкции, наша вера в то, что она вырабатывает правильный ответ, может основываться только на нашем доверии к ее создателю: он мог ошибиться либо при проектировании своей машины, либо при изготовлении нашего конкретного экземпляра.
Чтобы преодолеть меньшее затруднение, мы могли бы рассмотреть изображение на огромном листе картона большого прямоугольного массива из сетевых точек с целыми координатами x и y,
удовлетворяющими отношениям 0
≤ x ≤ 500 и 0 ≤ y ≤ 500. Для каждой такой точки (x, y) с поло- жительными координатами (т. е. за исключением точек на осях) мы можем выписать в соответству- ющей позиции значение НОД(x, y); предлагается двумерная таблица из 250 000 элементов. С точки зрения полезности это значительное усовершенствование: вместо конструкции, способной выдавать наибольший общий делитель единичной пары чисел, мы имеем теперь "конструкцию", способную выдавать наибольший общий делитель любой пары из 250 000 различных пар чисел. Это много, но особенно радоваться нечему, так как указанный ранее второй недостаток (почему мы должны верить,
что конструкция выдает правильный ответ?) помножился на те же самые 250 000, и теперь от нас тре- буется уже совсем безграничное доверие к ее изготовителю. Поэтому перейдем к рассмотрению другой конструкции. На таком же листе картона с сетевыми точками написаны только числа, пробегающие значения от 1 до 500 вдоль обеих осей. Кроме того, начерчены следующие прямые линии:
1) вертикальные линии (с уравнением x = константа);
2) горизонтальные линии (с уравнением y = константа);
3) диагонали (с уравнением x + y = константа);
4) "линия ответа" с уравнением x = y.
Чтобы работать на этой машине, мы должны следовать следующим инструкциям ("играть по следующим правилам"). Когда хотим найти наибольший общий делитель двух чисел X и Y , мы по- мещаем фишку — также поставляемую изготовителем — в сетевую точку с координатами x = X и y = Y . Коль скоро фишка не находится на "линии ответа", рассматриваем наименьший равнобедрен- ный прямоугольный треугольник, у которого вершина прямого угла совпадает с фишкой, а один из концов гипотенузы (либо ниже фишки, либо слева от нее) находится на одной из осей. (Поскольку фишка не лежит на линии ответа, такой прямоугольный треугольник будет иметь на осях только одну вершину.) Затем фишка перемещается в сетевую точку, совпадающую с другим концом гипоте- нузы. Такое перемещение повторяется до тех пор, пока фишка не достигнет линии ответа. После этого x-координата (или y-координата) окончательного положения фишки является искомым ответом.
Как нам убедиться в том, что эта машина будет выдавать правильный результат? Если (x, y) — лю- бая из 249 500 точек не на линии ответа и (x y ) — точка, в которую передвинется фишка за один шаг игры, то либо x = x и y = y −x, либо x = x−y и y = y. Нетрудно доказать, что НОД(x, y) = НОД(x , y ).
Важный момент здесь состоит в том, что одно и то же рассуждение применяется одинаково верно к любому из возможных шагов! Кроме того, — и опять-таки без труда — мы можем доказать для любой точки (x, y) где x = y (т.е. (x, y) является одной из 500 точек на линии ответа), что НОД(x, y) = x. И
снова важный момент в том, что одинаковое рассуждение применимо к любой из 500 точек на линии ответа. В-третьих,— и вновь это не составит труда — нам нужно показать, что при любом исходном положении (X, Y ) конечное число шагов в самом деле перенесет фишку на линию ответа, и опять важно отметить, что одно и то же рассуждение одинаково применимо к любому из 250 000 исходных положений (X, Y ). Три простых рассуждения, пространность которых не зависит от числа сетевых то-

Э. Дейкстра. ”Дисциплина программирования”
7
чек: эта миниатюра показывает, насколько велики возможности математики. Если обозначить через
(x, y) произвольное положение фишки на протяжении игры, начатой в положении (X, Y ), то первая наша теорема позволяет утверждать, что во время этой игры отношение НОД(x, y) = НОД(X, Y ) будет всегда справедливо, или — выражаясь на соответствущем жаргоне — "оно сохраняет инвариант- ность". Далее, вторая торема гласит, что мы можем интерпретировать x-координату окончательного положения фишки как требуемый ответ, а третья теорема гласит, что такое окончательное положе- ние существует (т. е. будет достигнуто за конечное число шагов) И этим завершается анализ того, что мы могли бы назвать "нашей абстрактной машиной".
Теперь нам остается убедиться в том, что лист, поступивший от изготовителя, является на самом деле правильной моделью абстрактной машины. Для этого нужно проверить нумерацию вдоль обеих осей, а также проверить, правильно ли проведены все прямые линии. Это несколько затруднительно,
так как предстоит исследовать объекты, число которых пропорционально N , где N (в нашем примере
500) — длина стороны квадрата, но все же предпочтительнее, чем N
2
, число возможных вариантов вычисления.
Другая машина могла бы работать не с огромным листом картона, а с двумя девятибитовыми регистрами, в каждом из которых можно запомнить двоичное число от 0 до 500. При этом мы могли бы использовать один регистр для запоминания значения x-координаты, а другой — для запомина- ния значения y-координаты, соответствующей "текущему положению фишки". Перемещение тогда соответствует уменьшению содержимого одного регистра на содержимое другого. Мы могли бы ре- ализовать арифметику самостоятельно, но, разумеется, лучше, если машина сможет делать это за нас. Если мы захотим полагаться на полученный ответ, то нам нужно уметь убеждаться в том, что машина правильно выполняет операции сравнения и вычитания. В уменьшенном масштабе повто- ряется та же история: мы выводим единожды и на все случаи, т.е для любой пары n-разрядных двоичных чисел, уравнения для устройства двоичного вычитания, а затем удостоверяемся в том, что наша физическая машина правильно моделирует это абстрактное устройство. Если это устройство параллельного вычитания, то число проверок — пропорциональное числу элементов и их взаимосвя- зей — пропорционально значению n = log
2
N . В последовательной машине сделан еще один шаг на пути упрощения оборудования за счет расхода времени.
Теперь я попытаюсь, хоть бы для своего собственного просвещения, уловить основной смысл приведенного примера.
Вместо того чтобы рассматривать одиночную проблему вычисления НОД(111, 259), мы обобщили ее и подошли как к частному случаю более широкого класса пpoблeм вычисления НОД(X, Y ), Стоит отметить, что мы могли бы обобщить проблему вычисления НОД(111, 259) по-разному: можно было бы рассматривать эту задачу как частный случай иного, более широкого класса задач, например вычисления НОД(111, 259), НОК(111, 259), 111 × 259, 111 + 259, 111/259, 111 − 259, 111 259
, дня недели для 111-го дня 259-го года нашей эры и т. д. В результате мог бы появиться "процессор для 111 и 259", и для того, чтобы он выдал упомянутый выше ответ, нам следовало бы дать на его вход команду "НОД, пожалуйста". Вместо этого мы предложили "НОД-вычислитель", которому для получения этого ответа потребуется задать на вход пару чисел "111, 259" и это совсем другая машина!
Другими словами, когда требуется выработать один или несколько результатов, обычная прак- тика состоит в том, чтобы обобщить проблему и рассматривать эти результаты как частные случаи некоего более широкого класса. Однако мало радости, если ограничиться утверждением, что любой предмет является частным случаем чего-то более общего. Если мы хотим следовать этому подходу,
то на нас возлагаются две обязанности:
1. Мы должны иметь полную ясность относительно способа обобщения, т. е. должны тщательно выбрать и явно определить более широкий класс, поскольку наши рассуждения должны приме- няться ко всему этому классу.
2. Мы должны выбрать ("изобрести", если вам угодно) такое обобщение, которое окажется полез- ным для наших целей.
В нашем примере я, разумеется, отдаю предпочтение "НОД-вычислителю", а не "процессору для 111 и 259", и сравнение этих двух конструкций дает нам намек на то, какие характеристики делают обобщение "полезным для наших целей". Машина, которая по команде может вырабаты- вать в качестве ответа значения всех видов забавных функций от 111 и 259, становится все более неудобной для проверки по мере того, как растет набор функций. В этом явный контраст с нашим "НОД-вычислителем".
НОД-вычислитель был бы столь же плох, если бы он представлял собой таблицу из 250 000
записей, содержащих "заготовленные" ответы. Его характерное отличие в том, что он может быть задан в форме компактного набора "правил игры", который, если играть в соответствии с этими правилами, обеспечит выдачу нужного ответа.

8
Э. Дейкстра. ”Дисциплина программирования”
Огромный выигрыш состоит в том, что единое рассуждение применительно к этим правилам позволяет нам доказывать существенные утверждения о результатах любого варианта игры. Это достигается ценой того, что при каждом из 250 000 конкретных применений этих правил мы получаем ответ "не сразу": каждый раз игра должна быть сыграна в соответствии с правилами!
Тот факт, что мы в состоянии дать столь компактную формулировку правил игры, когда единое рассуждение позволяет нам выводить заключения о любых вариантах игры, непосредственно связан с систематизированным расположением 250 000 узловых точек. Мы оказались бы беспомощными,
если бы лист картона содержал беспорядочный случайный разброс точек, исключающий какую-либо систематизацию. В нашем случае мы могли бы разделить свою фишку на две половинки и двигать одну половинку вниз, пока она не ляжет на горизонтальную ось, а другую половинку влево, пока она не ляжет на вертикальную ось. Вместо того чтобы воспроизводить с одной фишкой 250 000 возможных положений, мы могли добиться того же с двумя фишками, для каждой из которых нужно только
500 возможных положений, т. е. всего 1000 положений в общей сложности. Мы бы достигли того же уровня в 250 000 позиций, используя то обстоятельство, что любое из 500 положений одной половинки фишки может комбинироваться с любым из 500 положений другой половинки: число положений неразделенной фишки равно произведению числа положений одной половинки на число положений другой. На принятом жаргоне мы говорим, что "общее пространство состояний рассматривается как декартово произведение пространств состояний переменных x и y".
Возможность замены одной фишки с двумерной свободой выбора положения на две половинки с одномерной свободой используется в предложенной выше двухрегистровой машине. С точки зрения технической реализации это представляется весьма заманчивым: требуется только построить реги- стры, способные различать 500 разных случаев ("значений"), а за счет простого объединения этих двух регистров общее число разных случаев возводится в квадрат! Это перемножительное правило позволяет нам различать громадное число возможных общих состояний с помощью ограниченного числа компонентов, у каждого из которых только ограниченное число возможных состояний. По мере добавления таких компонентов размер пространства состояний возрастает экспоненциально, но нам следует иметь в виду, что это допустимо только при условии, что обоснование нашего нововведения остается весьма компактным; если такое обоснование тоже возрастает экспоненциально, то вообще нет никакого смысла создавать такую машину.
Замечание. Убедительную иллюстрацию к сказанному выше можно найти в изобретении, возраст которого уже превысил десять веков: в десятичной системе счисления! Она обладает тем поистине пленительным свойством, что число необходимых цифр возрастает всего лишь пропорционально ло- гарифму максимального из чисел, которые должны быть представлены. Двоичная система счисления
— это то, что получается, когда вы забываете, что на каждой руке имеется по пяти пальцев. (Конец
замечания.)
Выше мы занимались одним аспектом множественности, а именно большим числом позиций фишки (= возможных состояний). Имеется еще одна аналогичная множественность, а именно боль- шое число различных игр (= вычислений), которые могут состояться в соответствии с нашими пра- вилалами игры: по одной игре для каждого начального положения, если говорить точнее. Наши правила игры являются очень общими в том смысле, что они применимы к любому начальному по- ложению. Но мы настаиваем на компактности обоснования правил игры, а это означает, что и сами правила должны быть компактными. В нашем примере это достигалось следующим приемом: вместо перечисления "сделай это, сделай то" мы задали правила игры в виде правил для выполнения "шага"
в сочетании с критерием того, должен ли "шаг" быть выполнен в следующий раз. (На самом деле шаг должен повторяться, пока не будет достигнуто состояние, в котором он становится неопределен- ным.) Иначе говоря, даже всю игру от начала до конца можно произвести с помощью повторяемых применений одного и того же "подправила".
Это очень продуктивный прием. Один алгоритм заключает в себе проект определенного класса вычислений, которые могут выполняться под его управлением; благодаря условному повторению "шага", вычисления из такого класса могут иметь различные протяженности. Этим объясняется,
как короткий алгоритм может занимать машину в течение длительного времени. С другой стороны,
в этом можно усмотреть начальный намек на то, зачем нам могут понадобиться особенно быстрые машины.
Меня завораживает мысль, что эта глава могла писаться еще в времена, когда Евклид мог смот- реть на нее из-за моего плеча.

Э. Дейкстра. ”Дисциплина программирования”
9
1 Роль языков программирования
В главе "Абстракция исполнения" я дал неформальные описания различных "машин", пред- назначенных для вычисления наибольшего общего делителя двух положительных (и не слишком больших) чисел. Одно описание было выражено с помощью фишки, передвигаемой по листу карто- на, другое — с помощью двух половинок фишки, каждая из которых двигалась вдоль своей оси, а последнее — с помощью двух регистров, каждый из которых мог содержать целое число (не превыша- ющее некоторой границы). Физически эти три "машины" весьма различны, однако математически они очень сходны: основная часть доказательства их способности вычислять наибольший общий делитель совпадает для всех трех машин. Это объясняется тем, что они представляют всего лишь раз- личные воплощения одного и того же набора "правил игры", и это именно тот набор правил, который ставляет сущность реального изобретения, известного как "алгоритм Евклида".
В предыдущей главе алгоритм Евклида описывался словесно, не совсем формальным образом.
Однако мы отметили, что число соответствующих ему возможных вычислений столь велико, что нужно доказать его корректность. Коль скоро алгоритм дан только не формально, он не является вполне подходящим объектом для формального рассмотрения. Для дальнейшего нам потребуется описание этого алгоритма в некоторой удобной формальной записи.
Такая формальная запись может обладать многочисленными преимуществами. Любой способ записи подразумевает, что всякий описываемый с его помощью предмет задается как конкретный представитель (часто бесконечного) класса объектов, которые могут быть описаны этим способом.
Haш спсоб описания должен, разумеется, обеспечивать изящное и точное описание алгоритма Ев- клида, но когда мы этого достигнем, алгоритм Евклида окажется в действительности заданным, как представитель огромного класса всех видов алгоритмов. И в описаниях некоторых из этих других ал- горитмов мы сможем найти более интересные применения нашего способа записи. В случае алгоритма
Евклида есть основания утверждать, что он настолько прост, что можно обойтись его неформальным описанием. Сила формальной записи должна проявиться в таких достижениях, которых без нее мы никогда не смогли бы добиться!
Второе преимущество формального способа записи состоит в том, что он дает нам возможность изучать алгоритмы как математические объекты; при этом формальное описание алгоритма служит основой, позволяющей нам интеллектуально охватить этот алгоритм. Благодаря этому мы сумеем доказывать теоремы о классах алгоритмов, например пользуясь тем, что их описания обладают не- которым общим структурным свойством.
Наконец, такой способ записи позволит нам описывать алгоритмы настолько точно, что если будет задан описанный так алгоритм и заданы значения аргументов (вход), то не будет никаких со- мнений относительно того, какими должны быть соответствующие ответы (выход). При этом можно считать, что вычисление выполняется автоматом, который, получив (формально описанный) алго- ритм и аргументы, порождает ответы без дальнейшего вмешательства человека. Такие автоматы,
способные обеспечивать взаимное воздействие алгоритма и аргумента, и в самом деле были по- строены. Они называются "автоматическими вычислителями". Алгоритмы, предназначенные для автоматического выполнения такими вычислителями, называются "программами", и с конца пя- тидесятых годов формальные способы, используемые для записи программ, называются "языками программирования". (Введение термина "язык" применительно к способам записи программ вызы- вает смешанные чувства. С одной стороны, оно оказалось весьма полезным, поскольку существующая теория лингвистики обеспечила естественную основу и устоявшуюся терминологию ("грамматика",
"синтаксис", "семантика" и т. д.) для рассмотрений применительно к этой новой тематике. С другой стороны, необходимо отметить, что аналогия с (ныне именуемыми так) "естественными языками" ча- сто оказывалась дезориентирующей, так как для естественных языков, неформальных по существу,
источником как их слабости, так и их силы является присущая им неопределенность и неточность).
В историческом плане этот последний аспект, т. е. возможность использования языков про- граммирования в качестве средства общения с существующими автоматическими вычислителями,
в течение долгого времени рассматривался как их наиболее важное свойство. Эффективность, с ко- торой существующие автоматические вычислители могли бы выполнять программы, записанные на конкретном языке, становилась главным критерием качества этого языка. Как огорчительное след- ствие мы нередко обнаруживаем, что аномалии существующих вычислительных машин старательно воспроизводятся в языках программирования, причем это происходит в ущерб интеллектуальной управляемости программ, выражаемых на таком языке (как будто программирование и без этих ано- малий не было уже достаточно трудным!). В рамках нашего подхода мы постараемся восстановить равновесие и поэтому будем относиться к возможности фактического выполнения наших алгорит- мов вычислительной машиной только как к счастливой случайности, которая не должна занимать центрального места в наших рассмотрениях. (В одном недавно опубликованном учебном тексте по

1   2   3   4   5   6   7   8   9


написать администратору сайта