Последнее в категории Лингвистика

Lingua::Identify

| Комментариев: 2

На спане есть модуль Lingua::Identify, который предназначен для определения языка, на котором написан текст.

Я опробовал его с данными из базы сайта sayperl.org, сравнивая результат с тем, что определил переводчик Гугла.

use v5.12;

use SayPerl::Datasource;
use Lingua::Identify qw(:language_identification);

my ($total, $equal);
for (@{dbh->selectall_arrayref("
    select title, description, language
    from item where language
    in ('bg', 'da', 'de', 'en', 'es', 'fi', 'fr',
    'hr', 'hu', 'id', 'it', 'la', 'nl', 'pl', 'pt',
    'ro', 'ru', 'sl', 'sq', 'sv', 'tr')")}) {
    $total++;
   
    my $text = "$$_[0] $$_[1]";
    $text =~ s{<.*?>}{ }gs;
    my $detected = langof($text);
    my $googled = $$_[2];
   
    if ($detected ne $googled) {
        say "$detected $googled $$_[0]";
        $equal++;
    }
}
say "$equal/$total";

Из таблицы выбирались записи только на языках, которые поддержвает модуль, а для определения я воспользовался самым простым вариантом — вызовом функции langof(), которая в скалярном контексте возвращает код языка.

Результат — из 5063 записей язык не совпал в 1590 случаях. Прям увы. Русские записи не определились в подавляющем большинстве случаев: 1401 из 1492 (может надо было где-то включить юникод?).

В тестах к модулю лежат всего четыре файла с текстами: de, en, pt и pt_big. В немецком — ни одного умляута, а португальские — в однобайтовой Latin-1. Перекодировка Encode::from_to из UTF-8 в CP1251 вдвое уменьшила число ошибок в русских текстах, но это уже шаманство.

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

В начале века я пользовался функцией на парсере, не уверен, что точно знаю, кем написанной, и не знаю, где сегодня найти в архиве. Она умела выбирать правильный падеж существительного, поставленного после числа. Хорошо помню, что она называлась ^rightCase[], но сходу не смог найти ее в бекапах. Но зато нашел ее предшественницу:

@grammar[count]
    $count[0$count]

    $last2[^count.match[(\d\d)^$]]
    $last2[$last2.1]

    ^if(^last2.match[[02-9]1]){
        $ending[]
    }{
        ^if(^last2.match[[2-4]^$]){
            $ending[а]
        }{
            $ending[ов]
        }
    }
    $result[язык$ending]

Потом долгое время пользовался (и продолжаю) функцией на перле, переписанной по мотивам этого алгоритма, и размещенной на спане в модуле Lingua::RU::Numeric::Declension:

sub numdecl {
   my ($number, $nominative, $genitive, $plural) = @_;

   return $plural if $number =~ /1.$/;

   my ($last_digit) = $number =~ /(.)$/;

   return $nominative if $last_digit == 1;
   return $genitive if $last_digit > 0 && $last_digit < 5;
   return $plural;
}

А сегодня еще раз скопировал этот алгоритм, но уже на XSLT:

<func:function name="w:declension">
    <xsl:param name="number"/>
    <xsl:param name="nominative"/>
    <xsl:param name="genitive"/>
    <xsl:param name="plural"/>
    <xsl:variable name="length" select="string-length(string($number))"/>
    <xsl:variable name="last" select="substring(string($number), $length, 1)"/>
    <func:result>
        <xsl:choose>
            <xsl:when test="$length &gt; 1 and substing(string($number), $length - 1, 1) = '1'">
                <xsl:value-of select="$plural"/>
            </xsl:when>
            <xsl:when test="$last = '1'">
                <xsl:value-of select="$nominative"/>
            </xsl:when>
            <xsl:when test="$last &gt; 0 and $last &lt; 5">
                <xsl:value-of select="$genitive"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$plural"/>
            </xsl:otherwise>
        </xsl:choose>
    </func:result>
</func:function>

Какая из этого мораль? Да такая, что в большинстве случаев выбор языка не важен.

Мой блиц-доклад о том, что такое sayperl.org, почему он появился, и какие проблемы вскрылись по пути.

Автор ясень, автоматически переводятся с английского языка.

Краткая версия: SayPerl.org сайт запущен. Она обеспечивает Английский перевод для Perl сообщения со всего мира.

Длинная версия:

Железный Человек и PlanetPerl.ru показали, что Есть много людей Perl, которые пишут о Perl и вы никогда не слышали о них.

Более того, по железной человек, которого мы видим множество отличных от английского должности, а те из нас, с не-носителями английского как правило, может читать только свой родной язык и английский язык. Например, много японских сообщения появляются, и я вижу только куски кода Perl понятно там.

Копирование-вставка после URL-адреса в Google Translate на ежедневной основе очень раздражает. Я поймал себя на начало чтения последних итальянских сообщения о предстоящих YAPC:: Европе, а затем переход к Google Translate, как я не могу много читать итальянский еще. То же самое с немецким. Оба они являются более понятными, чем японские, хотя :-)

Сегодня я начал SayPerl.org сайт, который блоге агрегатор сообщений в сочетании с автоматическим переводчиком на английский язык.

Все сообщения, вне зависимости от языка оригинала, появляются на SayPerl.org на английском языке, таким образом, теперь я могу читать все, включая японский, персидский и китайский языки!

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

Чтобы продемонстрировать преимущества SayPerl.org, сравнивать эти две должности, оригинальных и переводных:

Слово "Rakvdv", который выглядит так, как это будет написано древние римляне, четко понятно сейчас.

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

Что касается технических деталей, я только хотел бы отметить еще одну проблему с обработкой Unicode. Так произошло и с WebService:: Google:: Язык. Такие ошибки являются одним из наиболее распространенных проблем с модулями Perl сегодня (вспомните, например, еще одна проблема с Unicode возникает тогда, когда Алекс кодирования PlanetPerl.ru).

sayperl.org

Начало использования SayPerl.org прямо сейчас!

Trim

| Комментариев: 3

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

Вместо двух замен

$str =~ s/^\s+//;
$str =~ s/\s+$//;

Себастьян указывает на такой вариант:

$str =~ s/^\s*(.*?)\s*$/$1/;

Интересно почитать и комментарии, где предлагают несколько похожих вариантов с альтернативами в регулярном выражении:

$str =~ s/(^\s+|\s+$)//g;

$str =~ s/(?:^\s+|\s+$)//g;

$str =~ s/^\s*|\s*$//g;

$str =~ s/^\s+|\s+$//g;

P. S. В комментариях, кстати, видно, как почти одно и то же выражение разные люди записывают по-разному.

Lingua::RU::Inflect

| 1 комментарий

Одна из приятных сторон в организации конференций — возможность получать удовольствие, косвенно (например, оплатив фуршет :-) участвуя в развитии перла и его приложений.

Александр Сапожников после блица Анатолия Шарифулина выложил на CPAN свой первый модуль Lingua::RU::Inflect, предназначенный для склонения русских имен.

Тест-драйв (с массой бойлерплейтов перед основным кодом :-):

use v5.10;
use utf8;
use open qw(:std :utf8);
use strict;
use Lingua::RU::Inflect qw(:all);

say inflect_given_name(GENITIVE, 'Андрей');

say join ' ', inflect_given_name(GENITIVE, 'Андрей', 'Борисыч');

Программа печатает

Андрея
Андрея Борисыча

Нелитературное Борисыч просклонялось правильно :-) Такой вот лингвоспан.

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

