КаталогИндекс раздела
НазадОглавлениеВперед


Некоторые фундаментальные понятия

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

Не слишком необычным - хотя и ошибочным - является рассмотрение задачи программиста как изготовления программ. (Некоторые находят такие термины как "производство программного обеспечения" (software manufacturing), предлагают измерять производительность труда программиста количеством строк кода в месяц и т.п., хотя я никогда не видел того, кто рекомендовал бы измерять производительность труда композитора количеством нот, написанных в его партитуре за месяц!) Эта ошибка является сердцем неудачи управления, которая бывает так очевидной во многих программных проектах. Это ошибка, поскольку действительный предмет деятельности программиста - не программа, им составленная, но класс возможных вычислений, который может быть выполнен ею, "изготовление" которого делегируется машине. Представляется более плодотворным описание деятельности программиста как "разработки класса вычислений", а не "изготовление программы". В этой связи я должен иметь в виду, что другие существенные утверждения о программе - напр., о ее корректности или о ее требованиях к ресурсам - несомненно имеют отношение скорее к вычислениям, чем к последней вещи, которая выходит из рук программиста, то есть, к тексту программы. По этой причине при введении фундаментальных понятий я начну со стороны вычислений, с "событий во времени".

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

Требование, что действие имеет место в конечный период времени наиболее существенно: оно подразумевает, что мы можем говорить о моменте T0, когда действие начинается, и о более позднем моменте T1, когда действие заканчивается. Мы предполагаем, что конечный результат действия может быть обнаружен сравнением "состояния в момент T0" с "состоянием в момент T1".

Примером действия может быть домохозяйка, чистящая картошку для ужина. Конечный результат состоит в том, что картошка для ужина в момент времени T0 не очищена и находится, скажем, в корзине в подвале, а в момент времени T1 очищена и находится, скажем, в кастрюле, в которой она будет вариться.

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

Всякий раз, когда допускается такое разбиение, мы можем рассматривать одно и то же явление либо как действие, либо как последовательный процесс. Когда наш интерес ограничен конечным результатом, состояниями "до и после", мы можем рассматривать его как действие. Если, однако, нас интересует одно или более промежуточных состояний, мы рассматриваем его как процесс. В последнем случае момент T0 совпадает с началом первого поддействия, а конец каждого поддействия совпадает с началом следующего, за исключением последнего поддействия, конец которого совпадает с T1, концом всего явления.

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

Явление хозяйки, чистящей картошку, например, описывается временной последовательностью следующих поддействий хозяйки:

"приносит корзину из подвала;
 достает кастрюлю из шкафа;
 чистит картошку;
 возвращает корзину в подвал"

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

Мы постулируем, что в каждом явлении мы можем распознать шаблон поведения или коротко шаблон; явление происходит, когда осуществляется этот шаблон. Конечный результат явления полностью определятся шаблоном и (возможно) начальным состоянием (т.е., состоянием на момент T0). Разные явления могут следовать одному и тому же шаблону; если эти явления устанавливают разный конечный результат, то конечный результат должен определять также и начальным состоянием, и соответствующие начальные состояния должны быть разными.

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

Мы вернемся на минуту к нашей домохозяйке. В какой-то день она чистила картошку на ужин, и у нас есть отчет наблюдателя об этом явлении. На следующий день она опять чистит картошку на ужин, и второе явление дает отчет наблюдателя, эквивалентный предыдущему. Можем ли мы сказать без дальнейших забот: "Очевидно, что поскольку два отчета эквивалентны друг другу для обоих случаев, она делала точно одно то же."?

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

Два действия хозяйки различаются между собою настолько же, насколько различаются платья: они имеют как явления, как минимум, одно различие: одно имело место вчера, другое сегодня. Поскольку каждая картофелина может быть очищена только один раз, картофелины, вовлеченные в два явления, имеют также разную идентификацию; в первый раз корзина могла быть более полной, чем во второй, количество очищенных картофелин могло различаться и т.д.

И все же эти два явления настолько подобны, что один и тот же отчет наблюдателя может быть принят как адекватный в обоих случаях и что мы готовы дать одно и то же название обоим явлениям (напр., "чистка картошки на ужин").

Алгоритм является описанием шаблона поведения, выраженным в терминах хорошо понимаемого, конечного набора именованных действий (так называемых, "примитивов") о которых априорно предполагается, что они могут быть выполнены (т.е., могут быть применены в явлении).

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

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

В нашем определении алгоритма мы должны подчеркнуть, что примитивные действия должны быть выполняемыми, что они могут быть выполнены. "Перейти на другую сторону площади" - прекрасно приемлемое действие, однако, "пойти к черту" является не алгоритмом, а ругательством, поскольку оно не может быть выполнено.

