16+
ComputerPrice
НА ГЛАВНУЮ СТАТЬИ НОВОСТИ О НАС




Яндекс цитирования


Версия для печати

Модуль поиска не установлен.

Программирование и восприятие

17.01.2011

Рустам Галеев


Жизнь подчас оказывается фантастичнее самого буйного воображения. Писатели-фантасты в 1970-х и даже 80-х годах представляли себе в картинах будущего далекие космические путешествия, новое устройство общества, столкновения цивилизаций и т.п. Но никто не смог догадаться о том, что произошло через какие-то 20 лет - и не просто произошло, а радикально изменило всю жизнь человечества - с появлением персонального компьютера и последовавшим за этим бурным взрывом Интернета. Цифровые технологии проникли всюду и на все наложили свою печать. Цифровые изображения, цифровой звук, цифровое видео, глобальные цифровые коммуникации. Даже в таких, казалось бы, далеких от техники гуманитарных областях, как психология, произошли значительные изменения, и это касается не только инженерной психологии. Когнитивная психология, изучающая "божий дар" человека - его разум, использует в исследовании психических процессов в качестве основы "компьютерную метафору".

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

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

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

Первый контакт

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

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

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

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

Имена

Конечно, набирать 'B8 9E 03' гораздо проще и быстрее, чем '10111000 10011110 00000011'; тем не менее, человеку сложно работать с бессмысленной информацией - в этом еще одно огромное различие между человеком и компьютером. Вместо набора цифр, кодирующих те или иные действия ЭВМ, человек стал использовать мнемонические обозначения этих выполняемых машиной действий - у команд появились имена. Возникает язык мнемокодов - ассемблер. Но расплата за это небольшое удобство оказалась существенной. Теперь путь от написания программы до ее исполнения стал двухэтапным: написанную программу необходимо было еще откомпилировать, т.е. перевести в машинные коды.

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

Процесс именования оказался чрезвычайно плодотворным. Забегая вперед, отметим, что в современном программировании создание имен приобрело чудовищные размеры. Именуется все, что только можно; появились даже имена для самих имен - алиасы. Самый очевидный пример - интернетовские адреса: ради возможности создания удобочитаемых имен пришлось даже создавать дополнительную службу DNS. Теперь браузер клиента должен сначала обратиться к базе данных DNS, получить от нее цифровой адрес, соответствующий данному имени, затем направить запрос (уже второй раз!) по этому полученному адресу - и все это усложнение ради простого удобства простоты восприятия!

Специфическое выражение нашли имена в области хранения информации. Как известно, у современных компьютеров есть память двух типов: оперативная и дисковая. Я говорю "современных", потому что относительно недавно были еще времена, когда данные вводились в компьютер с перфолент и перфокарт, а выводились на них же или распечатывались на рулонах бумаги. Помню, будучи юными (тогда) программистами, мы проходили практику в ВЦ, где каждому в соответствии с его статусом полагались соответствующие квоты на всякого рода ресурсы, в т.ч. и на распечатки. Нам, как неопытным юнцам, не разрешалось использовать более 1 м бумажной ленты (размер листинга указывался в параметрах программы; если этого программе не хватало, она печатала в конце: "Дай метры АЦПУ"). А вот работавшему там же нашему научному руководителю, член-корреспонденту АН СССР А.П.Ершову, полагалось 200 м - предмет нашей зависти.

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

Языки высокого уровня

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

Все это побудило к попыткам создания "универсального" языка программирования. Действительно, поскольку все равно написанную программу приходится перед запуском компилировать, не проще ли создать единообразный синтаксис команд, повысив заодно уровень их обобщенности - например, включив в него такие операторы, которые соответствовали бы целым наборам часто встречающихся команд? Одним из первых таких языков, которые стали называть (в отличие от ассемблера) "языками высокого уровня", стал фортран (от FORmula TRANslation). Как показывает само название, язык предназначался для быстрого создания программ для различного рода математических расчетов.

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

Тем не менее, языки высокого уровня имеют ряд общих черт. Все они скрывают излишние для существа логики выполняющейся программы (ее алгоритма) детали, позволяя сосредоточиться на более крупных смысловых единицах. Отсюда пошло другое название языков высокого уровня - алгоритмические языки. (Кстати, именно отсюда алгол получил свое название - ALGOrithmic Language).

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

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