На прошлой неделе я купил книгу создателя Wolfram|Alpha Стивена Вольфрама A New Kind of Science. Книга, конечно, объемная (1200 страниц), и всю ее я еще не прочитал, но основная мысль заключается в том, что большинство, если не все, явлений в природе возможно запрограммировать последовательностью простых вычислений.

Тот рассказ о программировании полета мухи — а я его тогда проверил на C++ — я всегда помню и стараюсь пользоваться простыми расчетами, если это возможно.

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

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

my @grammars = (
    'RateWordNoun CurrencyNameGenitiveA',
    'RateWordAdjective CurrencyNameNominativeA',
    'HowMuchCurrencyNameWithValueA',
    'Value CurrencyCodeA ConvertWord CurrencyCodeB',
    'CurrencyNameWithValueA ConvertWord
     CurrencyNamePluralLocativeB'
);

Каждое слово в строке отдельной грамматики — вызов одноименной функции; каждая из них тоже простейшая:

sub RateWordNoun {
    return $rateWordNoun[int rand scalar @rateWordNoun];
}

sub RateWordAdjective {
   return $rateWordAdjective[int rand scalar @rateWordAdjective];
}

Эти функции используют небольшой набор слов:

my @rateWordNoun = ('курс');
my @rateWordAdjective = ('сколько стоит', 'почем');

Однако это не мешает делать и сколь угодно длинные списки:

my @currencyCode = qw(
    RUR EUR USD JPY CHF GBP UAH KZT CAD
    AUD BYR DKK ISK NOK SEK CNY TRY SGD ILS
);
my @currencyNameNominative =
   ('рубль', 'евро', 'доллар США', 'японская иена',
    'швейцарский франк', 'фунт стерлингов',
    'украинская гривна', 'казахстанский тенге',
    'канадский доллар', 'австралийский доллар',
    'белорусский рубль', 'датская крона',
    'исландская крона', 'норвежская крона',
    'шведская крона', 'китайский юань',
    'турецкая лира', 'сингапурский доллар', 'шекель');

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

