Текст книги "ГЕДЕЛЬ, ЭШЕР, БАХ: эта бесконечная гирлянда"
Автор книги: Даглас Хофштадтер
Жанры:
Философия
,сообщить о нарушении
Текущая страница: 26 (всего у книги 64 страниц)
ДОБАВИТЬ слово, указанное в команде, к регистру. (В этом случае данное слово интерпретируется как число.)
НАПЕЧАТАТЬ слово, указанное в команде, в виде букв. (В этом случае данное слово интерпретируется не как число, а как строчка букв.)
ПЕРЕЙТИ к слову, указанному в команде. (В этом случае ЦП интерпретирует данное слово, как следующую команду.)
Если первоначальная команда не содержит явного указания поступить иначе, ЦП просто обращается к следующему слову и интерпретирует его, как команду. Иными словами, ЦП предполагает, что он должен двигаться вдоль по «улице» последовательно, как почтальон, интерпретируя слово за словом как команды. Однако это последовательное движение может быть прервано некоторыми командами, такими как, например, ПЕРЕХОД.
Язык машины и язык ассемблера
Вы только что прочитали очень краткий обзор машинного языка. В этом языке типы существующих операций составляют конечный репертуар, который не может быть расширен. Таким образом любая программа, какой бы большой и сложной она не была, должна состоять из этих типов команд. Рассматривать программу, написанную на машинном языке, это все равно что рассматривать молекулу ДНК атом за атомом. Если вы вернетесь к рис. 41, где изображена последовательность нуклеотидов молекулы ДНК (и имейте в виду, что в каждом нуклеотиде около двух дюжин атомов) и представите себе, что вам надо записать, атом за атомом, ДНК крохотного вируса (уж не говоря о человеке!), то вы получите представление о том, что такое создание сложной программы на машинном языке и каково пытаться понять, что происходит в этой программе, если у вас есть доступ только к ее описанию на машинном языке.
Надо сказать, что первоначально программирование делалось на еще более низком уровне, чем машинный язык: соединялись определенные провода, так что нужные операции как бы «телеграфировались» машине. Этот процесс настолько примитивен по современным понятиям, что теперь его трудно себе вообразить. И все же люди, впервые это сделавшие, безусловно испытали такую же радость, какую когда-либо чувствовали создатели современных компьютеров…
Перейдем теперь на более высокую ступень иерархии уровней описания программ – уровень языка ассемблера. Между машинным языком и языком ассемблера дистанция не так уж велика; скорее, это маленький шажок. Главное здесь то, что между командами на языке машины и командами на языке ассемблера существует взаимно однозначное соответствие. Язык ассемблера представляет отдельные команды машинного языка в виде «блоков», так что, желая например, записать команду сложения, вместо последовательности битов «010111000» вы пишете просто ДОБАВИТЬ, и вместо того, чтобы давать адрес в двоичном коде, вы можете указать на слово в памяти, назвав его по имени. Следовательно, программа на языке ассемблера – это что-то вроде программы на машинном языке, сделанной более удобной для людского чтения. Машинную версию программы можно сравнить с деривацией ТТЧ, записанной в туманной нотации Гёделевых номеров, в то время как версия на языке ассемблера сравнима с изоморфной деривацией ТТЧ, записанной в более легкой для понимания первоначальной нотации самой ТТЧ. Или, возвращаясь к образу ДНК: разница, существующая между машинным языком и языком ассемблера подобна разнице между определением нуклеотидов при помощи их кропотливого, атом за атомом, описания и определением нуклеотидов по именам (как, например, «A», «G», «С» или «Т»). Подобная операция «превращения в блоки» представляет собой огромную экономию труда, хотя концептуально почти ничего при этом не меняется.
Программы, переводящие программы
Возможно, что самое важное в языке ассемблера – не его отличие от машинного языка, которое не столь уж велико, но сама идея того, что программы вообще могут быть написаны на различных уровнях. Ведь компьютерная аппаратура построена так, чтобы «понимать» программы на машинном языке – последовательности битов – а не буквы и не числа в десятичной записи! Что происходит, когда в эту аппаратуру вводится программа на языке ассемблера? Это напоминает попытку заставить клетку узнать бумажку с записанном буквами нуклеотидом, вместо самого нуклеотида со всеми его химическими компонентами. Что делать клетке с этой бумажкой? Что делать компьютеру с программой на языке ассемблера?
Здесь мы подошли к главному: возможно написать на машинном языке программу-переводчик. Эта программа, под названием ассемблер, берет имена, десятичные числа и другие сокращения, которые программист может легко запомнить, и превращает их в монотонные, но необходимые последовательности битов. После того, как программа на языке ассемблера собрана (то есть переведена), она – точнее, ее эквивалент на машинном языке – выполняется компьютером. Однако здесь это лишь вопрос терминологии; программа какого уровня выполняется машиной? Вы не ошибетесь, сказав, что выполняется программа на машинном языке поскольку в выполнении любой программы всегда задействована аппаратура – но вполне разумно также предположить, что выполняется программа на языке ассемблера. Например, вполне можно сказать: «В данный момент ЦП выполняет команду „ПЕРЕХОД“», вместо того, чтобы говорить «В данный момент ЦП выполняет команду „111010000“». Пианист, играющий ноты G-E B-E-B-G. в то же время играет арпеджио в ми миноре. Нет причин отказываться от описания вещей с точки зрения высших уровней. Таким образом, можно считать, что программа на языке ассемблера выполняется одновременно с программой на машинном языке то, что происходит в ЦП, можно описать двумя способами.
Языки высших уровней, компиляторы и интерпретаторы
На следующем уровне иерархии крайне важная идея о том, что сами компьютеры можно заставить переводить программы с высших на низшие уровни, развивается еще далее. В начале 1950-х годов, когда программа ассемблера уже использовалась в течение нескольких лет, было подмечено, что существуют несколько характерных структур, появляющихся в программе за программой. По-видимому, так же как и в шахматах, это были некие характерные структуры, естественно возникающие тогда, когда люди пытаются найти алгоритмы – точные описания процессов, которые они хотят осуществить. Иными словами, кажется, что в алгоритмах есть некие компоненты высшего уровня, при помощи которых они могут быть описаны с большей легкостью и эстетизмом, нежели на весьма ограниченном машинном языке или языке ассемблера. Обычно такой компонент высшего уровня в алгоритме представляет собой не одну-две машинных команды, но целый конгломерат; при этом эти команды не обязательно соседствуют в памяти. Подобный компонент может быть представлен на языке высшего уровня как некое единство, или блок.
Оказывается, что кроме стандартных блоков (только что открытых компонентов, из которых могут быть построены все алгоритмы), почти все программы содержат еще большие блоки – так сказать, суперблоки. Эти суперблоки меняются от программы к программе, в зависимости от типа задания на высшем уровне, которое данная программа должна выполнить. Мы уже говорили о суперблоках в главе V, употребляя общепринятые названия, «подпрограммы» и «процедуры». Ясно, что если бы удалось определить новые единицы высшего уровня в терминах уже известных единиц и затем вызывать их по имени, это было бы важнейшим дополнением к любому языку программирования. Таким образом разделение на блоки оказалось бы включено в сам язык. Вместо определенного репертуара команд, из которых пришлось бы кропотливо собирать каждую программу, программист смог бы создавать свои собственные модули. Каждый из них имел бы собственное имя и мог бы использоваться в любом месте программы, как если бы он был неотъемлемой характеристикой языка. Разумеется, невозможно избежать того, что внизу, на уровне машинного языка, все будет состоять из прежних команд на этом языке; но они будут неявны и не будут видны программисту, работающему на высшем уровне. Новые языки, основанные на этих идеях, были названы языки-компиляторы. Один из самых первых и элегантных получил имя «АЛГОЛ» (от английского Algorithmic Language – алгоритмический язык). В отличие от языка ассемблера, здесь нет взаимно-однозначного соответствия между высказываниями на АЛГОЛе и командами на машинном языке. Однако некий тип соответствия между АЛГОЛом и машинным языком все же существует, хотя оно гораздо более запутано, чем соответствие между языком ассемблера и машинным языком. Грубо говоря, перевод программы на АЛГОЛе в ее машинный эквивалент сравним с переводом словесного выражения алгебраической проблемы на язык формул. (На самом деле, переход от словесного выражения задачи к её выражению в формулах гораздо более сложен, но он дает определенное представление о типе «распутывания», необходимом при переводе с языка высшего уровня на язык низшего уровня ) В середине 1950-х годов были созданы удачные программы под названием компиляторы, функцией которых был перевод с языка-компилятора на машинный язык.
Были изобретены также интерпретаторы. Подобно компиляторам, интерпретаторы переводят с языков высших уровней на машинный язык, но вместо того, чтобы сначала переводить все высказывания и затем выполнять машинный код, они считывают одну строчку и тут же ее выполняют. Преимущество здесь в том, что для использования интерпретатора не обязательно иметь полную программу. Программист может придумывать свою программу строчка за строчкой и проверять ее в процессе создания. Интерпретатор по сравнению с компилятором – то же, что устный переводчик по сравнению с переводчиком письменных текстов. Один из самых интересных и важных языков программирования – это ЛИСП (от английского List Processing – обработка списка), изобретенный Джоном Маккарти примерно тогда же когда был изобретен АЛГОЛ. Впоследствии ЛИСП приобрел большую популярность среди специалистов по искусственному интеллекту.
Между принципом работы интерпретаторов и компиляторов есть одно интересное различие. Компилятор берет входные данные (к примеру, законченную программу на Алголе) и производит некий результат (длинную последовательность команд на машинном языке). На этом его работа закончена и результат вводится в компьютер для обработки. Интерпретатор, напротив работает непрерывно, пока программист вводит одно за другим высказывания ЛИСПа, каждое из них немедленно выполняется. Однако это не означает что каждое высказывание сначала переводится и затем выполняется – тогда интерпретатор был бы всего лишь построчным компилятором. Вместо этого в интерпретаторе операции считки новой строчки, ее «понимания» и выполнения переплетены – они происходят одновременно.
Поясню эту идею немного подробнее. Как только новая строчка ЛИСПа вводится в интерпретатор, он пытается ее обработать. Это означает, что интерпретатор начинает действовать, и в нем выполняются некие машинные команды. Какие именно – это зависит, разумеется, от данного высказывания ЛИСПа. Внутри интерпретатора много команд типа ПЕРЕХОД, так что новая строчка ЛИСПа может заставить контроль двигаться довольно сложным путем вперед, назад, затем опять вперед и т. д. Таким образом, каждое высказывание ЛИСПа превращается в некий «маршрут» внутри интерпретатора, и следование по этому маршруту приносит нужный эффект.
Иногда бывает полезно интерпретировать высказывания ЛИСПа как некие блоки данных, которые постепенно вводятся в непрерывно действующую программу машинного языка (интерпретатор ЛИСПа). Думая об этом таким образом, вы по-иному видите отношения между программой, написанной на языке высшего уровня, и исполняющей эту программу машиной.
Самонастройка
Разумеется, компилятор, будучи программой, должен быть сам написан на каком-либо языке. Первые компиляторы были созданы на языке ассемблера вместо машинного языка; таким образом полностью использовались преимущества подъема на одну ступеньку над машинным языком. Краткое описание этих довольно сложных понятий представлено на рис. 58.
РИС. 58. Как ассемблеры, так и компиляторы – это переводчики на машинный язык. Это указано прямыми линиями. Более того, поскольку они сами являются программами, они первоначально также создаются на каком-либо языке программирования. Волнистые линии указы указывают на то, что компилятор может быть написан на языке ассемблера, а ассемблер – на машинном языке.
По мере того, как программирование становилось более изощренным, было замечено, что частично законченный компилятор может быть использован для того, чтобы компилировать собственные продолжения. Иными словами, когда создано определенное минимальное ядро компилятора, это минимальное ядро может переводить большие компиляторы на машинный язык, пока таким образом не создастся окончательный, полный компилятор. Этот процесс известен под именем «самонастройки»; он несколько напоминает достижение ребенком критического уровня владения своим родным языком, после чего его словарь и грамматическое мастерство растут как снежный ком, так как для изучения языка он может использовать сам язык.
Уровни описания работающих программ
Языки компиляторов обычно не отражают структуры машин, на которых будут выполняться написанные на этих языках программы. Это одно из их основных преимуществ по сравнению с весьма специализированными языками ассемблера и машинным языком. Разумеется, когда программа на языке компилятора переводится на язык машины, получается программа, зависящая от машины. Таким образом, возможно описать программу, которая исполняется либо зависящим от машины путем, либо не зависящим, подобно тому, как мы можем описать абзац в книге по его содержанию (описание, не зависящее от издания) или по номеру страницы и его расположению на ней (описание, зависящее от издания).
Пока программа работает хорошо, то, как мы ее описываем и что мы о ней думаем, не столь важно. Но как только возникают неполадки, становится важным умение увидеть программу на разных уровнях. Если, например, компьютеру дана задача в какой-то момент разделить на нуль, он остановится и сообщит пользователю о возникшей проблеме, указав при этом, в каком месте программы произошло это неприятное событие. Однако эти детали часто сообщаются на более низком уровне, чем тот, на котором написана сама программа. Вот три параллельных описания забуксовавшей программы:
Уровень машинного языка:
«Выполнение программы прекратилось по адресу 1110010101110111»
Уровень языка ассемблера:
«Выполнение программы прекратилось, когда она дошла до команды РАЗДЕЛИТЬ».
Уровень языка компилятора:
«Выполнение программы прекратилось в момент оценки алгебраического выражения „(А + B)/Z“».
Одна из основных задач программистов (людей, которые создают компиляторы, интерпретаторы, ассемблеры и другие программы, которые затем используются многими людьми) – это создание находящих ошибки подпрограмм. Необходимо, чтобы информация, которую эти подпрограммы выдают пользователю, в чьей программе обнаружен дефект, представляла бы описание проблемы на высшем, а не на низшем, уровне. Интересно, что если сбой обнаруживается в генетической «программе» (например, мутация), то происходит обратное, ошибка бывает заметна только на высшем уровне, то есть на уровне фенотипа, а не генотипа. На самом деле, современная биология использует мутации, как одно из основных окон в мир генетических процессов, поскольку они могут быть прослежены на многих уровнях.
Микропрограммирование и операционные системы
В современных компьютерных системах есть несколько других уровней иерархии. Например, некоторые системы – часто называемые «микрокомпьютерами» – используют еще более рудиментарные команды на машинном языке, чем добавка числа в памяти к числу в регистре. Пользователь должен сам решать, какой тип команд на обычном машинном языке он хочет запрограммировать; он «микропрограммирует» эти команды в терминах имеющихся у него «микрокоманд» После этого разработанные им команды на языке высшего уровня могут быть включены в схему компьютера и стать частью аппаратуры, хотя это и не обязательно. Подобное микропрограммирование позволяет пользователю спуститься немного ниже уровня обычного машинного языка. Одним из следствий этого является то, что какой-либо компьютер одной фирмы может, путем микропрограммирования, быть снабжен такой аппаратурой, что она повторяет машинные команды другого компьютера той же (или даже иной) фирмы. При этом говорится, что компьютер с микропрограммой имитирует другой компьютер.
Далее, у нас имеется уровень операционной системы, который расположен между уровнями программы на машинном языке и следующим уровнем, на котором программирует пользователь. Операционная система – это программа, предотвращающая доступ пользователей к самой машине (и таким образом защищающая систему); эта программа избавляет пользователя от многих сложных и запутанных проблем, таких, как прочтение программы, вызов программы-переводчика, выполнение переведенной программы, направление результата по нужным каналам в нужное время и передача контроля следующему пользователю. В случае, когда с ЦП говорят сразу несколько пользователей, операционная система переключает внимание ЦП в определенном порядке. Операционные системы удивительно сложны; здесь я только намекну на эти сложности при помощи следующей аналогии.
Рассмотрим первую телефонную систему. Александр Грэхем Белл мог позвонить своему ассистенту в соседнюю комнату: электронная передача голоса! Это сравнимо с простым компьютером без операционной системы: электронные вычисления!
Рассмотрим теперь современную телефонную систему. У вас есть выбор, с каким телефоном соединиться; к тому же, можно отвечать на многие звонки одновременно. Вы можете добавить код и соединиться с другими районами. Вы можете позвонить прямо или через оператора; так, что звонок будет оплачен вашим собеседником или по вашей кредитной карточке. Можно говорить с одним человеком или сразу с несколькими; можно «перенаправить» или проследить звонок. Существует сигнал «занято», сигнал, говорящий вам, что набранный номер не является «хорошо сформированным» и сигнал, говорящий вам, что вы набирали номер слишком долго. Вы можете установить местный коммутатор, соединяющий несколько телефонов, – и так далее, и тому подобное. Это удивительный список, если подумать, сколько возможностей он представляет, в особенности, по сравнению с былым чудом «голого» телефона. Вернемся теперь к компьютерам: сложные операционные системы выполняют примерно те же операции направления трафика и переключения уровней по отношению к пользователям и их программам. Мы можем быть практически уверены в том, что у нас в мозгу происходят некие параллельные процессы, одновременная обработка многих стимулов; решения о том, что должно выйти на первый план и на какое время; мгновенные «перерывы» из-за неожиданных событий и критических положений и так далее.
Забота о пользователе и защита системы
Многие уровни сложной компьютерной системы, взятые вместе, облегчают пользователям их работу, позволяя им не думать о процессах, происходящих на низших уровнях (которые, скорее всего, для них совершенно неважны). Пассажир в самолете обычно не интересуется уровнем горючего в баках, скоростью ветра, количеством куриных крылышек, которые будут поданы на ужин пассажирам, или воздушным трафиком около места назначения. Все это – дело служащих на разных уровнях иерархии авиакомпании; пассажир же хочет только одного: чтобы его доставили из одного места в другое. Только когда случается что-нибудь непредвиденное, например, потеря багажа, пассажир понимает, с какой запутанной системой уровней он имеет дело.
Компьютеры – супергибкость или супержесткость?
Одной из основной целей в нашем стремлении к высшим уровням всегда было желание сообщать компьютеру о том. чего мы от него хотим, самым естественным для нас образом. Безусловно, конструкции высшего уровня в языках компиляторах ближе к категориям, в которых обычно думают люди, чем конструкции низшего уровня, такие, как в машинных языках. Но в этом стремлении к легкости общения с компьютерами мы обычно забываем об одном из аспектов «естественности», – а именно, том факте, что общение между людьми имеет намного меньше ограничений, чем общение между человеком и машиной. Например, мы зачастую произносим бессмысленные словосочетания, ища, как бы получше выразить свою мысль, кашляем в середине фразы, перебиваем друг друга, используем двусмысленные описания и «неправильный» синтаксис, придумываем выражения и искажаем смысл – но наши сообщения обычно все же достигают цели. В языках программирования, напротив синтаксис должен быть стопроцентно строгим, в них не должно быть двусмысленных выражений и конструкций. Интересно, что печатный эквивалент кашля разрешен, но только если он предварен условным знаком (например, словом КОММЕНТАРИЙ), после него также должен иметься условный знак (например, точка с запятой). Ирония в том, что эта небольшая уступка гибкости создает свои проблемы: если точка с запятой (или любой другой условный знак, отмечающий конец комментария) встречается внутри комментария, программа переводчик интерпретирует ее, как сигнал окончания комментария, после чего следует полная неразбериха.
Представьте, что в программе определена процедура под названием ПОНИМАНИЕ, и эта процедура затем вызвана семнадцать раз. Если в восемнадцатый раз это слово ошибочно написано ПОМИНАНИЕ, горе программисту! Компилятор взбунтуется и напечатает весьма неприятное послание ОШИБКА, сообщая, что он никогда не слыхал ни о каком ПОМИНАНИИ. Часто, когда компилятор обнаруживает подобную ошибку, он пытается продолжить работу, но из-за отсутствия у него поминания, он не может понять, что имел в виду программист. На самом деле, он может даже вообразить, что тот имел в виду нечто совершенно другое, и начать действовать согласно этой ошибочной интерпретации. В результате, остальная программа будет усеяна посланиями «ошибка», потому что компилятор – а не программист – запутался. Вообразите, какая путаница получится, если, переводя с английского на русский, переводчик услышит фразу по-французски и попытается переводить остальной английский текст, как французский! Компиляторы часто запутываются таким жалким образом. C'est la vie.
Может быть, это звучит как приговор компьютерам, – но я вовсе не имел это в виду. В некотором смысле, такое положение вещей необходимо. Если подумать, для чего обычно используются компьютеры, становится ясно, что они выполняют весьма определенные и точные задания, которые слишком сложны для людей. Чтобы мы могли доверять компьютерам, необходимо, чтобы они совершенно точно, без следа двусмысленности, понимали, что от них требуется. Необходимо также, чтобы компьютер делал не больше и не меньше того, что ему приказано. Если между компьютером и программистом стоит программа, предназначенная угадывать, чего тот хочет или имеет в виду, то весьма вероятно, что, когда программист попытается сообщить машине задачу, она будет понята совершенно неверно. Таким образом важно, чтобы программы высшего уровня хотя и удобные для людей, тем не менее были бы недвусмысленными и точными.
Как предвосхитить желания пользователя
Несмотря на это, возможно создать язык программирования который допускает некоторый тип неточности и программу, переводящую его на низшие уровни. Можно сказать, что программа-переводчик при этом будет пытаться интерпретировать нечто, сделанное «вне правил языка». Но если в языке допускаются некие «нарушения» правил, подобные нарушения уже нельзя назвать настоящими нарушениями, поскольку они включены в правила! Если программисту разрешено допускать определенный тип ошибок, он может использовать эту черту, зная, что при этом он оперирует строго в рамках правил, несмотря на видимость обратного. Иными словами, если пользователь знает о всех трюках, делающих программу-переводчика более гибкой и удобной для пользования, то он знает и предел, который он не может перейти; следовательно, ему эта программа все равно кажется жесткой и негибкой, хотя она и дает ему гораздо большую свободу по сравнению с ранними версиями, не включавшими «автоматическую компенсацию человеческих ошибок.»
По отношению к «эластичным» языкам подобного типа может быть две альтернативы: (1) пользователь знает о встроенных в язык и в программу-переводчика уступках; (2) пользователь о них не знает. В первом случае, язык может быть использован для точного сообщения программ, поскольку программист может предсказать, как компьютер будет интерпретировать программы, написанные на этом языке. Во втором случае, в языке есть скрытые черты, могущие выкинуть что-нибудь непредсказуемое с точки зрения пользователя, не знающего о том, как работает программа-переводчик. Результатом этого могут быть грубые ошибки в интерпретации программы, поэтому такой язык не годится для использования компьютеров за их быстроту и надежность.
На самом деле, есть и третья альтернатива; (3) пользователь знает о встроенных в язык и в программу-переводчик отклонениях от правил, но их так много и они взаимодействуют таких сложным путем, что что он не может предсказать, как будут интерпретированы программы. Это может быть сказано о человеке, написавшем переводящую программу; он, разумеется, знает ее структуру как никто другой – и все же он не может предсказать того, как она будет реагировать на данный тип необычной конструкции.
Одна из основных областей исследования в сегодняшней науке об искусственном интеллекте называется автоматическим программированием; автоматическое программирование работает над созданием языков еще более высоких уровней, языков, переводящие программы которых смогут проделывать хотя бы некоторые из следующих удивительных вещей: обобщать на основе примеров, исправлять типографские или грамматические ошибки, пытаться понять двусмысленные описания, при помощи упрощенной модели пытаться угадывать, что на уме у пользователя, задавать вопросы, когда машине что-то непонятно, использовать человеческий язык и т. д. Может быть, со временем удастся найти компромисс между гибкостью и строгостью.
Прогресс искусственного интеллекта – это прогресс языка
Удивительно, насколько прогресс в исследовании компьютерной техники (и в частности, искусственного интеллекта) связан с развитием новых языков. В последнее десятилетие возникла ясная тенденция: воплощать новые открытия в новых языках. Один из ключей к пониманию и созданию интеллекта лежит в постоянном развитии и улучшении языков, описывающих процессы манипуляции символами. На сегодняшний день имеется около трех-четырех дюжин экспериментальных языков, созданных исключительно для исследований по искусственному интеллекту. Важно понимать, что любая программа, написанная на одном из этих языков, в принципе может быть переведена на языки низших уровней, хотя это и потребовало бы от людей огромных усилий; получившаяся программа была бы такой длинной, что она оказалась бы за пределами человеческого понимания. Это не означает, что каждый высший уровень увеличивает потенциал компьютера; весь этот потенциал уже существует в наборе команд машинного языка. Просто новые понятия на языках высшего уровня по самой своей природе наводят на мысль о новых путях и перспективах.
«Пространство» всех новых программ настолько обширно, что никто не может представить себе всех возможностей. Каждый язык высшего уровня предназначен для исследования определенных районов «программного пространства», таким образом, используя данный язык, программист оказывается в соответствующем районе. Язык не заставляет его писать программы именно такого типа, но облегчает для него выполнение определенных задач. Близость к понятию и небольшой толчок – вот все, что обычно требуется здесь для крупного открытия; именно поэтому исследователи стремятся к языкам еще более высоких уровней.
Программирование на разных языках подобно сочинению музыкальных произведений в различных тональностях, особенно если вы работаете на клавиатуре. Если вы уже выучили или написали произведения во многих ключах, каждая клавиша будет иметь для вас свою собственную эмоциональную окраску. Некоторые мелодии кажутся естественными в одном ключе, но неловкими в другом. Таким образом, ваше направление определяется выбором тональности. В некотором роде, даже энгармонические тональности, такие как до-диез и ре-бемоль, весьма отличаются по настроению. Это говорит о том, что система нотации может играть важную роль в том, как будет выглядеть конечный продукт.
«Стратифицированная» схема искусственного интеллекта показана на рис. 59; внизу лежат компоненты аппаратуры, такие, как транзисторы, а на вершине расположены «думающие программы». Эта иллюстрация взята из книги «Искусственный интеллект» Патрика Генри Винстона (Patrick Henry Winston, «Artificial Intelligence»), она представляет собой общепринятый среди специалистов взгляд на искусственный интеллект. Хотя я согласен с идеей, что ИИ должен быть стратифицирован подобным образом, мне не кажется, что с таким небольшим количеством уровней возможно получить думающие программы.
РИС. 59. Для создания думающих программ необходимо построить серию уровней аппаратуры и программного обеспечения, чтобы избежать мучений работы со всеми процессами только на низшем уровне. Описания одного и того же процесса на разных уровнях весьма отличаются друг от друга, и только самый высший уровень представлен в блоках, достаточно крупных для нашего понимания. [Взято из книги «Искусственный интеллект» П. Г. Винстона (Р.Н. Winston, «Artificial Intelligence»).]
Между уровнем машинного языка и уровнем, на котором может быть достигнут настоящий интеллект, должна, по-моему убеждению, лежать еще дюжина (или даже несколько дюжин!) уровней, каждый следующий из которых, базируясь на предыдущем, в то же время был бы более гибким. Сейчас мы с трудом можем себе вообразить, как это будет выглядеть…
Параноик и операционная система
Схожесть уровней в компьютерной системе может привести к странному их смешению. Однажды я был свидетелем того, как пара моих приятелей – оба новички в компьютерном деле – забавлялись на терминале с программой «PARRY». PARRY – это печально известная программа, симулирующая параноика весьма простеньким образом: она выдает заранее заготовленные английские фразы, выбранные из широкого репертуара. Правдоподобие достигается тем, что программа может определить, какие именно «заготовки» могут звучать разумно в ответ на фразы, введенные в компьютер человеком.