Как стать автором
Обновить

Лингвистическая загадка. Переводим с «мертвого» языка. [§2] Разбор полётов

Время на прочтение 11 мин
Количество просмотров 19K
Это — продолжение, вернее ответ на задачу из статьи "Лингвистическая загадка. Переводим с мертвого языка".

Времени нет катастрофически, но как известно его никогда не бывает, а раз уж обещал, статью приходится писать. Засим еще раз извиняюсь за опоздание.

Ответ

Для нетерпеливых сразу ответ, который кстати на момент написания статьи, кроме единственного человека (не с хабра), полностью не разгадал никто. Но об этом ниже…
Упомянутая известная фраза про «Глокую куздру» (привет AndreyDmitriev), что есть цитата из книги Успенского «Слово о словах», была коварно помещена мной в середину текста. Остальное, как уже говорилось, добил другими из той же темы, причем немного даже в «старорусской» манере...
не произноси будлания глокого на куздрение бокра своего, ибо вострепещут будлающие, да воссияют будлаемые. глокая куздра штеко будланула бокра и курдячит бокрёнка. да не будет у бокра куздры, а у куздры бокра перед лицем моим, ибо я страшно будланул, и мною так будлануто.

Было множество попыток от разных людей и в верном направлении и не очень. Вашему покорному слуге было очень интересно следить за самим процессом «разгадывания». На этом, всем участникам огромнейшее спасибо.
Несколько человек, после моих примеров, открыли для себя тикль или решили выучить его серьезнее («новобранцам» особый привет), что меня как его разработчика тоже не может не радовать!
В общем с моей стороны сплошной позитив…
Думается, предложи я работу отличившимся, интерес к задаче был бы много выше, но к сожалению вакансий на тот момент у нас не было, поэтому что есть — то есть.

Простой пример машинного перевода (TCL в три строчки)