sub random { 
    my $template = $grammars[int rand scalar @grammars];
    $template =~ s{(\w)\b}{$1()}g;
    $template =~ s{([AB]\b)\(\)}{(\$$1)}g;
 
    $template =~ s{\s+}{ . ' ' . }g;
 
    return eval $template;
}

Блюдо готово.

курс швейцарского франка
сколько стоят 2 шведские кроны
курс японской иены
29 UAH в TRY
15 сингапурских долларов в норвежских кронах
почем турецкая лира
сколько стоят 36 евро
почем шекель
8 CHF в SEK
51 китайский юань в белорусских рублях
360 EUR в AUD
82 шекеля в рублях
600 долларов США в австралийских долларах

Search::Query

| Нет комментариев

Разбор строки поискового запроса — задача, которая быстро превращается в головную боль, как только захочется ввести синтаксис запросов. Если фразы типа +театр -большой разобрать еще просто, то при обработке запроса театр & ((малый & москва) | (большой & урюпинск)) без грамматики не обойтись.

В январе 2010 на спане появился модуль Search::Query, который на первый взгляд поможет разрулить некоторые проблемы разбора поисковых запросов и даже подготовить подзапросы для последующих обращений к собственно поиску и склеиванию методами MapReduce.

Модуль заменяет простую грамматику поискового запроса, предоствляя в замен несколько методов, которые позволяют быстро представить запрос в виде древовидной структуры.

Разбор строки выполняет инстанс $parser типа Search::Query::Parser. (Сам класс Search::Query не содержит рабочую логику.)

use Search::Query;

my $parser = Search::Query->parser();

Затем вызывают метод parse, передав ему поисковый запрос.

my $query = $parser->parse('большой театр');

Возвращаемое значение — значение типа Search::Query::Dialect::Native. Оно может быть преобразовано в строку (это поможет, например, визуально проконтролировать правильность разбора), хотя в простейшем случае она не сильно отличается от запроса:

+:большой +:театр

Кроме того, возможно пройтись по построенному дереву запроса, вызвав метод walk():

say $query->walk(\&my_walk);

Функция, ссылку на которую получает метод walk(), получает четыре параметра, содержащие объяснение частей запроса.

sub my_walk{
    my ($clause, $dialect, $code, $prefix) = @_;
    . . .
}

Модуль Search::Query позволяет создавать и собственные диалекты поисковых языков, наследуя их от Search::Query::Dialect, либо определяя типовые конструкции при создании парсера:

my $parser = Search::Query->parser({
        and_regex => 'И',
        or_regex  => 'ИЛИ',
        not_regex => 'НЕ',
    });

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

Разбить текст на предложения — довольно распространенная задача. (Часто возникает схожая задача — выделить одно-два первых предложения.)

На спане есть несколько модулей, которые частично решают задачу. К сожалению, некоторые модули просят установить локаль (в наш век юникода), однако на поверку вполне уверенно работают и с русскими текстами безо всяких локалей.

Мне хочется, чтобы на входе был UTF-8, и не разрывались части предложения типа А. С. Пушкин, а еще лучше — зал им. А. Пушкина.

Text::Sentence

Модуль входит в состав комплекта HTML::Summary. Без установки локали в русских текстах лихо пропускает половину точек (да, я нагло тестировал юникод).

use Text::Sentence qw(split_sentences);

my @s = split_sentences($text); # возвращается список

Lingua::EN::Sentence

use Lingua::EN::Sentence qw(get_sentences);

my $s = get_sentences($text); # возвращается ссылка на список

Несмотря на название, модуль уверенно выделяет русские предложения, и не разбивает инициалы. Не прошел тест на немецкую дату 30. Dezember (разбил на точке), но на это есть другой модуль. Зал им. А. С. Пушкина поделил на части после им.

Lingua::DE::Sentence

use Lingua::DE::Sentence;

my $s = get_sentences($text); # ссылка на список

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

Text::ToSentences

Свежачок: модуль, добавленный на спан позавчера.

use Text::ToSentences qw(convert);

my $s = convert($text);

Функция convert возвращает ссылку на список предложений.

Здесь все хорошо с юникодом, но из зала им. А. С. Пушкина получислоь четыре предложения.

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

P. S. Это была история о том, почему рождаются велосипеды :-)

is_ru

| Нет комментариев

Обожаю простые решения.

Задача: разделить текстовые записи и выделить из них те, которые написаны на русском языке.

Решение:

    my $without_ru = $text;
    $without_ru =~ s{[а-я]}{}gi;
    next if length($without_ru) > 0.6 * length($text);

Страницы

  • img

Об архиве

Эта страница содержит последние записи категории Лингвистика.

Предыдущая категория — Книги.

Следующая категория — Мероприятия.

Смотрите новые записи на главной странице или загляните в архив, где есть ссылки на все сообщения.