Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > Подкиньте задачки по перлу


Автор: Irokez 12.9.2007, 16:05
Учу вот перл smile Голое чтение книги утомляет. Хочется попробовать реализовать какие то небольшие, но и не совсем элементарные задачи.
Перл понадобится в основном для обработки текстов (точнее для NLP), не для веб и не для системного программирования.
Буду признателен, если подкините задачки, на которых смогу учиться. Или может ссылки на задачи. Чьи то домашние задания тоже пойдут, но за срок выполнения я не ручаюсь smile

Автор: amg 13.9.2007, 06:56
Вот, например.
Написать программу-фильтр, преобразующую текст типа
Код

Yandex  www.yandex.ru
Google  www.google.com
Aport   www.aport.ru
Yandex  www.yandex.ru
Google  http://google.ru
Aport   http://aport.ru
Yandex  http://ya.ru
Google  http://www.google.ru
Yandex  yandex.ru
в
Код

Google www.google.com google.ru www.google.ru
Aport www.aport.ru aport.ru
Yandex www.yandex.ru ya.ru yandex.ru
В самый раз для перла. С использованием хэша массивов и регулярных выражений - всего несколько строк.
И в лингвистике подобное встречается. Например, сгруппировать по частям речи текст типа Мама(сущ.) мыла(гл.) раму(сущ.)

Автор: Irokez 13.9.2007, 09:13
ммм.. решил на 10-й строке сделать проверку на уже имеющийся урл.. однако, почему то не работает =(
Код

push(@{$urls{$site}}, $url) unless grep(/^$site$/i, @{$urls{$site}});

Автор: Sadok 13.9.2007, 09:43
Каждый перловик должен написать свою поисковую систему smile

Автор: Irokez 13.9.2007, 10:36
а .. разобрался.. и вдобавок сократл код =)
Код

#!/usr/bin/perl -w

open(FILE, 'url-list.dat') or die 'Cannot open file!';
$urls = {};
while (<FILE>) {
    ($site, $url) = split(/\s+/);
    if (!exists($urls{$site})) {
        $urls{$site} = [$url];
    } else {
        push(@{$urls{$site}}, $url) unless grep(/^$url$/i, @{$urls{$site}});
    }
}

print "$_ " . join(' ', @{$urls{$_}}) . "\n" foreach (sort keys %urls);


Добавлено через 1 минуту и 52 секунды
вопрос.. а нужно ли вообще условие if (!exists($urls{$site})) ? Без него вроде тоже работает и предупреждений не дает

Автор: Irokez 13.9.2007, 11:21
итого:
Код

#!/usr/bin/perl -w

use strict;

open(FILE, 'url-list.dat') or die 'Cannot open file!';
my %urls;
while (<FILE>) {
    my($site, $url) = split(/\s+/);
    push(@{$urls{$site}}, $url) unless grep(/^$url$/i, @{$urls{$site}});
}

print "$_ " . join(' ', @{$urls{$_}}) . "\n" foreach (sort keys %urls);

Автор: Shaggie 13.9.2007, 11:44
Цитата(amg @  13.9.2007,  07:56 Найти цитируемый пост)
Yandex  www.yandex.ru
Google  www.google.com
Aport   www.aport.ru
Yandex  www.yandex.ru
Google  http://google.ru
Aport   http://aport.ru
Yandex  http://ya.ru
Google  http://www.google.ru
Yandex  yandex.ru


Цитата(amg @  13.9.2007,  07:56 Найти цитируемый пост)
Google www.google.com google.ru www.google.ru
Aport www.aport.ru aport.ru
Yandex www.yandex.ru ya.ru yandex.ru

Irokez, обрати внимание - ведущие "http://", если они есть, должны быть обрезаны по условию задачи.

Автор: amg 13.9.2007, 13:26
Irokez, браво! Для новичка в перле - 5+.
Еще я просил не просто программу, а программу-фильтр (получает имя файла из параметра или текст из stdout), причем не случайно. На своем опыте знаю, что именно для обработки текстовых файлов фильтры предпочтительнее, т.к. сочетают гибкость использования с простотой написания. На каждую типовую задачу - свой коротенький фильтр, а потом их можно комбинировать через пайп.

Еще при "разовой" обработке текстов мне очень помогают так называемые однострочники - это когда из командной строки вызывается перл с нужными опциями, текстом программы после опции -е и именем файла в качестве параметра. Опции у перла такие, что для множества задач текст программы получается очень коротким. 

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

Автор: korob2001 22.9.2007, 12:31
Цитата(amg @  13.9.2007,  10:26 Найти цитируемый пост)
Отсюда очередная задача: написать однострочник, который напечатает имена всех встречающихся в предыдущем тексте порталов (но без повторов).

Как вариант:
Код

perl -e "$_{(split /\s+/)[0]}++ while <>;print join(\"\n\", keys %_)" file.txt