Здесь совсем уж простенькие исходники на TCL со словарями для преобразования туда-сюда. Они только чтобы показать работоспособность «машинного» перевода (в три строчки), но как примеры не совсем дееспособны. Здесь, чтобы применить слоговую и символьную замены, различающие приставки и суффиксы, в словарях использовались пробелы, точки и запятые. Такой словарь довольно громоздкий и не удобен для разбора, поэтому ниже приведу алгоритмы посложнее, но более легким в понимании словарем.
Тикль есть под linux как правило уже из коробки (tclsh), под windows (после установки tcl) вместо tclsh лучше использовать wish из-за множественных кодировок). Если будут желающие примеры на python, перепишу под него и выложу в пост.
  • Туда:
    С русского на «мертвый»
    # словарь туда
    set map1 {шт სტ стр სცლგ ст სცტ сс შც гл გილრ во ფაგ ла რეოლ буд პაგდლ бу პეულ бо პეალ зд ზდ прои პგლოე зно ზგა окая აგელეო кая კელეო кра კგლეგ ка კეოლეგ ку კეუგლ ко კეალ то გტაგ ок აგლ ну მეუგლ ит ედგეგ ат ოდგ ут ულდგ пер ბლგ ая ოლეოგ ия ელეოგ ю ეუგლ {и } {ელ } и, ელ, и. ელ. {я } {ეოგლ } я, ეოგლ, я. ეოგლ. {а } {ოლ } а, ოლ, а. ოლ. {е } {ილ } е, ილ, е. ილ. {о } {აგ } о, აგ, о. აგ. {у } {უგ } у, უგ, у. უგ. {м } {ნოგ } м, ნოგ, м. ნოგ. {к } {ხგ } к, ხგ, к. ხგ. {не } {მეაგ } не, მეაგ, не. მეაგ. ё ეა щ შჩ ы ეგ ю ეუ я ეო а ო б პ в ვ г ხ д დ е ი ж ზ з ჟ и ე й ე к კ л რ м ნ н მ о ა п ბ р ლ с ს т ტ у უ ф ფ х გ ц ც ч ჩ ш შ ъ _ ь ე э ი}
    # оригинальный текст
    set decStr "Не произноси будлания глокого на куздрение бокра своего, ибо вострепещут будлающие, да воссияют будлаемые. Глокая куздра штеко будланула бокра и курдячит бокрёнка. Да не будет у бокра куздры, а у куздры бокра перед лицем Моим, ибо Я страшно будланул, и Мною так будлануто."
    # преобразуем по словарю (но сначала lower case)
    set encStr [string map $map1 [string tolower $decStr]]
    # stdout
    puts $encStr
  • Обратно:
    С «мертвого» на русский
    # словарь обратно
    set map2 {სტ шт სცლგ стр სცტ ст შც сс გილრ гл ფაგ во რეოლ ла პაგდლ буд პეულ бу პეალ бо ზდ зд პგლოე прои ზგა зно აგელეო окая კელეო кая კგლეგ кра კეოლეგ ка კეუგლ ку კეალ ко გტაგ то აგლ ок მეუგლ ну ედგეგ ит ოდგ ат ულდგ ут ბლგ пер ოლეოგ ая ელეოგ ия ეუგლ ю {ელ } {и } ელ, и, ელ. и. {ეოგლ } {я } ეოგლ, я, ეოგლ. я. {ოლ } {а } ოლ, а, ოლ. а. {ილ } {е } ილ, е, ილ. е. {აგ } {о } აგ, о, აგ. о. {უგ } {у } უგ, у, უგ. у. {ნოგ } {м } ნოგ, м, ნოგ. м. {ხგ } {к } ხგ, к, ხგ. к. {მეაგ } {не } მეაგ, не, მეაგ. не. ეა ё შჩ щ ეგ ы ეუ ю ეო я ო а პ б ვ в ხ г დ д ი е ზ ж ჟ з ე и ე й კ к რ л ნ м მ н ა о ბ п ლ р ს с ტ т უ у ფ ф გ х ც ц ჩ ч შ ш _ ъ ე ь ი э}
    # мертвый текст
    set encStr "მეაგ პგლოეზგასელ პაგდლრეოლმელეოგ გილრაგლახაგ მოლ კეუგლზდლიმეილ პეალკგლეგ სფაგიხაგ, ეპეალ ფაგსცლგიბიშჩულდგ პაგდლრეოლეუგლშჩეილ, დოლ ფაგშცელეოგეუგლტ პაგდლრეოლინეგილ. გილრაგელეო კეუგლზდლოლ სტიკეალ პაგდლრეოლმეუგლრეოლ პეალკგლეგ ელ კეუგლლდეოჩედგეგ პეალკლეამკეოლეგ. დოლ მეაგ პაგდლიტ უგ პეალკგლეგ კეუგლზდლეგ, ოლ უგ კეუგლზდლეგ პეალკგლეგ ბლგიდ რეცინოგ ნაენოგ, ეპეალ ეოგლ სცლგოშმაგ პაგდლრეოლმეუგლრ, ელ ნმაეუგლ ტოხგ პაგდლრეოლმეუგლგტაგ."
    # преобразуем по словарю
    set decStr [string map $map2 $encStr]
    # stdout
    puts $decStr

Собственно разбор полётов


Я не лингвист в прямом смысле этого понятия, но во первых, бегло говорю и пишу на нескольких языках из разных языковых групп (причем на некоторых из них, как на родном). Во вторых, по работе связан с разбором и анализом текстов, в том числе многоязычных (поисковые системы, распознавание, индексирование и т.д. и т.п.). В третьих, опять же частично по роду деятельности, прекрасно понимаю как некоторые языки развивались и видоизменялись со временем. К тому же я программист, т.е. преобразования из одного в другое хоть «машинным» способом, хоть в голове, присущи мне в определенной степени тоже.