Типы

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

Говоря кратко, тип - это способ интерпретации данных. Допустим, в какой-то ячейке памяти находится значение '10010001'. Что оно означает? Целое число 145? Но целое отрицательное число -111 имеет в машинной памяти такое же представление. В расширенной кодировке IBM так обозначается символ "?"; для кодовой страницы 866 этому значению соответствует русская буква "С", а в кодировке Windows - символ "'". Чтобы еще больше запутать дело, добавим, что это же значение представляет собой однобайтную команду процессора 'XCHG CX,AX'. Это может быть и адресом ячейки памяти, в которой хранится нужное нам значение или даже команда, которой нужно передать управление. При этом мы ничего не упоминали о числах с плавающей запятой и разного рода данных, соответствующих оцифрованным изображениям, звуку, видео и т.д. К тому же, здесь мы привели лишь один байт, тогда как для размещения переменной может потребоваться и два, и четыре байта, и даже участок памяти размером в несколько килобайт или мегабайт.

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

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

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

Помимо этого "глобального" деления, внутри каждого из них существует еще огромное количество более мелких делений. Например, небольшие целые числа можно записывать в один байт, для обычных вычислений использовать два байта, а для точных - четыре. При этом, если мы знаем, что нам нужны только натуральные числа, мы можем записывать в байт значения от 0 до 255 (беззнаковые); а если нужно использовать и отрицательные числа, то старший бит становится указателем знака числа, а помещаемое в ячейку значение может быть от -128 до +127. Точно так же разные формы записи могут использоваться и для записи чисел с плавающей точкой; это же относится и к символическим данным - символы могут кодироваться и одним байтом, и двумя (как в кодировке Unicode), и тремя, и даже переменным числом байтов.

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

Структурное программирование

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

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

Оператор GOTO объявляется вне закона; ссылки с его помощью считаются дурным тоном. Появляются специальные методы, такие как метод Ашкрофта-Манна, для переделки неструктурных алгоритмов в структурные. Передача управления должна осуществляться теперь лишь с помощью условных (IF-ELSE) и циклических (FOR и WHILE) операторов. Группы операторов, выполняющих единое смысловое действие, изолируются в процедурах. Программирование в значительной степени становится процедурным; и процедуры стоят того, чтобы рассмотреть их подробнее.

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

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

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

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

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

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

Аналогичное понятие было изобретено в области хранения информации: появились каталоги. Каталог фактически представлял из себя файл, в котором записывались сведения о других файлах. Таким образом, он как бы "содержал" эти файлы в себе, как папка содержит бумаги (а Microsoft, начиная с Windows 95, действительно стала называть каталоги папками). Причем, каталог мог содержать в себе и другие каталоги, что также дало возможность создавать иерархические структуры хранящихся данных - появляется настоящая файловая система.

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

Объектно-ориентированное программирование

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

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

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

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

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

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

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

Предположим, нам необходимо создать пользовательский интерфейс к базе данных со сведениями о различных товарах. Для получения доступа к записям базы данных создадим базовый класс Goods, в котором определим общие для работы со всеми типами записей функции, например, previous() и next(). Для различных категорий товаров создадим подклассы: для аудиозаписей - Audio, для книг - Books, для автомобилей - Auto. Во всех этих подклассах будут присутствовать унаследованные от родительского класса Goods функции previous() и next(). Кроме того, каждый производный класс может определить свои специфические функции: например, play() для прослушивания отрывка аудиозаписи, abstract() для показа аннотации книги, features() для отображения технических характеристик автомобиля. А как быть с получением доступа к этим специализированным функциям и отображением общих сведений о товаре? Как раз в этом случае можно воспользоваться полиморфными функциями. Для этого в базовом классе (Goods) объявляется виртуальная функция, например, virtual show(). Затем каждый производный класс переопределяет эту функцию, т.е. создает собственную реализацию этой функции. Теперь в нашем приложении мы можем создать переменную класса Goods и воспользоваться ее методами previous(), next() и show() для "пролистывания" характеристик товаров, при этом функция show() для каждого типа товара будет использовать свой собственный формат отображения, позволяющий получить доступ к специфическим функциям соответствующего подкласса - play(), abstract() или features().