В конце можно указать несколько файлов со списком порталов и все они будут обработаны как один. Например:
Код

perl -e "$_{(split /\s+/)[0]}++ while <>;print join(\"\n\", keys %_)" file1.txt file2.txt file3.txt


Автор: Nab 22.9.2007, 15:19
Ну вот smile 
Модер с моря приехал небось, и с ходу в бой smile

Задачка то была не для монстров от перла, типа тебя smile


Автор: under_sun 15.10.2007, 16:42
Если тебе еще нужны задачки, то могу подкинуть одну достаточно интересную задачку на сравнение выражений.

Задача заключается в следующем: 
Даны два выражения - например   a+2bc-(b+c)^2+2*a   и   3a-(b*b+c^2). Нужно сказать равны они или нет.
Знак умножения может опускаться (т.е. 2bc, 2*bc, 2*b*c считается корректной записью).
Также может быть любая вложенность скобок. 
Вот еще пара примерчиков:
               (x+z)(x+z)(x+z)   и   (x+z)^3
               (a+b)(3+4+5-2*(a-2b)*3+2a-12b)   и   -4*(a+b)(a-3)

Удачи  smile    

Автор: korob2001 15.10.2007, 17:46
А чему равны переменные x, z, a, b, c?

Автор: under_sun 16.10.2007, 15:56
Ни чему не равны, это произвольные переменные. 
В этом то и фишка  smile 

Автор: arto 16.10.2007, 19:09
а вы сами знаете решение?

Автор: KSURi 16.10.2007, 19:21
Если слегка упростить задачу: знак степени заменить на ** и убрать условие об опускании знака умножения, то задача решается в одну строку)

Автор: arto 16.10.2007, 21:39
а доказательство сколько строк будет занимать?

Автор: KSURi 16.10.2007, 23:37
Док-во чего? Я имел ввиду вот это:
Код

perl -ne '($a,$b)=split' ';print eval$a>eval$b?$a:$b,"\n"'
(4+4)**2+7 3+(2*1)**2
(4+4)**2+7

Автор: under_sun 17.10.2007, 13:00
Цитата

а вы сами знаете решение?

Да, я сдавал эту задачку в универе.


KSURi, хочу напомнить, что выражения могут быть не только числовыми, но и буквенными smile 

Автор: KSURi 17.10.2007, 15:56
Код

C:\>perl -ne "($a,$b)=split' ';print eval$a>eval$b?$a:$b,qq{\n}"
(1+a)*7 (b+2)*2
(1+a)*7


Автор: under_sun 17.10.2007, 17:00
eval -ом одним здесь ничего не сделать, т.к. он не понимает символьную арифметику )).
(a*7 будет 0 и a+с тоже)
т.е. в твоем коде a*5 и a*7 будут равны, и все выражения в, которых нет цифр, тоже....


  

Автор: KSURi 17.10.2007, 18:17
Ну начальное условие не совсем точно было написано...

Автор: amg 19.10.2007, 13:24
Задача не то чтобы сложная, но довольно муторная (если решать ее так, как я это делал). Хотя, может, есть способ гораздо проще.
Код