В качестве начального примера возьмем некоторые «преобразования», произошедшие с отдельными языками давным давно:
  • kiks <-> Kekse <-> koekjes <-> cookies — датский, немецкий, голландский (нидерландский) и английский соответственно.
  • ting <-> das Ding <-> het ding <-> the thing — они же;
  • каждого года <-> кожнага года <-> кожного року <-> každého roka — русский, белорусский, украинский, словацкий;

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

Грамматические видоизменения синтаксиса предложений при переводе я не рассматривал в принципе, т.е. в искомом тексте все «переводилось» дословно. Просто при полном видоизменении, например перестановке слов, шансы восстановить русский текст по такому небольшому отрывку стремились к нулю. Сравните например даже такие близкие языки, одной языковой группы, как датский и немецкий.

Дословный перевод (морфологически правильно, но синтаксически никуда не годится):
[DK] Du behøver ikke komme, hvis du har bedre ting for
[DE] Du brauchst nicht kommen, falls du hast bessere Dinge vor
Правильный перевод на немецкий:
[DK] Du behøver ikke komme, hvis du har bedre ting for
[DE] Du brauchst nicht zu kommen, falls du etwas besseres vorhast

Поэтому, еще раз повторюсь, перевод осуществляем дословно, без изменения порядка слов — т.е. остается характерный русскому языку синтаксис.

Определим наш язык как немного «рычащий» и «хыкающий» и сделаем некоторые буквы в определенных местах «плохо слышимыми» или нечитаемыми. Слегка усложним морфологию написания языка, а слова в нем сделаем чуть длиннее чем в русском. То что получится, должно напоминать русский с примесью с одной стороны скандинавских (и возможно других германских) языков, с другой стороны иметь что-то от языков тюркской группы.
Итак, приступим…

Переводим на «мертвый» язык

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

Для начала небольшая подпрограмма позволяющая простенько и со вкусом изменять слоги и буквы, при этом «различая» начало и конец слов.

Магическая подпрограмма [Translate]
# позволяет преобразовывать текст словарем и регулярками:
proc magic_text {args} {
  set text [lindex $args end]
  foreach {op val} [lrange $args 0 end-1] {
    switch -- $op \
    -regexp {
      foreach {re val} $val {
        regsub -nocase -all $re $text $val text
      }
    } \
    -map {
      set text [string map -nocase $val $text]
    } \
    -default {
      error "uknown operation '$op'"
    }
  }
  return $text
}

# расширенная версия, где сначала помечаем слова, выделив начало и конец спецсимволами:
# ... ^слово$, ^следующее$ ... - а после преобразований удаляем эти маркеры
proc Translate {dictMap encText} {
  magic_text -regexp {{(\m[^\s[:punct:]]+\M)} {^\1$}} -map $dictMap -map {^ "" $ ""} $encText
}

Проба пера — здесь используя простенький словарь, попробуем переписать фразу «мама мыла раму» во множественном числе, и вместо «рамы» будет «Рома».
# пример словаря (^ - начало слова, $ - конец):
% Translate {^ма Ма ла$ ли а$ ы у$ "" ^ра Ро} "мама мыла раму, рама пищала и стонала."
Мамы мыли Ром, Ромы пищали и стонали.

Собственно приступаем к созданию словаря для нашего «мертвого» языка.

# словарь туда
set RuXy {
  шт ст      стр сцрх    ст сцт     сс шц
  гл херл    во фох      ла ляр    
  буд бохдр  бу бюр      бо бёр
  зд жд      прои бхраи  зно жхо  
  окая охиря кая киря    кра кхры   ка кяры   ку кюхр
  ко кёр     то хтох
  ок охр     ну нюхр
  ит идхы    ат адх
  ут урдх    пер прх
  ая арях    ия ирях     ю юхр 
  и$ ир 
  я$ ях
  а$ ар
  е$ ер
  о$ ох
  у$ ух
  м$ мах
  к$ гх
  не$ ниох
}
# 1 оригинальный русский
puts 1:[set ru "Не произноси будлания глокого на куздрение бокра своего, ибо вострепещут будлающие, да воссияют будлаемые. Глокая куздра штеко будланула бокра и курдячит бокрёнка. Да не будет у бокра куздры, а у куздры бокра перед лицем Моим, ибо Я страшно будланул, и Мною так будлануто."]
# 2 переводим :
puts 2:[Translate $RuXy $ru]