Теперь представьте себе, что у нас появилась новая категория товаров - программное обеспечение - и для ее отображения мы создаем новый подкласс Software, который позволяет посредством функции load() загрузить демо-версию программы через сеть. Нам достаточно в новом подклассе реализовать эту функцию, а также переопределить базовую функцию show(), и наше приложение без всякой переделки сможет работать с новой категорией товаров. Обратите внимание, что на момент написания нашего приложения реализации функции show() для категории товаров "Программное обеспечение" не существовало, тем не менее, старый код может ее успешно вызывать! Именно для этого и служит механизм виртуальных функций, реализуемый через упомянутую ранее таблицу виртуальных функций (отсюда, кстати, название и самой таблицы). В самом деле, реализация функций хранится отдельно от объектов, а в структуре данных объекта размещены указатели на эти функции. Это позволяет при создании объекта заместить адрес родительской виртуальной функции адресом переопределенной функции. В результате действительный тип (класс) данных на момент компиляции определить невозможно, он определяется лишь в момент исполнения программы: клиент может "думать", что он вызывает функцию show() родительского класса Goods, тогда как фактически исполняется переопределенная функция с таким же именем и набором параметров одного из подклассов.

Концепция объектно-ориентированного программирования утверждает, что модули программ (реализованные в виде классов) должны представлять объекты реального мира. То, что раньше реализовывалось в алгоритме программы через ее код, теперь должно быть отражено в виде структуры классов. Удачно выразил эту мысль А.Цимбал: "Важнейшей особенностью ООП является наличие непосредственно в программе (а не в голове программиста) иерархии моделей, из которых строится программа". Если раньше контекст программы, придававший смысл ее работе, зачастую действительно оставался в голове у программиста и никак не выражался (не считая попыток описать его на обычном человеческом языке в виде комментариев), то теперь он должен быть выражен явно в виде системы взаимосвязанных классов. С этой точки зрения программирование усложнилось: необходимость описать контекст программы в виде структуры модулей увеличила интеллектуальную составляющую труда программиста. Из программирования вычленяется относительно независимый вид деятельности - объектно-ориентированное проектирование; в реализации же самих методов классов намечаются тенденции к упрощению. Появляется, например, такая рекомендация: код метода должен быть компактным, удобным для непосредственного восприятия, и не должен превышать 25 строк, т.е. одной страницы экрана - чтобы реализацию всего метода можно было видеть одномоментно. Эти тенденции получили дальнейшее подкрепление с появляением графических операционных систем и последующим созданием визуальных интегрированных сред разработки (IDE).

Визуальные среды разработки

Важнейшей победой симультанного зрительного восприятия человека в "борьбе" с последовательностью обработки двоичной информации компьютером явилось создание первых графических операционных систем - MacOS, затем Windows и OS/2. Решающую роль в этом сыграло появление персонального компьютера. Если раньше к ЭВМ обращались лишь те, кому это действительно было нужно - в первую очередь программисты, привыкшие к общению с машиной посредством искусственных языков программирования, - то теперь компьютер стал "персональным", и использовать его (физически) мог любой желающий, даже незнакомый с программированием вообще; но это потребовало дальнейшего "приспособления" компьютера к особенностям привычного для человека способа восприятия.

Другим важнейшим стимулом для графического интерфейса пользователя явилась многозадачная, но не многопользовательская операционная система. Звучит странно? Разберемся.

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

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

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

Широкая экспансия персоналок сначала в офисах, затем и в быту привела к появлению широчайшего разнообразия прикладных приложений, работающих с самой разнородной информацией. Графическая основа ОС позволила "рисовать" не только изображения, но и текст: возникшее огромное разнообразие шрифтов и возможности автоматической проверки правописания позволили компьютеру практически вытеснить пишущие машинки. Пакеты приложений, объединяемых под вывеской "Office", берут на себя значительную часть конторской работы. К этому времени развивается мультимедиа, что еще более приближает пользовательские интерфейсы к привычным для человека способам восприятия. Компьютер становится средством не только работы, но и отдыха: теперь на нем можно прослушивать любимые музыкальные компакт-диски, просматривать видеоклипы и даже телепрограммы, не говоря уже о компьютерных играх. Компьютер превращается в своего рода интегрирующий центр.