while (<DATA>) {
  chomp;        # убираем конец строки
  my $inp = $_; # сохраняем на будущее исходное выражение
  
  # Возведение в степень заменяем умножением
  warn "Error: $&\n" and next if m/\^\D/;
  s/(\([^)(]+\)|\d+|[a-z])\^(\d+)/$2 ? ("$1*"x($2-1)).$1 : 1/eg;

  # Добавляем недостающие знаки умножения
  s/(\d+|[a-z]|\))(?=\d|[a-z]|\()/$1*/g;

  s/-/+M*/g;          # заменяем - на "-1" (M)
  s/^\+|(?<=\()\+//g; # убираем + в начале и после открывающей скобки

  # Пропускаем получившееся выражение через perl: не поругается ли
  (my $test = $_) =~ s/[a-zM]/1/g;
  defined(eval $test) or warn "Error in $inp=$_: $@\n" and next;

  1 while (s/\(([^)(]+)\)\*\(([^)(]+)\)/ mult($1,$2) /ge or
           s/\(([^)(]+)\)\*(\d+|[a-zM])/ mult($1,$2) /ge or
           s/(\d+|[a-zM])\*\(([^)(]+)\)/ mult($1,$2) /ge or
           s/(^|\+|\()\(([^)(]+)\)(\+|\)|$)/$1$2$3/g);

  my %h;
  foreach (split /\+/, $_) {
    $_ = join '*', sort split '\*', $_;
    s/^([a-z])/1*$1/;
    s/^([\dM*]+)$/$1*Z/;
    my ($v,$k) = m/^([^a-zZ]*)\*(.+)$/;
    $h{$_}++ unless defined $k;
    $v =~ s/M/-1/g;
    $v = eval($v); die $@ if $@;
    $h{$k} += $v;
  }

  my $res = '';
  foreach (sort keys %h) {
    next if $h{$_} == 0;
    $h{$_} = "+$h{$_}" if $h{$_} > 0;
    $res .= "$h{$_}$_";
  }

  $res =~ s/(?<!\d)1(?=[a-z])//g;
  $res =~ s/^\+//;
  $res =~  s/[*Z]//g;
  $res =~ s/([a-z])(\1+)/ "$1^".(length($2)+1) /eg;
  $res = '0' unless $res;
  printf "%30s = %s\n", $inp, $res;
}

sub mult {
  my @a = split /\+/, $_[0]; 
  my @b = split /\+/, $_[1];
  my $result;
  foreach my $a (@a) {
    foreach my $b (@b) {
      $result .= "$a*$b+";
    }
  } 
  return '(' . substr($result,0,-1) . ')';
}

__DATA__
(a+b)(3+4+5-2*(a-2b)*3+2a-12b)
-4*(a+b)(a-3)
(x+z)(x+z)(x+z)
(x+z)^3
a+2bc-(b+c)^2+2*a
3a-(b*b+c^2)

Получается 
Код

(a+b)(3+4+5-2*(a-2b)*3+2a-12b) = 12a-4a^2-4ab+12b
                 -4*(a+b)(a-3) = 12a-4a^2-4ab+12b
               (x+z)(x+z)(x+z) = x^3+3x^2z+3xz^2+z^3
                       (x+z)^3 = x^3+3x^2z+3xz^2+z^3
             a+2bc-(b+c)^2+2*a = 3a-b^2-c^2
                  3a-(b*b+c^2) = 3a-b^2-c^2
Дальше понятно - просто сравнить.
PS Исправил код (см. ниже)

Автор: under_sun 19.10.2007, 16:36
amg, классное решение! smile  Особенно понравилась вот эта сторочка: 
Цитата
s/-/+M*/g;

Согласен, что довольно муторная задача. Довольно много случаев.
Цитата

Хотя, может, есть способ гораздо проще.

Может и есть, но явно не у меня с моими 190 строками кода  smile

Неправильно обработанные примеры:
Код

x(a+b(c+d)) = 1(ax+1(bc+bd))
a3 = a3


И 0 - тоже многочлен )).

p.s.
Если откровенно, у меня тоже работает не все )).



Автор: amg 20.10.2007, 13:49
Исправил свой код от ошибок, про которые сказал under_sun, уже начал было комментарии вносить и код оптимизировать, хотел даже операцию деления реализовать, потом догадался на CPAN глянуть и понял, что все это зря. Конечно же, оказалось, что символьная алгебра на perl'е давным-давно реализована, причем в полном объеме. 

Автор: tishaishii 7.11.2007, 13:54
Есть группа менеджеров, каждый работает над группой проектов. Необходимо уровнять количество проектов у каждого менеджера так, чтобы минимально прервать связь менеджера со своими проектами.

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

Попытайся решить.

Автор: ZLOvar 4.6.2009, 09:46
Всем привет, я тоже задачку хочу! только полегче чем верхнее))
 smile  smile  smile  smile 

Автор: Suppir 4.6.2009, 11:10
Вот задачка:

ЕСТЬ:
Строка $a, где-то в середине строки расположена запись даты 04.05.2007.
Дата может быть с начальными нулями и без: 04.05.2007 или 4.5.2007 или 04.5.2007.
Дата может быть любой в интервале от 01.01.1900 до "ближайшей субботы" - иначе выдавать ошибку.

НУЖНО:
Нужно в строке $b найти любое упоминание об этой дате.
В строке $b дата может быть в следующих форматах:
04.05.2007
4/5/2007 г.
04 мая 2007
4 мае 2007года
и подобные этим варианты

Автор: ZLOvar 4.6.2009, 11:36
Suppir, а можно показать в коде? (мне так легче будет)
зарание спасибо)

Автор: shamber 4.6.2009, 11:51
ZLOvar, вам как? Нужна задача и ее решение в одном посте ?

Автор: Suppir 4.6.2009, 12:45
ZLOvar, так я предлагаю вам написать решение это задачи в Perl-коде smile Или вам не понятны условия задачи?
.
1. Есть строка $a, в ней записана дата в формате 04.05.2007 (или 4.4.2007 или 04.4.2007). Нужно написать регулярное выражение, которое находит эту дату.
2. Написать условие, которое проверяет, чтобы найденная дата была в интервале от 01.01.1990 до "ближайшей субботы" (например, у этой субботы дата 06.06.2009)
3. Написать регулярное выражение, которое найдет эту же дату в следующих форматах: 
04.05.2007
4/5/2007 г.
04 мая 2007
4 мае 2007года
.
Во всех строках даты могут располагаться в любом месте (начале, середине, конце). Желательно, чтобы все эти проверки были оптимизированы - представьте, что вам нужно "прочесать" миллион строк.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)