Результат исполнения (перевода):
1:Не произноси будлания глокого на куздрение бокра своего, ибо вострепещут будлающие, да воссияют будлаемые. Глокая куздра штеко будланула бокра и курдячит бокрёнка. Да не будет у бокра куздры, а у куздры бокра перед лицем Моим, ибо Я страшно будланул, и Мною так будлануто.
2:ниох бхраижхосир бохдрлярнирях херлохрогох нар кюхрждрениер бёркхры сфохегох, ибёр фохсцрхепещурдх бохдрлярюхрщиер, дар фохшциряхюхрт бохдрляремыер. херлохиря кюхрждрар стекёр бохдрлярнюхрляр бёркхры ир кюхррдячидхы бёркрёнкяры. Дар ниох бохдрет ух бёркхры кюхрждры, ар ух кюхрждры бёркхры прхед лицемах Моимах, ибёр ях сцрхашнох бохдрлярнюхрл, ир Мноюхр тагх бохдрлярнюхрхтох.

Кстати, если ввести некоторые конвенции при чтении (например «р» и «х» в конце почти всегда не произносятся), то можно сходу прочитать все предложение почти как на русском.
Цель достигнута, теперь просто наложим грузинский алфавит, соблюдая гласные и согласные (чтобы не совсем уж усложнять).

Ниже скрипт, использующий уже грузинский алфавит, и переводящий туда и обратно:
# словарь туда
set RuXz {шт სტ стр სცლგ ст სცტ сс შც гл გილრ во ფაგ ла რეოლ буд პაგდლ бу პეულ бо პეალ зд ზდ прои პგლოე зно ზგა окая აგელეო кая კელეო кра კგლეგ ка კეოლეგ ку კეუგლ ко კეალ то გტაგ ок აგლ ну მეუგლ ит ედგეგ ат ოდგ ут ულდგ пер ბლგ ая ოლეოგ ия ელეოგ ю ეუგლ и$ ელ я$ ეოგლ а$ ოლ е$ ილ о$ აგ у$ უგ м$ ნოგ к$ ხგ не$ მეაგ ё ეა щ შჩ ы ეგ ю ეუ я ეო а ო б პ в ვ г ხ д დ е ი ж ზ з ჟ и ე й ე к კ л რ м ნ н მ о ა п ბ р ლ с ს т ტ у უ ф ფ х გ ц ც ч ჩ ш შ ъ _ ь ე э ი}
# словарь обратно
set XzRu {სტ шт სცლგ стр სცტ ст შც сс გილრ гл ფაგ во რეოლ ла პაგდლ буд პეულ бу პეალ бо ზდ зд პგლოე прои ზგა зно აგელეო окая კელეო кая კგლეგ кра კეოლეგ ка კეუგლ ку კეალ ко გტაგ то აგლ ок მეუგლ ну ედგეგ ит ოდგ ат ულდგ ут ბლგ пер ოლეოგ ая ელეოგ ия ეუგლ ю ელ$ и ეოგლ$ я ოლ$ а ილ$ е აგ$ о უგ$ у ნოგ$ м ხგ$ к მეაგ$ не ეა ё შჩ щ ეგ ы ეუ ю ეო я ო а პ б ვ в ხ г დ д ი е ზ ж ჟ з ე и ე й კ к რ л ნ м მ н ა о ბ п ლ р ს с ტ т უ у ფ ф გ х ც ц ჩ ч შ ш _ ъ ე ь ი э}
# 1 оригинальный русский
puts 1:[set ru "Не произноси будлания глокого на куздрение бокра своего, ибо вострепещут будлающие, да воссияют будлаемые. Глокая куздра штеко будланула бокра и курдячит бокрёнка. Да не будет у бокра куздры, а у куздры бокра перед лицем Моим, ибо Я страшно будланул, и Мною так будлануто."]
# 2 перевод на мертвый используя словарь $RuXz
puts 2:[set xz [Translate $RuXz $ru]]
# 3 перевод на русский используя словарь $XzRu
puts 3:[set ru2 [Translate $XzRu $xz]]

