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

Trim

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

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

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

$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);

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

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

Даккар приводит такие примеры:

VMWare Server è *gratuito*. (VMWare Server бесплатный.)

VirtualBox e qemu sono *liberi*. (VirtualBox и qemu свободные.)

К сожалению, читать итальянскую рассылку mongers@lists.perl.it можно только «вживую», веб-архива у нее нет.

 

Страницы

  • img

Об архиве

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

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

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

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