Кроме того, мы должны подчеркнуть, что набор действий должен быть хорошо понимаемым: между тем, кто составляет алгоритм и тем, кто намеревается ему следовать, не должно быть непонимания этого набора. (В этом отношении шаблоны для вязания являются, как правило, превосходными, рецепты имеют среднее качество, тогда как инструкции, получаемые, когда мы спрашиваем дорогу, обычно чрезвычайно плохи!) Насколько существенным может быть этот недостаток понимания, возможно, лучше всего можно продемонстрировать рецептом тушеного зайца, который был в старой голландской кулинарной книге, в переводе на английский этот рецепт выполняется так: "Взять зайца и приготовить из него тушеного зайца". Рецепт не является совершенно неверным, но он вряд ли полезен!

Давайте теперь сравним отчет наблюдателя о сеансе чистки картошки:

"приносит корзину из подвала;
 достает кастрюлю из шкафа;
 чистит картошку;
 возвращает корзину в подвал"

с соответствующим алгоритмом - набором инструкций, который хозяйка, скажем дает новой служанке:

"принести корзину из подвала;
 достать кастрюлю из шкафа;
 почистить картошку;
 вернуть корзину в подвал" 

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

Чего мы достигли? Ну, ничего, пока мы ограничиваем себя алгоритмами, которые могут быть получены - как в нашем примере - конкатенацией именованных действий, которые выполняются в заданном порядке. При таком ограничении отчет наблюдателя о действиях "как они имели место" так же хорош. Но поведение хозяйки (или служанки) может быть чуть усложнено: предположим, что после того, как картошка принесена, она надевает фартук, если нужно, т.е., если она одета в светлую юбку, и что в один день она использует фартук, а в другой день - нет.

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

"приносит корзину из подвала;
 достает кастрюлю из шкафа;
 готовится, в зависимости от одежды;
 чистит картошку;
 возвращает корзину в подвал"
с неявным пониманием того, что "готовится, в зависимости от одежды" включает в себя пустое действие, если ее юбка не светлая, и надевание фартука, если ее юбка светлая. Если, однако, мы хотим больше подробностей и хотим явно упомянуть фартук, то " готовится, в зависимости от одежды" должно быть заменено в отчете наблюдателя в один день на:
"видит, что ее юбка светлая и поэтому надевает фартук"
а в другой день на
"видит, что ее юбка светлая и поэтому пропускает надевание фартука" 
Беда в том, что отчет наблюдателя не может содержать одну фразу:
"надевает фартук, если ее юбка светлая"
поскольку аудитория обязательно спросит "делает она это или нет?" Другими словами: на этой степени детализации мы не можем включить два явления в один и тот же отчет наблюдателя, поскольку на этой степени детализации два явления различаются! В этом становится видимой потенциальная мощь алгоритма, поскольку он может распознать один и тот же шаблон поведения в двух явлениях и описанием этого шаблона поведения мы даем нечто, что применима при обоих обстоятельствах, и при светлой, и при темной юбке. Это возможно благодаря тому факту, что то, что происходит на самом деле при следовании определенному шаблону поведения, может быть совместно определено текущим состоянием дел, когда действие начинается.

Мы видим две вещи: проверку того, светлого цвета юбка или нет и, в зависимости от результата этой проверки, действие "надеть фартук" имеет место или нет. Чтобы выразить это условное выполнение, нам нужен в нашем алгоритме другой связующий элемент вместо точки с запятой. В нашем примере алгоритма (я имею в виду инструкции новой служанке) двоеточие имеет двойную функцию: в тексте оно отделяет одно имя действия от следующего имени действия, но кроме того оно предполагало для явления нечто, технически называемое "управлением последовательностью", т.е., означало, что предполагается, что конечный момент предшествующего действия совпадает с начальным моментом следующего действия. Теперь нам нужен другой связующий элемент, показывающий, будет или нет за проверкой следовать следующее по тексту действие. Мы пишем, например, следующий алгоритм:

"принести корзину из подвала;
 достать кастрюлю из шкафа;
 if юбка светлая do надеть фартук;
 почистить картошку;
 вернуть корзину в подвал" 

(По историческим соображениям, так называемый, условный связующий элемент "if ... do" разделен на два символа if и do, между которыми заключена проверка.) Условный связующий элемент соединяет два действия, первое из которых должно быть, так называемой, "проверкой". Эта проверка описывает состояние дел, которое может быть истинным (true) или ложным (false) ("false" - технический термин для "не true"). Явление, которое имеет место в соответствии с условной структурой

 "if проверка do действие"