Ну и результат исполнения (перевода):
1:Не произноси будлания глокого на куздрение бокра своего, ибо вострепещут будлающие, да воссияют будлаемые. Глокая куздра штеко будланула бокра и курдячит бокрёнка. Да не будет у бокра куздры, а у куздры бокра перед лицем Моим, ибо Я страшно будланул, и Мною так будлануто.
2:მეაგ პგლოეზგასელ პაგდლრეოლმელეოგ გილრაგლახაგ მოლ კეუგლზდლიმეილ პეალკგლეგ სფაგიხაგ, ეპეალ ფაგსცლგიბიშჩულდგ პაგდლრეოლეუგლშჩეილ, დოლ ფაგშცელეოგეუგლტ პაგდლრეოლინეგილ. გილრაგელეო კეუგლზდლოლ სტიკეალ პაგდლრეოლმეუგლრეოლ პეალკგლეგ ელ კეუგლლდეოჩედგეგ პეალკლეამკეოლეგ. დოლ მეაგ პაგდლიტ უგ პეალკგლეგ კეუგლზდლეგ, ოლ უგ კეუგლზდლეგ პეალკგლეგ ბლგიდ რეცინოგ ნაენოგ, ეპეალ ეოგლ სცლგოშმაგ პაგდლრეოლმეუგლრ, ელ ნმაეუგლ ტოხგ პაგდლრეოლმეუგლგტაგ.
3:не произноси будлания глокого на куздрение бокра своего, ибо вострепещут будлающие, да воссияют будлаемые. глокая куздра штеко будланула бокра и курдячит бокрёнка. да не будет у бокра куздры, а у куздры бокра перед лицем моим, ибо я страшно будланул, и мною так будлануто.


Перевод с «мертвого» языка

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

Должен заметить, что мне представлялось почти невозможным сделать перевод полностью автоматически или полностью вручную. В моем понимании, я и компьютер будем работать постоянно в сцепке.

В результате краткий сценарий обратного перевода, как это делал я:

  1. Компьютерный анализ текста (использовался пропритарный VDK, самописные фильтры к нему, стемеры русского и куча скриптов, например для привязки diff). Изначально пустой словарь трансляции алфавита. Результат анализа — множество вариантов «неправильного» текста на кирилице, в которых все в именительном падеже, с универсальной морфологией в неопределенной форме.
  2. Анализ этих вариантов текстов вручную (поиск чего нибудь читабельного). Цель — увеличить словарь, использующийся при компьютерном анализе. Повторить с шага 1. Если читабельность текста стала хуже, откатиться к предыдущему словарю.

Вот в принципе и все, а теперь чуть развернуто:
UPD(1)
  1. Компьютерный анализ
    API VDK позволяет осуществлять анализ и обработку текста, вплоть до полнотекстового индексирования — опенсоурсный «аналог» был бы Apache Lucene (api под Solr), если бы оно умело все то, что может VDK.
    Я долго уже горю желанием полностью переписать свои наработки, скрипты, фильтры и анализаторы без использования VDK. Пока к сожалению удалось только частично.
    Не отвлекаясь на API VDK, попытаюсь все же развернуть свой workflow — как все работает.
    • Генерируем новый словарь для Translate (см. функцию выше), и запускаем его — переводим текст, который затем скармливаем последующим шагам workflow.
    • «Индексация» текста используя самописные фильтры и пре-стеммеры. В результате имеем разобранный на слова текст.
    • Работаем со словами. Для этого можно использовать например акцентные или диалектные фильтры (с настраиваемыми правилами). Тут можно убрать ударения и т.д. Покажу на примере немецких умляутов: бывшего канцлера Германии можно было встретить в тексте как «Schröder» или как «Schroeder». Правописание в немецком тоже менялось (старое, новое), поэтому «Crème» и «Krem» или например «Schmant» и «Schmand» — это одно и то же слово.
      Естественно в нашем случае имеем «псевдорусский» акцентный словарь. Поэтому проведем здесь обратное действие, которое обычно используют не при индексировании, а при поиске, т.е. экспандируем каждое слово в несколько похожих.
    • Далее натравливаем стеммер. Стеммер позволяет привести слово к уникальному виду, типа корня, неопределенной формы, именительного падежа, и т.д. Умные стеммеры, например использующие словари, могут при этом удалять приставки или суффиксы, если смысл слова при этом не теряется (т.е. слово не становится совсем другим).
      Естественно чисто русский стеммер здесь совершенно не подходит. Но можно использовать настраиваемый стеммер, который использует грамматические фильтры русского языка (что касается синтаксиса) и новый словарь (изначально пустой).
    • Все действия перемешиваются скриптом (workflow), пропуская на промежуточных этапах через другие фильтры, например UniqueFilter.
    • По окончанию запускается diff, чтобы на каждой итерации концентрироваться только на изменениях в словах или видеть новые корни и т.д.

  2. Ручной анализ.
    • На каждой итерации смотрим на изменения в словах (результат diff) и несколько вариантов «перевода» текста (тоже diff), ищем в изменениях какие-либо зацепки, известные или похожие нам слова.
    • Правим словари фильтров, отдельные правила, редко сам workflow.
    • Повторяем итерацию — заново запускаем flow.

UPD(2)
Т.к. появились вопросы, чего не хватает, чтобы расшифровать автоматически, иначе говоря «зачем включать голову» (я утрирую) и почему в моем понимании человек и компьютер должны тут работать в паре. Отвечу здесь коротко: представим только одну ситуацию имеющуюся во многих языках — чтение и написание часто не соответствуют, при побуквенном их произношении. К примеру возьму опять немецкий: слог EU в немецком произносится как ОЙ, соответственно слова EURO и Europa немцы произносят как "Ойро" и "Ойропа". А теперь представим, как написал бы немец русское слово "мой" — я вам гарантирую что это было бы "meu" а не "moi" (хотя бы потому, что в немецком слово новый — "neu", произносится как "ной"). И это только одна ситуация, которая имеет место — тут хоть Ватсон берите, все равно без головы никуда!

Человек, разгадавший эту загадку и переведший текст полностью, вплоть до совпадения хеша, прислал мне ответ по е-майлу. Позже он описал способ, которым он это сделал, который в корне отличается от того, как к разгадыванию и переводу подошел я.
К сожалению, по многим причинам (время в том числе) он пока не горит желанием присоединится к хабрасообществу (и инвайты у меня пока все вышли, прислал бы назло:). Но если вдруг получится совместно или может он разрешит опубликовать решение от его имени, я сделаю это с большим удовольствием. Его решение, вернее концепция оного, несмотря на то, что тонкости к сожалению я не понял, т.к. полностью не видел, все равно очень и очень впечатляет.
По непонятным для меня причинам, этот скромник не хотел, чтобы я упоминал его имя в статье, да и в то, кто он в действительности и откуда, я к сожалению тоже не был посвящен. Стиль общения позволяет предположить, что человек этот еще очень молод. Хотя… В общем, тут пока сплошные тайны мадридского двора. Однозначно только могу сказать, что он — «русский» (по нынешним временам правильнее — русскоговорящий:).

Еще раз благодарю всех участвовавших.
Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
+28
Комментарии 19
Комментарии Комментарии 19

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн