суббота, 3 января 2009 г.

Тайна сия велика есть: парсеры на основе C++ шаблонов

Сегодня в ленте новостей пришла ссылка на статью об еще одном генераторе парсеров на основе C++ шаблонов: YARD - Yet Another Recursive Descent . Итого, полку парсеров, использующих для описания грамматик C++ шаблоны, прибыло. Теперь есть Boost.Spirit, YARD и PEGTL (Parsing Expression Grammar Template Library) (последняя использует возможности нового стандарта C++0x).

Когда я смотрю на эти парсеры, тайнами для меня являются несколько вещей.

Вот, например, когда я выбираю инструмент для какой-то задачи, я хочу выбрать наиболее мощный и удобный из доступных. Но как C++ библиотека, значительная часть работы которой выполняется в compile-time (т.е. когда разработчик вообще не имеет возможности вмешаться в ход происходящего), может быть мощнее специализированного инструмента вроде Coco/R или Antlr? Ведь отдельный инструмент может реализовывать сложные алгоритмы обработки грамматики, использовать различные стратегии оптимизации, иметь отличные диагностические возможности, генерировать различные вспомогательные представления (вроде средств визуализации процесса разбора, как в Antlr).

Разработчик YARD указывает следующие проблемы, которые, как он думает, присутствуют при использовании внешних генераторов парсеров (между строк мои саркастические комментарии):

* Code generators are complex pieces of software that have their own peculiar syntax and a steep learning curve.
Ну да, освоить Lex/Flex+Yacc/Bison или Coco/R, или Ragel, или Antlr значительно, значительно сложнее, чем написанную на грани возможностей языка библиотеки C++ шаблонов.

* Debugging the grammars and generated code is challenging.
Ну просто очевидно, что отлаживать сгенерированный Bison-ом или Antlr-ом код гораздо сложнее, чем трехэтажные шаблоны внутри YARD или Boost.Spirit.

* Converting from BNF form to an appropriate LR form is difficult.
Да просто ужас какая сложная задача - преобразовать BNF в Antlr-описание. Кошмар разработчика просто. Сделать то же самое, но вместо внятного синтаксиса использовать C++ шаблоны гораздо, гораздо проще.

* Separate passes and tools for lexing and parsing is inconvenient.
Ну что тут сказать? Может для Lex/Flex+Yacc/Bison это и представляет собой серьезную проблему, но уж для Coco/R, Antlr и Ragel это уж точно не так.

Ну и еще одна совершенно таинственная штука. Как может считаться читабельным описание грамматики, заданное в виде C++ шаблонов? Вот пример из статьи о YARD:

struct Element :
  Or<EmptyElemTag, Seq<STag, Content, ETag> >
{ };
struct STag :
  Seq<
    Char<'<'>,
    Name,
    Star<Seq<S, Attribute> >,
    Opt<S>,
    Char<'>'>
  >
{ };
struct Attribute :
  Seq<Name, Eq, AttValue>
{ };

Данный фрагмент сопровождается замечательной фразой:

Take notice of the close correspondence of the YARD grammar to the official XML specification.

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

element    ::= EmptyElemTag
           |   STag content ETag
STag       ::= '<' Name (S Attribute)* S? '>'
Attribute  ::= Name Eq AttValue

Наверное, если брать только значащие элементы грамматики (вроде имен Name, Eq, AttValue, Attribute и т.п.), то запись в YARD действительно близка к тому, что записано в XML спецификации. Но сопровождается все это слишком большим количеством синтаксического шума, чтобы считаться удобным для использования. По моему мнению.

понедельник, 29 декабря 2008 г.

Программирование и рисование