может принимать одну из двух взаимно исключающих форм: либо проверка возвращает "true", и за ней следует действие, либо проверка возвращает "false", и при этом полное действие структуры завершено. Алгоритм устанавливает свое превосходство над отчетом наблюдателя из того факта, что он может содержать связующие элементы (такие, как условный), которые предусматривают лучшее управление последовательностью, чем точка с запятой. Нам понадобится дополнительный связующий элемент прежде, чем мы сможем увидеть полное превосходство алгоритма над отчетом наблюдателя, а именно, связующий элемент повторения.

Предположим, что мы хотим выразить, что "чистка картошки" сама является процессом, который имеет дело с одной картофелиной за раз, и что, соответственно, наше примитивное действие называется "почистить следующую картофелину". Если количество картофелин, которые должны быть очищены, является константой, скажем, всегда 25, то мы можем заменить "почистить картошку" на 25 раз "почистить следующую картофелину", разделенных 24 точками с запятой. Но теперь мы предположим, что число картофелин для чистки может различаться изо дня в день; но мы должны распознавать в каждом сеансе чистки один и тот же шаблон поведения. Мы предполагаем, что хозяйка в состоянии заглянуть в кастрюлю и вынести заключение, достаточно ли количество почищенной картошки или нет. Если мы знаем априори, что в наихудшем случае (т.е., много гостей и очень мелкие картофелины) ей все же никогда не придется чистить более 500 картофелин, мы можем дать общий алгоритм, описывающий фактическую чистку, повторением в тексте 500 раз (разделенных 499 точками с запятой) условной структуры

"if число почищенных картофелин недостаточно do почистить следующую картофелину"

К этому решению может быть сделано несколько возражений. Это практическое возражение, что мы должны сжать алгоритм до выполняемых строк. Более того, мы должны сделать фундаментальное предположение, что мы наперед знаем максимальной число. Часто задать такую верхнюю границу априори бывает очень трудно, а если она и может быть задана, то такая верхняя граница обычно бывает во много раз больше среднего значения. И если фактически должно быть почищено 25 картофелин, то 26-я проверка "число почищенных картофелин недостаточно", т.е., первая, которая даст результат "false", даст новую информацию, следующие 474 проверки (которые, как предполагалось, предусмотрены алгоритмом) не дадут новой информации. Как только хозяйка установит, что количество почищенных картофелин более не является недостаточным, она не будет заглядывать в кастрюлю еще 474 раза, чтобы убедиться в этом!

Чтобы удовлетворить это возражение, мы вводим связующий элемент повторения, записываемый, опять-таки из исторических соображений, в двух частях "while ... do". Используя этот связующий элемент, мы можем записать алгоритм:

"принести корзину из подвала;
 достать кастрюлю из шкафа;
 if юбка светлая do надеть фартук;
 while число почищенных картофелин недостаточно do почистить  следующую картофелину;
 вернуть корзину в подвал" 

Процесс, соответствующий

"while проверка do действие"

состоит из одного или более выполнений условного связующего элемента

"if проверка do действие"

а именно, вплоть до и включительно того момента, когда проверка даст результат "false".

Мы можем также описать семантику связующего элемента повторения в терминах условного связующего элемента рекурсивно:

"while проверка do действие"

семантически эквивалентно

"if проверка do 
 begin действие; while проверка do действие end"

Здесь символы "begin" и "end" используются как открывающие и закрывающие скобки соответственно; они являются синтаксическим устройством, показывающим, что условный связующий элемент соединяет проверку (из первой строки) со всей второй строкой: значение, порожденное первой проверкой определяет, будет ли описанное во второй строке (от "begin" до "end") полностью или будет полностью пропущено.

Замечание. Выше я предложил идею алгоритма, с которой начал свое рассмотрение, как класса событий, в которых мы хотим распознавать один и тот же шаблон поведения. В дополнение к точке с запятой как связующему элементу в тексте алгоритма это привело к другим связующим элементам, таким как условный связующий элемент "if ... do" и связующий элемент повторения "while ... do". Не таким уж необычным является подход к отношению между алгоритмом и вычислениями со стороны алгоритма; такой подход ведет на очень раннем этапе к синтаксическим рассмотрениям, в результате которых связующие элементы вводятся в несколько иной терминологии. Вместо

"if проверка do действие"

люди пишут

"if условие do оператор"

Часть текста, обозначенная "if условие do", описывается как "условное выражение", которое рассматривается как префикс, присоединенный к "оператору", вся конструкция, составляющая условное выражение и оператор вместе, называется "условным оператором". Так же и в "while условие do оператор"

"while условие do" называется "выражением повторения", а оператор называется "оператором повторения". Эта терминология используется настолько широко, что - досадуя на ее синтаксическое происхождение - я не буду воздерживаться от ее употребления, если увижу, что она подходит для этого.

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

"принести корзину из подвала;
 достать кастрюлю из шкафа;
 if юбка светлая do надеть фартук;
 while число почищенных картофелин недостаточно do 
     begin почистить следующую картофелину; почистить следующую картофелину;  end
 вернуть корзину в подвал"

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

С одной стороны мы имеем алгоритм, конечный текст, вневременное, статическое понятие; с другой стороны мы имеем соответствующие явления, которые могут быть вызваны им, динамические понятия, события разворачиваются во времени. Тесные отношения между этими двумя понятиями - для которых мы употребляем термин "последовательность" - лежит в основе алгоритмической нотации. (Это то тесное отношение на, которое я ссылался, когда подчеркивал, что настоящей деятельностью программиста является "разработка классов вычислений".) Нотация алгоритма, по общему признанию, является очень мощной; но прежде, чем следовать ей, я позволю себе сделать небольшое отступление, чтобы показать, какую "цену" мы должны заплатить за ее введение.

Мы установили, что ограничиваем себя явлениями, которые имеют место в конечный период времени. Всякий раз, когда мы следуем алгоритму, имеет место явление, в конечном счете, как временная последовательность примитивных действий. Будет естественным постулировать, что каждое примитивное действие будет занимать конечный период времени, не равный нулю: ни одно действие не будет происходить "бесконечно быстро". Это подразумевает, что мы ограничиваем наше внимание явлениями, которые происходят как временная последовательность из конечного числа примитивных действий.

И теперь мы начинаем видеть цену: очень легко написать текст, который выглядит как алгоритм, но не является алгоритмом в нашем смысле слова, поскольку попытка следовать ему окажется бесконечной задачей, например

"while юбка светлая do почистить следующую картофелину"

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

Вопрос о том, является ли текст, выглядящий как алгоритм, корректным алгоритмом или нет, далеко не тривиальный. В действительности Алан М. Тьюринг доказал, что не может существовать алгоритма, который был бы способен проверять любой текст и устанавливать, является он корректным алгоритмом или нет. Предположение о существовании такого алгоритма порождает противоречие, которое будет показано ниже. Предположим, что мы имеем такой алгоритм, проверку

"proper(L)"

которая дает результат "true", если текст с именем "L" является корректным алгоритмом, и "false", если он является некорректным. Теперь рассмотрим следующий текст с именем "L":

L: "while proper(L) do разок свистнуть"

(где предполагается, что "разок свистнуть" - доступный примитив). Если мы начнем следовать этому алгоритму, сколько раз прозвучит свисток? Предположение, что "proper(L)" выдает результат "true" приведет к тому, что алгоритм будет некорректным и наоборот! Вывод: не может существовать алгоритма для проверки "корректности". (Марвин Мински заключает в "Computation, Funite and Infinite Mashines", Prentice Hall, 1967 формальное рассмотрение этого доказательства такой фразой: "Мы можем только глубоко посочувствовать тем читателям, которые не встречали этот тип простого, но поразительного аргумента раньше.")

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

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

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

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

Механизм, который умеет делать только одну вещь (такой, как наиболее распространенный автомат, туалетный бачок), не называется машиной. Для нас существенен связанный с машиной набор действий, возможность принимать шаблоны поведения и действовать в соответствии с ними.

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

Замечание. Тривиальная задача - составить алгоритм для самой быстрой машины в мире, корректный в теоретическом смысле, но в чем-то непрактичный, поскольку он потребует миллиона лет, чтобы выполнить соответствующий ему процесс до конца. Заявление о том, что машина "способна заставлять события происходить" является кое в чем предметом сомнения: на самом деле она не способна на это. В последующем мы не будем беспокоиться о различии между "теоретически возможно" и "практически выполнимо". Не потому, что мы непрактичны, наоборот! То, в чем современные компьютеры являются столь мощными, это класс практически выполнимых вычислений, который пока что достаточно велик - мягко говоря! - чтобы сделать машины очень полезной и увлекательной частью оборудования, вполне заслуживающей нашего внимания.

