Книга: Искусство программирования для Unix

13.3.1. Идентификация проблем сложности

13.3.1. Идентификация проблем сложности

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

Из всех рассмотренных редакторов ed(1) обладает наименьшей сложностью. Почти единственной неортогональной функцией в его командном наборе является то, что многие его команды могут принимать суффиксы "р" или "]" для распечатки или вывода результатов выполнения. Даже после трех десятилетий добавления функций существует менее тридцати команд редактирования, а обычный рабочий набор большинства пользователей включает в себя не многим более десятка команд. Не многое можно удалить из редактора в части необязательной сложности, а случайную сложность вообще трудно идентифицировать. Пользовательский интерфейс редактора ed строго компактен.

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

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

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

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

В противоположность Sam, редактор vi выглядит довольно раздутым и недоработанным. В vi имеются сотни команд, многие из которых дублируются, что в лучшем случае является необязательной сложностью, а возможно, и случайной. Предположительно, большинству пользователей известно не более 5 % командного набора. При сравнении данного редактора с примером Sam возникает закономерный вопрос: почему сложность интерфейса vi настолько высока?

В главе 11 описан результат отсутствия стандартных клавиш перемещения курсора в ранних rogue-подобных программах. Редактор vi был одной из таких программ. Когда он появился, его автор знал, что многим пользователям понадобится возможность использовать традиционные для экранных телетайпов Unix клавиши перемещения курсора. Это сделало модальный интерфейс неизбежным. Как только клавиши h, j, k и l получили зависимые от режима значения в буфере редактирования, слишком быстро проявилась привычка добавлять новые команды уникальным способом.

Редактор Sam, с его зависимостью от растрового дисплея с клавишами-стрелками и мышью, может быть гораздо более четким. И является таковым.

Однако нагромождение команд vi является сравнительно поверхностной проблемой. Это интерфейсная сложность, но большинство пользователей могут проигнорировать и игнорируют ее (интерфейс является полукомпактным в том смысле, как это было определено в главе 4). Более глубокой проблемой является ловушка специального кода. За годы своего существования vi постепенно накапливал все больше встроенного узкоспециального C-кода для выполнения таких задач, которые Sam "отказывается" выполнять, а в редакторе Emacs для этих целей используются модули Lisp-кода и управление подпроцессами. Расширения vi, в отличие от расширений Emacs, не являются библиотеками, загружаемыми по мере необходимости. Пользователи постоянно расплачиваются за распухание кода. В результате различие в размерах между современными версиями редакторов vi и Emacs не такое большое, как можно было бы ожидать. В середине 2003 года на Intel-машине GNU Emacs занимал 1500 Кбайт, тогда как vim занимал 900 Кбайт. Эти 900 Кбайт хранят в себе огромную необязательную и случайную сложность.

Для приверженцев vi отсутствие встроенного языка сценариев, то есть отличие от Emacs, стало вопросом индивидуальности, центральной частью общего мифа о том, что vi является легковесным редактором. Поклонники vi любят говорить о буферах фильтрации с внешними программами и сценариями для выполнения тех задач, которые в Emacs выполняют встроенные сценарии. Однако в реальности команда "!" редактора vi не способна фильтровать меньшие диапазоны буфера редактирования, чем диапазон строк (редакторы Sam и Wily, несмотря на то, что управление подпроцессами в них развито не больше, чем в vi, способны, по крайней мере, фильтровать произвольные текстовые диапазоны, а не только диапазоны строк). Если в vi необходима поддержка файловых форматов и синтаксиса, которые отличаются в более мелких деталях (а в большинстве случаев это так), то сведения об этих форматах и синтаксисе должны быть встроены в C-код. Таким образом, вряд ли соотношение размеров кодовых баз между Emacs и vi будет улучшаться в пользу vi, а, вероятнее всего, оно будет только ухудшаться.

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

Возможно, наиболее очевидной незначительной частью конструкции Emacs является язык Emacs Lisp. Lisp существенен для работы редактора, он представляет то, что в настоящее время называется встроенным языком написания сценариев, однако если бы данным языком был Python, Java или Perl, то Emacs немногим отличался бы в своих возможностях. Однако в то время, когда был создан Emacs (в 1970-х годах), Lisp был почти единственным языком, обладающим характеристиками (включая неограниченно расширяемые типы и методику сборки мусора), которые позволяли применять его для данной задачи.

Большинство деталей обработки событий в emacs и управления растровым дисплеем (включая поддержку интернационализации) также являются случайными. В истории Emacs они привели к "великому расколу" (разветвление GNU Emacs/XEmacs), который демонстрирует тот факт, что ни одна другая часть конструкции не требует какой-либо модели событий.

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

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

Однако взаимодействие с подпроцессами является необходимым свойством. Без него режимы Emacs были бы не способны реализовать ожидаемую IDE-подобную интеграцию и возможность функционировать в качестве интерфейсной части для многих различных инструментальных средств.

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

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

Редактор Wily представляет собой интересную противоположность Emacs. Как в случае с Sam, уровень необязательной сложности в Wily низок. Пользовательский интерфейс редактора может быть кратко, но эффективно описан на одной странице.

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

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

Таким образом, распределенные сценарии wily не являются таким же мощным средством, как встроенный язык сценариев в Emacs. Соответственно, рамки технических требований к Wily значительно сужены. Создатели редактора, в частности, отказываются от какой бы то ни было поддержки подсвечивания синтаксиса или редактирования расширенного текстового формата, и ни Wily, ни его потомок в Plan 9, acme, не могут выполнять данные функции.

Все вышесказанное заставляет более четко сформулировать центральный вопрос данной главы: когда большие цели оправдывают создание большой программы?

Оглавление книги


Генерация: 1.221. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз