Вместо предисловия
Давным-давно, аж в далеком 2002 году я совершенно случайно наткнулся на книжки замечательной писательницы
Лорел К. Гамильтон. На тот момент на русский язык были переведены только первые 6 книг из серии про Аниту Блейк.
В то время я уже отказался от чтения современной американской фантастики и фэнтези ввиду их примитивности (в сравнении с отчественными). Поэтому вдвойне удивительно то, что эти книжки так меня увлекли, что я полез в сеть в поисках продолжения :) Которое и нашел на
фанатском сайте.
Но радость была недолгой - пятая книга, переведенная силами фанатов обрывалась (по закону подлости) на самом интересном месте.
К счастью - в наличии был английский текст книжки и даже еще две книги-продолжения :)
Идея читать в оригинале была отброшена сразу как несостоятельная - мое тогдашнее знание языка позволяло уверенно читать без словаря документацию на сайте MSDN и всё. Точнее, художественный текст я бы осилил, но скорость чтения была бы катастрофически медленной, что меня, естественно, не устраивало.
И я совершил страшное. Я запустил ПРОМТ и перевел книжку...
Не буду говорить
что из этого получилось, но результат можно было читать. Главное - не заострять внимание на том,
как написано, а пытаться понять
что написано :) При некотором опыте читать - можно.
При чтении я как-то обратил внимание, что, в принципе, основные ошибки перевода ПРОМТа - это ошибки склонения слов (естественно, в тех случаях, когда предложение было разобрано верно).
Вот тогда-то и появилась идея (нет-нет, не переводчика!) программы-корректора, которая бы подправляла текст, сгенерированный ПРОМТом.
Примечание: в то далекое время я еще ничего не знал про Microsoft .Net Framework. Да и был он в стадии "почти релиза" и особо нигде не упоминался. Так что писал на том, что знал - C++ Builder 6.0.
Этап №1
Была написана маленькая программа, со своим словарем слов, которая заменяла отдельные слова на альтернативные. Изначально хотелось увидев "неправильное" слово или словосочетание заменить их на "правильное".
Я думаю, не стоит говорить о том, что в конечном итоге то, что получилось оказалось на 100% привязанным к тестовому тексту и с чем-либо другим корректно работать не могло.
Стало понятно, что необходим собственный алгоритм построения склонения/спряжения слов. Алгоритм был написан и успешно заработал. Причем он оказался настолько успешным, что без особых изменений переползал из версии в версию и существует и используется и поныне :)
Однако, алгоритм склонения не сильно помог - стало понятно, что простое склонение пар слов относительно друг-друга нужного эффекта не даёт - необходимо, как минимум, знать связи между словами.
Этап №2
Вот тогда-то и появилась амбициозная идея написать "свой" переводчик, в котором можно было бы контролировать процесс перевода.
Именно тогда родился тот подход к синтаксическому анализу, который я использую и сейчас. Как раз тогда я впервые прочитал про регулярные выражения (regexp) и подумалось: "да это же самый простой способ определить вид предложения!".
Сам алгоритм анализа основанный на регулярных выражениях был написан всего за 4 часа. А вот уже загрузчик правил писался долго и муторно. Достаточно сказать, что пока я не начал использовать формат xml для хранения этих правил анализа алгоритм чтения файла с правилами продолжал глючить и падать от малейшего чиха. И это при том, что он постоянно правился в течение нескольких месяцев! Именно после этого я так невзлюбил Си++...
Кстати, та самая реализация алгоритма анализа до сих пор вполне успешно используется :) Ну понятно, что она нексколько мутировала, но все-таки...
Этап №3
Где-то в районе 2004-го года мне попадается интересная статья про Mono. И как-то руки зачесались попробовать :) А там как раз стало понятно, что Mono и .Net Framework - братья. А там и SharpDevelop попался... Вообщем, был написан тестовый редактор правил - благо правила уже давно хранились в формате xml, поэтому, фактически, писалась оболочка.
И как-то оно всё так хорошо пошло, что буквально за неделю весь проект переполз на C#. Я сам не ожидал, честно :) Ну не без глюков, конечно... Но в целом - изменения в коде были минимальные и в основном касались работы со строками.
Примечание: Самая старая версия переводчика, которую я смог раскопать в архивах датируется 19.09.2004. Однако, это уже практически окончательный вариант третьего поколения алгоритма перевода (а может и большей версии - сейчас сложно сказать).
Этапы №4 и далее
Становится понятно, что то, что получилось, в принципе, даже работает. И где-то даже правильно. И вообще...
Система довольно длительное время развивается пока не упирается в принципиальный тупик: разбора только одного варианта предложения недостаточно. Вероятность ошибки неверного разбора предложения прямо зависела от того, вариант какой части речи стоял у слова в словаре первым.
Поэтому был добавлен маленький кусочек кода из-за которого пришлось переписывать половину программы :) А занимался он генерацией вариантов предложений. Основная идея - в предложении каждое слово имеет варианты перевода только одной части речи.
Примерно тогда же становится понятно, что при таком подходе легко можно получить агромаднейшее количество вариантов предложения. Главное в него подсунуть правильные слова с несколькими переводами разных частей речи (и побольше, побольше!).
Не знаю уж, что мне подсказало пойти таким путем, но я использовал все тот же алгоритм синтаксического анализа - для предварительной обработки предложения
до этапа разделения его на варианты. В этом случае использовался специальный набор правил, выполнение которых дает 100% правильный результат и позволяет сократить количество вариантов перевода отдельныъ слов.
Примечание: Например, если после артикля the стоит слово - это слово 100% не глагол. И т.д и т.п.
В итоге, на самом большом моем тестовом тексте я до сих пор получаю не более 17 вариантов предложения. Что, конечно много, но, во-первых, правил не так уж и много, а во-вторых, при существующей производительности железа - это крайне мало.
Этап №14
Худо-бедно система развивалась, пока я не уперся в потолок - каждое последующее изменение требовало все больше и больше усилий. Я думаю, многим это знакомо.
Это было страшное время :( Когда понятно,
что надо сделать, но непонятно
как это сделать так, чтобы ничего не сломалось...
Поэтому в начале 2006-го года было принятно решение все переписать :)
Программа была переписана полностью с нуля. Т.е.
вообще всё с нуля. Естественно, часть старого кода переползла в новую версию, но все пережитки вызванные предыдущим бурным развитием безжалостно уничтожались...
Какое-то время удавалось поддерживать новую версию на плаву... Пока я не уперся в тот же самый потолок.
Этап №15
Собственно, деваться было некуда и пришлось полностью менять подход к созданию крупных программ. Благо уже "созрел".
К лету 2006-го стало понятно, что надо делать модульную систему. К такому решению подталкивало два фактора:
- во-первых, по своей структуре она уже была модульной. Осталось только реализовать динамическую загрузку и переделать основную часть программы (ядро) под более абстрактный код.
- во-вторых, пришло понимание, что написать переводчик полностью силами одного человека - задача из области фантастики. Использование же модулей позволяет распределить отдельные части задачи между разными разработчиками.
Практически год потребовался на полную переделку программы. Конечно, основной причиной столь длительного срока было угасание интереса - в основном из-за понимания
насколько много придется переписывать заново. И желания это делать как-то не наблюдалось :)
В конце-концов, процесс подошел к своей конечной стадии. По пути программа лишилась удобного интерфейса выбора варианта перевода предложения и алгоритма поиска фраз (который был хоть и кривой и недоделанный, но все равно - жалко). Нет-нет! Они не совсем умерли. Просто пока что руки не доходят реализовать их по-новому - надо сначала доделать то, что важнее.
Зато появилось много чего нового и интересного :)
В конечном итоге - оно того стоило.
Продолжение следует...