Мы называем алгоритм, предназначенный для управления поведением машины, программой. Другими словами, мы резервируем название программа для такого алгоритма, который предназначен для механического выполнения. В общей нотации алгоритма мы только требовали, чтобы набор действий был "хорошо понимаемым", не заботясь о том, как это понимание устанавливается: если композитор показывает "Andante" (= "подвижно") для композиции в три четверти, он может это делать, поскольку, совершенно определенно, мы можем ожидать, что такая пометка будет иметь какой-то смысл даже для исполнителя с двумя ногами. В случае машины ситуация кардинально иная, машина является ограниченной частью оборудования, которая по своей конструкции, связана с конечным, очень строго определенным набором действий и, когда ей дают программу, она будет вести себя в точности так, как ей предписано.

То, что машины являются абсолютно послушными слугами, является причиной недовольства многих начинающих программистов. Это послушание, полагают они, делает программирование мучительно трудным, поскольку тривиальная ошибка в программе определенно ведет к совершенно не тому поведению, которое нужно. Невозможность для программиста апеллировать к "здравому смыслу машины" производит впечатление одного из главных ее недостатков. Более опытный программист учится ценить ее рабскую, полную покорность: благодаря ей мы можем заставить ее сделать что-то "необычное"! И это то, чего вы не сможете сделать со слугой, который "округляет" свои инструкции до ближайшей возможной интерпретации.

В предыдущих параграфах я ввел программирование как важную деятельность потому, что сейчас мы имеем машины, которые могут управляться программами и для которых мы должны составлять программы, если мы хотим их использовать, т.е., если мы хотим превратить их в инструмент для решения наших проблем. Но это еще не вся история. Компьютер - вещь многосторонняя. Для его производителей он, прежде всего, товар, который может быть (надеемся) произведен и продан с прибылью. Для многих покупателей-учреждений компьютер, прежде всего, символ положения. Для многих пользователей он либо источник бесконечных беспокойств, либо, в некоторых случаях, очень полезный инструмент. В университетских средах имеется тенденция преобладания взгляда на компьютер, как на полезный инструмент. И они не отрицают, что как мощный инструмент компьютеры изменяют лицо земли (и луны тоже!). И все же я убежден, что мы недооцениваем значимость компьютеров, если мы ограничиваем наши оценки их только упомянутыми аспектами. Это может вызвать потрясение основ нашего общества, но я верю, что в долговременном аспекте это превратится всего лишь в зыбь на поверхности нашей культуры. Я ожидаю значительно более глубокого влияния от появления современного компьютера, а именно, от того, что он способен стать гигантским интеллектуальным вызовом, беспрецедентным в истории человечества.

Компьютер как часть оборудования предоставляет нам совершенно новое сочетание простоты и мощности, которое делает задачу программирования уникальной проблемой. Если инженер-электронщик сделал свою работу правильно, он предоставил программисту математически тривиальное оборудование. Его коды инструкций (его "набор действий") может быть великолепно описан на небольшом количестве страниц, все конечное и дискретное, тут просто нет места для концептуально сложных математических нотаций, таких как непрерывность, бесконечность, ограничения, иррациональные числа и т.п. Математическая основа программирования является очень, очень простой, такой простой, что программирование должно быть легким: должно быть легко понимать программы, должно быть легко убеждаться, что программа корректна и что машина, работающая под ее управлением, будет гарантированно вырабатывать желаемый результат. Из этой базовой простоты у кого-то возникает интуитивное ощущение, что сохранение явлений, вызываемых его программой, строго под его интеллектуальным контролем - вопрос тривиальный.

Но базовая простота - только одна сторона медали: другую сторону представляет исключительная мощность (в отношении и возможностей, и скорости) ныне существующих компьютеров. В результате их исключительной мощности количество информации играет такую же роль в вычислениях, как и количество операций, выполняемых в ходе вычислений, превосходя по величине наши мыслительные способности на несколько порядков. Вследствие ограниченного размера нашего черепа мы абсолютно не в состоянии представить себе с существенной степенью детализации, что мы собираемся привести в действие, и программирование, следовательно, становится деятельностью, сталкивающей нас с концептуальными проблемами, которые далеко, очень далеко превосходят обычный уровень тривиальности.

Именно возможность рассматривать реалистически эффективное решение любой степени сложности, сочетаемая с нашим полным интеллектуальным контролем над тем, что мы рассматриваем, будет иметь глубокое влияние на наши способы мышления и на наше понимание наших мыслительных процессов. В прошлом не было прецедента, когда бы мы сталкивались с чем-то настолько мощным (штормом или армией), чтобы мы не могли эффективно контролировать это. (Постоянно используемое нами определение "мощный" (powerfull) означает, что мы "бессильны" (powerless) контролировать его.)


НазадОглавлениеВперед
КаталогИндекс раздела