В то же время значительно усиливается интегрирующая роль ОС. Прикладные программисты окончательно изолируются от аппаратного обеспечения: теперь все взаимодействие с "железом" должно осуществляться исключительно при посредничестве Windows. Значительную часть программирования стандартных действий операционная система также берет на себя; программистам предоставляются тысячи функций, составляющих интерфейс прикладного программирования (Application Programming Interface - API). Результатом явилась стандартизация пользовательского интерфейса: за отображение окон отвечала операционная система, а ввод-вывод осуществлялся посредством единообразных действий через систему меню, панелей инструментов, полос прокрутки и строк состояния. Windows предлагала даже встроенные в систему диалоговые окна для осуществления стандартных действий, таких как открытие или сохранение файла, выбор шрифта или цвета, поиск и замену слов в тексте. Графический интерфейс становится интуитивным: теперь пользователю, даже впервые видящему компьютер, достаточно было посидеть пару минтут за экраном монитора и произвести несколько пробных действий, чтобы самостоятельно начать делать что-то осмысленное. Пользователь, умеющий работать с одним приложением Windows, без труда мог начать работать и с другим. Интуитивности пользовательского интерфейса в значительной степени способствовала система всплывающих подсказок, изменение формы курсора в зависимости от действий, которые можно проделать с указываемым объектом, а также развитая система контекстной и концептуальной справки. Кстати, справка была сделана в виде гипертекста, т.е. содержала систему перекрестных ссылок и внедренные изображения, что явилось прообразом для создания в будущем WWW.

В еще большей степени интеграционная роль Windows возросла в связи с продвижением Microsoft документно-ориентированного подхода. Раньше каждый вид данных требовал своего приложения, работающего с ним. Например, если бы нам было нужно распечатать документ, содержащий текст, рисунки, фотографии, таблицы, графики и диаграммы, нам пришлось бы распечатывать его на отдельных листах с использованием "своих" программ: для текста - Word, для рисунка - CorelDraw, для фотографии - PhotoPaint, для таблицы - Excel, для графиков и диаграмм - Graph; для каждого типа данных необходимо помнить соответствующее приложение, запускать его, отдельно сохранять его файлы и т.д. - весьма неудобно с точки зрения пользователя. Возникает идея создания составных документов, т.е. таких, в которых можно хранить одновременно разные типы данных и работать с ними из одного и того же приложения. Но при этом мы сталкиваемся с проблемой.

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

Такая технология действительно появляется под названием OLE - Object Linking and Embedding (внедрение и связывание объектов). Первоначально она представляла собой набор правил (спецификацию) для взаимодействия программных компонентов без участия пользователя при создании составных документов. Но чтобы унифицированным образом обращаться с объектами, имеющими различное содержание, они должны были иметь некие стандартные способы взаимодействия; и такой основой, на которой была построена сама OLE, становится первая компонентная модель - COM. COM регламентировала создание, уничтожение и взаимодействие независимых друг от друга программных компонентов - объектов - посредством своих интерфейсов.

На самом деле, это положило начало неоднозначности и путанице с термином "объект". Объекты COM - совсем не то же самое, что объекты как экземпляры классов. Компонент COM - это структура более высокого порядка, чем класс; компонент COM может быть реализован в виде нескольких классов, тогда как один класс не может содержать несколько компонентов. Это отражено и в самой аббревиатуре COM - Component Object Model - модель составного объекта: объект COM является "составным", т.е. может содержать в себе несколько интерфейсов, каждый из которых реализуется обычно в языке программирования через свой класс. Возвращаясь к нашей аналогии с текстом, можно сказать, что компоненты COM - это целые "главы", составленные из нескольких абзацев. В свою очередь, из этих "глав" конструировались законченные произведения - прикладные программы.

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

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

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