В очередном RSDN-новском флейме была высказана аналогия между программированием и рисованием:
Рисовать можно научить любого. Научить же передавать через рисунок свои чувства (а тем более, чувства других людей), практически нереально, этому необходимо учится самому, имея некоторый врожденный фундамент и желание/стремление.
С утверждением о том, что рисовать можно научить любого, я, как несостоявшийся художник-самоучка, категорически не согласен. В процитированной мной фразе вообще есть внутреннее противоречие – по-моему, человек умеет рисовать только тогда, когда он рисунком может передать свои впечатления (будь то пейзаж, натюрморт или портрет). А то временами бывает, что все черты переданы в точности, а портрета нет – поскольку нет искорки в глазах или же какой-то характерной, неуловимой для зрителя черты – потому что художник не смог передать то самое впечатление. Тем не менее, данная аналогия хороша, если сравнивать обучение рисованию/живописи и программированию. Итак, начать нужно с того, что рисованию и программированию можно научить только тех людей, которые наделены природными способностями. Для рисования это какая-то особенность нервной или двигательной системы (а может их обеих в комплексе), благодоря которой рука художника точно исполняет команды мозга, в результате чего и формируется изображение. Плюс к этому чувство дистанции и перспективы. Для программирования нужна врожденная способность разбивать какое-то простое действие на произвольное количество мелких и еще более простых последовательных составляющих. Это у обычного человека операция рисования окружности с помощью циркуля выглядит одной неделимой операцией – берешь циркуль и рисуешь. А у программиста это целая серия шагов – поднести циркуль к линейке для выбора радиуса, раздвинуть ножки циркуля на нужное расстояние, поднести циркуль к нужному месту на бумаге, воткнуть ножку с иголкой в бумагу, повернуть циркуль на 360 градусов вокруг ножки с иголкой. Т.е., если природой способностей к рисованию или программированию не дано, ничего путного не получится. А если дано? А если дано, то процесс обучения – это, во многом, процесс тренировки, натаскивания. В рисовании на уровень инстинктов выводится владение, скажем, техникой штриховки. В программировании – способность написания циклов или разбиения одной большой функции на несколько мелких. А то и вовсе – способность набирать тексты в редакторе. И, если речь идет только о технике, то можно сказать, что вышколить любого имеющего природные способности человека до виртуозного владения техникой, в принципе, возможно. Это только вопрос времени. Хотя, нужно отметить, что на обучение программированию тратится гораздо меньше времени, чем на обучение рисованию. Ну да здесь можно сделать скидку на то, что в рисовании очень важна физическая тренировка – рука сама собой должна точно повторять нужные движения (штрихи, мазки). В программировании такое совершенное взаимодействие "мозг-рука" не требуется. Но техника (что в рисовании, что в программировании) не определяет успех усилий автора. Если мы рассматриваем какое-то художественное произведение, то мы рассматриваем три вещи: 1. Сюжет (идею). 2. Композицию (расположение фигур на рисунке или картине). 3. Исполнение (технику рисунка). Как я сказал выше, технике можно натаскать. Композиция, в принципе, так же может быть донесена до ученика в процессе обучения. Поскольку существуют яркие примеры того, что называется "правильной" композицией, "неправильной" композицией, "перегруженной" композицией и т.д. Хотя и здесь есть интересный феномен – иногда люди могут очень грамотно и красочно рассказывать теорию композиции, но на практике им не удается создавать удачные композиции в своих произведениях. Так что здесь так же есть фактор природных способностей – у кого-то чувство композиции заложено изначально, ему достаточно нескольких ярких примеров для того, чтобы подсознание выдавало готовые композиции. У остальных таких способностей нет. Но они могут пользоваться имеющимися примерами как образцами. А вот с сюжетом ситуация наиболее интересна. Я не знаю, можно ли как-то тренировать креативность, развивать способность выдумывать сюжеты. И сильно сомневаюсь в том, что это возможно. У меня всегда были проблемы с выдумыванием сюжетов. Подсмотреть готовый сюжет (особенно, если речь идет о пейзажах или портретах), это запросто. А вот родить что-нибудь вроде "Атомной Леды" или "Сон, навеянный полетом пчелы вокруг граната за миг до пробуждения", или "Предчувствие гражданской войны" Сальвадора Дали – уже нет. Не дано. И не обучиться этому.
Вот так же думаю, дело обстоит и в программировании. Когда речь идет о программной системе, мы можем рассматривать некоторые аналогичные вещи: 1. Поход к решению задачи. 2. Архитектура системы. 3. Качество кода. Качество кода, это как техника в рисовании. Можно, в принципе, надрессировать любого разработчика писать качественно. Вопрос только во времени. Если у программиста есть способности. Архитектура системы – как композиция. Если мы говорим об объектно-ориентированном программировании, то можно вспомнить паттерны. Которые можно рассматривать как примеры "хороших" композиций. Только вот у некоторых разработчиков паттерны автоматически распознаются и реализуются на уровне подсознания. Другие же способны перечислять названия паттернов, их характерные черты и особенности, но не способны удачно использовать их в своих программах. А вот подход к решению задачи, это как сюжет у рисунка. Можно позаимствовать его у кого-то. Иногда сама задача диктует подход к своему решению, почти как пейзаж диктует художнику, что именно он должен нарисовать. Однако мне очевидно, что у разных программистов совершенно разные способности к выбору подходов для решения задач. Во многом, думаю, способность к выбору правильного подхода определяется врожденными способностями. Развитыми со временем, но все-таки врожденными. Какой же из всего этого потока сознания вывод? Как и в рисовании, в программировании многое зависит от природных способностей. Которые обязательно должны быть развиты упорным трудом. И совокупность этих двух факторов (способности и вложенные в их развитие усилия) определяет качество программиста. А посему, действительно классные программисты – это такая же редкость, как и действительно классные художники.