Исходный код составляется согласно строгому синтаксису, где каждому элементу отводится свое место и не допускаются пунктуационные ошибки, поскольку он загружается в программу-компилятор, которая берет исходный код и транслирует его в последовательности элементарных операций (объектный код), подлежащих исполнению реальной (или виртуальной) машиной. Компилятор не может гадать, что программист имел в виду в той или иной строке исходного кода, поэтому исходный код должен точно говорить компилятору, какие команды исполнять, однако компилятор может выполнять эти задачи множеством разных способов и умеет выбирать наиболее эффективный способ, исходя из обстоятельств. Одни компиляторы работают лучше других: к примеру, если загрузить одну и ту же программу (в исходном коде) в два разных компилятора, объектный код, выданный одним компилятором, может исполняться гораздо быстрее, чем объектный код, выданный другим компилятором. Допустим, вы написали шахматную программу, загрузили ее исходный код в два разных компилятора и запустили две скомпилированных версии играть друг против друга на одном компьютере. Хотя обе версии будут “обдумывать одни и те же мысли в одном и том же порядке” (у них нет другого выбора – у них одинаковый исходный код), возможно, одна из них всегда будет выигрывать, просто потому что она обдумывает эти мысли быстрее, используя меньшее количество базовых машинных циклов, а следовательно, просчитывает за отведенное время большее количество ходов!
Вернемся к нашему лифту. Как только компилятор скомпилировал объектный код, этот код может быть исполнен (это исполняемый файл, обычно в расширении. exe) реальной (или виртуальной) машиной. Возможно, придется исправить ряд ошибок (вернуться к исходному коду, поправить его, снова скомпилировать программу и т. д.), но в итоге получится “законченный”продукт. Его можно будет “вшить”в ПЗУ на крошечный чип, содержащий универсальную машину – и неограниченное количество виртуальных машин в придачу к ней, – и установить его в лифт. Установка предполагает подключение всех преобразователей входящих сигналов, включая сигналы кнопок, вмонтированных в пол весов, измеряющих общую массу пассажиров, и других структурных элементов лифта, и привязки исходящих сигналов к исполнительным механизмам (которые управляют двигателями, открывающими и закрывающими двери, поднимающими и опускающими кабину, а также обновляют информацию на дисплеях и проигрывают записи). Тадам! Машина заменила настоящего человека – даже не метафорического гомункула. И машина следует тем же правилам, что и лифтер. Неужели? На самом деле нет. Она вроде как следует тем же правилам. Она занимает промежуточное положение между человеком, который запоминает – то есть в буквальном смысле моделирует в своей голове – правила, диктующие его поведение, чтобы снова и снова сверяться с ними, и планетами, которые “подчиняются” уравнениям, изящно описывающим их орбиты. Мы, люди, тоже часто занимаем промежуточное положение, когда усваиваем или доводим до автоматизма следование ряду четких правил, которые впоследствии можем отбросить и даже забыть (“жи” и “ши” пиши через “и”, “ча” и “ща” пиши через “а”). Порой мы также вроде как следуем правилам, которые еще не выведены окончательно, например некоторым правилам русской грамматики, по-прежнему сбивающим с толку лингвистов. Скажем, лингвисты сегодня тщетно пытаются написать учебник хорошего разговорного русского языка, в то время как любой десятилетний носитель русского каким-то образом умудряется установить и избавить от ошибок довольно хорошую версию объектного кода для своей РВМ[33]!
Прежде чем пойти дальше, обратите внимание, что комментарии к исходному коду, помогающие программистам отслеживать назначение всех взаимосвязанных элементов программного обеспечения, не имеют аналогов в процессе создания аппаратного обеспечения, прошивки и программного обеспечения нашего мозга. Когда естественный отбор устанавливает в наш мозг различные функциональные структуры, они напоминают лишенный комментариев код: у них есть четкое назначение, но это назначение не объясняется никакими ярлыками. Впрочем, если бы они и были, мозг все равно был бы не в состоянии их понять. (Подробнее об этом в главе 40.) Без комментариев и объяснений остаются и изменения, происходящие в процессе развития и обучения. Подобно лингвистам, мы отчаянно бьемся в попытках провести обратное конструирование всех этих “правил” и “процедур”. Эта задача даже сложнее обратного конструирования объектного кода с целью восстановления исходного кода (не считая комментариев), но теоретически она выполнима.
Резюме