Этот подход оказался настолько привлекательным, что подобные же системы создаются и на гораздо более "солидном" фундаменте: появляются Visual C++, Delphi; в дальнейшем любая система разработки строится по подобному же принципу. В интегрированную среду разработки (Integrated Development Environment - IDE) объединяются набор мастеров для создания заготовок самых различных приложений, визуальный дизайнер с палитрами компонентов, редактор кода с окнами всплывающих подсказок в соответствии с контекстом набираемого кода, разнообразные редакторы ресурсов, компилятор, отладчик и различные вспомогательные утилиты - программирование в значительной степени сводится к выбору различных вариантов из предлагаемых подсказок.

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

Интернет

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

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

Теперь компоненты графического пользовательского интерфейса можно внедрять в документы наравне с прочими объектами, а также пересылать их по сети - элементы управления OLE превращаются в элементы ActiveX. Наряду с ними в документах WWW начинают широко употребляться сценарии. Языки сценариев - это языки высокого уровня интерпретируемого типа, т.е. перевод в машинные коды осуществляется в ходе работы программы; отдельного этапа компиляции не существует. Это требует дополнительных системных ресурсов и замедляет работу программы; несмотря на это, сценарии широко используются в Интернете - с одной стороны, интерпретируемая программа более независима от платформы, а с другой, в отличие от откомпилированной программы, код сценария гораздо более удобен для восприятия и понятен человеку. Подобный же язык был создан для обращения к многочисленным базам данных - SQL.

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

Еще одним явлением того же рода является недавно появившийся язык "смысловой" разметки XML. Человек в очередной раз пытается дать свой ум "напрокат" компьютеру - чтобы у последнего появилась возможность оперировать большими объемами информации на основе смысловых признаков, а не просто для отображения ее на экране или на бумаге. В то же время XML, как и язык разметки документов - HTML, использует для собственно разметки формат, предназначенный для непосредственного восприятия человеком - хотя это значительно увеличивает необходимый для этого объем информации. Так, для кодирования одного признака с помощью тега требуется как минимум 7 байт - даже если в теге используется всего одна буква, угловые скобки и обязательный закрывающий тег с косой чертой добавляют еще 6 символов; при двоичном же кодировании можно было бы обойтись одним, в крайнем случае, двумя байтами.

Что дальше?

Таким образом, человек "навязывает" компьютеру собственный стиль восприятия и общения - не только на конечном этапе взаимодействия путем создания пользовательских интерфейсов прикладных программ, но и влезая во внутреннюю "кухню" вычислительных систем при программировании, зачастую порождая при этом дополнительные технические проблемы. Объектно-ориентированное программирование и интегрированные среды разработки привели к появлению относительно самостоятельного вида деятельности - объектно-ориентированного анализа и проектирования, особого унифицированного языка моделирования (UML) для этой цели и созданию на этой основе компьютерных систем разработки приложений (CASE-систем). Задачей человека теперь становится анализ объектов реального мира, построение на его основе визуальных моделей с использованием CASE-системы, которая автоматически генерирует на базе созданных моделей необходимый для программы код на том или ином языке программирования. Если развитие и дальше продолжится в том же направлении, можно предположить, что программирование в традиционном смысле, с использованием языков программирования, может вообще исчезнуть.

Фантазируя, можно было бы попытаться угадать, в каком направлении пойдет дальше развитие компьютерных технологий. В настоящее время они тесно сомкнулись с проблемами искусственного интеллекта. Так, Интернет сделал ощутимой проблему доступа к ресурсам на различных языках - обостряется проблема машинного перевода, которая, в свою очередь, сдерживается неспособностью компьютеров к семантическому анализу естественной речи человека. При обращении к базам данных также пытаются использовать программы-оболочки для перевода запросов на обычном языке в SQL-запросы. Аналогичная проблема имеет место и при автоматической проверке орфографии и оптическом распознавании текста, не говоря уже о речевом вводе и выводе: существующие словари являются механическими, содержащими все возможные формы слова и не способные исправлять ошибки в соответствии с контекстом. Естественный речевой интерфейс актуален и для развития экспертных систем.

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



статьи
статьи
 / 
новости
новости
 / 
контакты
контакты