[PERL] Вот как оказывается можно писать...

sakura

Я программирую на Perl в некой фирме. Часто приходиться копаться в чужих кодах. При этом находятся вещи, которые довольно трудны до понимания обычному смертному.
Пример 1:
my $x = ...; # некое число
my $res = exp( $x * log(10) )
Если вы внимательно посмотрите, то увидите, что значение переменной $res - это десять в степени $x. Возведение в степень в Perl пишется очень просто - две звездочки:
$res = 10 ** $x;
Пример 2:
my $str = "1 2 3 4 5"; # Некая строка со значениями, разденными пробелами. Нужно вытищить последний элемент, т.е. в данном случае число 5
my @arr = split( / / , $str );
@arr = reverse @arr;
my $res = $arr[0];
Прикольно, да. Решить эту задачу можно двумя намного более простыми способами:
Способ 1 - регулярное выражение:
my ($res) = $str =~ /(\S+)$/;
Способ 2 - можно сразу узнать последний элемнт массива. Не обязательно делать reverse:
my @arr = split( / / , $str );
my $res = $arr[-1];
P.S. Это происходит из-за неполного знания прекрасного языка Perl. Поэтому, люди, если вы не знаете как сделать простую вещь без изврата, лучше спросите. А то потом довольно тяжело понимать, что там хотел сделать программист, который это написал.

Marinavo_0507

Поэтому, люди, если вы не знаете как сделать простую вещь без изврата, лучше спросите.
У тебя спрашивать? А ты часто в форуме бываешь?

skvoria

пеаришься?

sakura

ну да, конечно спрашивай! я всегда рад помочь! только тут почему-то очень мало вопросов по Perl... Обычно C да PHP...

ark21

ладно, вопрос такой:
как в цикле использовать значения $1, $2 и т.д. без евала ибо он тормозной что песдетц.
я сделал кое-как, но хотелось бы услышать мнение специалиста по этому поводу(вдруг ему придется копаться в моем коде)

otets-mihail

вот как надо писать
url

sakura

напиши, если не сложно, кусок кода, где ты используешь переменные $1 и $2.
Вообще эти переменные не обязательно использовать. Можно сразу в одну строчку сохранять значения из регулярного выражения, например:
my @strs = ( "Hello, world!" , "Larry Wall" );
foreach my $str (@strs)
{
my ($first, $second) = $str =~ /(\S+)\s+(\S+)/;
print "
first = '$first'
1 = '$1'
second = '$second'
2 = '$2'
";
}
Напечатается вот что:
first = 'Hello,'
1 = 'Hello,'
second = 'world!'
2 = 'world!'
first = 'Larry'
1 = 'Larry'
second = 'Wall'
2 = 'Wall'
В твои переменные $first и $second попадает то же, что и в переменные $1 и $2.
По поводу eval. Я не совсем понимаю, зачем его нужно в твоем случае использовать. Вообще eval юзают аналогично явавской конструкции try{ ... }catch( ... ){ ... }, т.е. в местах, где вызываются потенциально опасные функции (т.е. функции, которые могут упасть). И в случае, если функция в евале падает, то программа, которая этот евал вызвала не падает, а продолжает работать.
А вообще лучше приведи код, по которому у тебя вопрос..

ark21

Есть функция, которая получает паттерн регэкспа. Но скобок которые из него нужны конечно всегда разное число, зато есть правила по которым их можно и нужно обрабатывать(независимые от их числа).
То есть смысла такой:
/$pattern/;
for (1 .. num_columns) {
сделать что-то с $i, ну то есть не с переменной i, а с $1, etc.
}
один из вариантов - написать евал
eval "my \$a=\$$_;";
Но он работает очень медленно(известный факт а это происходит в большом цикле.
поэтому пришлось сделать по-другому, но хотелось бы узнать как правильно.
насчет try/catch, это другой вариант евала, не eval EXPR, а eval BLOCK в основном

Marinavo_0507

юзай @- и @+

stm7583298

а что будет быстрее, @+ и @-, или использовать $^R в теле регэкспа?

sergey_m

Прикольно, да. Решить эту задачу можно двумя намного более простыми способами:
Способ 1 - регулярное выражение:
my ($res) = $str =~ /(\S+)$/;
Это будет медленнее.
Способ 2 - можно сразу узнать последний элемнт массива. Не обязательно делать reverse:
my @arr = split( / / , $str );
my $res = $arr[-1];
Раз уж выёбываться, то до конца! Зачем временные переменные?

my $res = (split(/ /, $str[-1];

smit1

Читаю этот тред, и не устаю поражаться, какое же перл гавно. То есть, извините, не гавно, а специфический язык.
Как будет решаться такая задача в любом нормальном ЯП? Примерно так:
  1) двигаясь с конца строки, найти первый непробельный символ
  2) двигаясь с этой позиции дальше, найти первый пробел
  3) подстрока между позициями из 1 и 2 - искомая
Количество операций равно (длина строки - позиция начала слова если нет финальных пробелов - длина слова.
Теперь смотрим на перл.
Решение с массивом как минимум просматривает всю строку + (что ещё печальнее) делает дохуя не нужных аллокаций памяти и копирования туда кусков строки. А если в строке миллион символов и тысяча слов? Тогда как нам нужно только последнее?
С регэкспом оценить трудоёмкость сложнее, но есть надежда, что такой регэксп должен прорабатывать за О(длина строки при этом лишней памяти не аллоцируя. Так что для достаточно больших строк с большим числом слов регэскп может порулить.

bobby

Ты, наверное, думаешь, что на перле нельзя реализовать твой алгоритм?

smit1

>Ты, наверное, думаешь, что на перле нельзя реализовать твой алгоритм?
Я перл не рюхаю, там разве посимвольный доступ к строкам есть? Субстры не предлагать - это скорее всего опять маллоки, опять сосут.
Ну если есть совсем правильный способ - то автор темы - лох бп

sergey_m

Как будет решаться такая задача в любом нормальном ЯП? Примерно так:
1) двигаясь с конца строки, найти первый непробельный символ
Выходит C уже не нормальный ЯП.

sergey_m

Я перл не рюхаю, там разве посимвольный доступ к строкам есть?
Прикинь!

Marinavo_0507

Если хочешь считать маллоки - пиши на C или ассемблере.

smit1

>Выходит C уже не нормальный ЯП.
Нормальный, зависит от того, что считать строками. ;-)
ЗЫ Даже для 0-терминэйтед строк, когда с ними делаются какие-то пертурбации, зачастую кем-то уже посчитано, какая у них длина.
ЗЗЫ Упражение специально для Глеба: модифицировать пункты 1-2 "моего алгоритма" на случай 0-терминэйтед строк. Подумать, что принципиально изменилось по отношению к перлу.

maggi14

> А то потом довольно тяжело понимать, что там хотел сделать программист, который это написал.
что хотел сделать программист, написавший a = exp(b*log(c должно быть понятно любому другому нормальному программисту с полуслова. Даже если в рамках конкретного перла эта запись излишня.

smit1

>Прикинь!
Монжо пример из одной строчки, чото не могу найти как это.

Marinavo_0507

Как будет решаться такая задача в любом нормальном ЯП? Примерно так:
1) двигаясь с конца строки, найти первый непробельный символ
2) двигаясь с этой позиции дальше, найти первый пробел
3) подстрока между позициями из 1 и 2 - искомая
Если это решать, как математическую задачку - то да.
А на практике, очень редко бывает, что последнее слово нужно, а предыдущие слова - нет. Поэтому язык должен уметь достаточно понятно и эффективно разбивать строки на слова. Perl в области его типичных применений это умеет неплохо.

sergey_m

Монжо пример из одной строчки, чото не могу найти как это.
Это substr. Он может быть и rhs.

sakura

Раз уж выёбываться, то до конца! Зачем временные переменные?
code:
my $res = (split(/ /, $str[-1];
согласен. еще можно так писать:
my $res = unshift split(/ /, $str);

uncle17

ржачный трэд. Не поленился прочитать весь на курорте с кпк+гпрс. Кстати, некоторые адекватные вещи порадовали - буду пользовать, спасибо.

sakura

Решение с массивом как минимум просматривает всю строку
Функция split позволяет просматривать не всю строку, а только до определенного количества кусочков, например:
my($head,$body,$tail) = split / /, '1 2 3 4 5', 3;
# Here:
# $head = '1'
# $body = '2'
# $tail = '3 4 5'

Dasar

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

sakura

в данном случае тоже можно всю строку не просматривать:
my ($res) = split( / / , reverse $str, 1 );

Dasar

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

Dasar

имхо, все эти выкрутасы сводят к нулю принцип: написана единожды, используется везде, т.к. все эти выкрутасы приводят к неочевидным ошибкам, когда, кажется, что у автора кода все работает, а при попытках сделать шажок влево/вправо - все рассыпается.

Dasar

кстати, все вышеприведенные примеры на пустой строке как будут себя вести?

sakura

Есть функция, которая получает паттерн регэкспа. Но скобок которые из него нужны конечно всегда разное число, зато есть правила по которым их можно и нужно обрабатывать(независимые от их числа).
а от чего тогда зависят эти правила?
Мне кажется, что этот случай довольно сложный для того, чтобы решить его с помощью одного универсального регулярного выражения (даже если это и можно, то получится регулярное выражения с кучей условий, которое тяжеловатo будет понимать).
Я бы в данном случае постарался разбить все возможные варианты обрабатываемой строки на несколько типов (исходя из каких-либо характерных для каждого варианта кусков в строке). А потом определял для каждой строки что это за вариант и применял к нему свое регулярное выражение:
if( /характерное_свойство_варианта_1/ )
{
# регулярное выражение для варианта строки 1
}
elsif( /характерное_свойство_варианта_2/ )
{
# регулярное выражение для варианта строки 2
}
...
хотя мне кажется я не ответил на вопрос. если не сложно, приведи пожалуйста разные варианты строк, а также, что из них нужно вытащить.

sakura

значения состоят из одного символа
схема такая
split /PATTERN/, EXPR, LIMIT
третий аргумент ( у меня он равен 1) определяет не длину значения, а количество раз, которое нужно применить к строке EXPR регулярное выражение EXP.
если только под словом значение ты подразумевал кусочки, получаемы после разбиения исходной строки...

sakura

кстати, все вышеприведенные примеры на пустой строке как будут себя вести?
очень просто - переменным присвоятся значения undef. При этом Perl НЕ выругается (только что проверил что, конечно, не очень хорошо. Но вообще это всегда нужно проверять...

Dasar

так что будет на выходе, если входная строка - '13', для варианта с reverse-ом?

sakura

то, что я написал не правильно, т.к. если действительно больше одного символа, то в $res попадет не просто последний кусочек строки, а перевернутый кусочек.
my ($res) = split( / / , reverse $str, 1 );
для $str = 13
получается $res = 31
Ты прав, этот вариант неправильный....

smit1

в данном случае тоже можно всю строку не просматривать:
my ($res) = split( / / , reverse $str, 1 );
А что, reverse $str не просматривает всю сроку? Он ещо и память на копию жрёт наверняка.

smit1

>Это substr. Он может быть и rhs.
Прикинь! Про субстры я написал на 20 постов выше.
В порицание за невнимательность (а ещо модератор, ай-яй-яй) можешь попробовать заботать исходники перла и доказать мне, что субстр реализован не совсем примитивно, и я не прав. Было бы приятно порадовацо за перл

sergey_m

> my $res = unshift split(/ /, $str);
Type of arg 1 to unshift must be array (not split)

sergey_m

В порицание за невнимательность (а ещо модератор, ай-яй-яй) можешь попробовать заботать исходники перла и доказать мне, что субстр реализован не совсем примитивно, и я не прав.
Почему я должен искать в исходниках и что-то доказывать, когда высказал спорное предположение ты, а не я?
P.S. Кстати я не модератор.

ark21

я кстати не до конца понимаю почему разговор пошел о производительности перла. Хочешь скорости и оптимальности - пиши на ассемблере

wwoland

может надо шифт а не аншифт?

ava3443

Хочешь скорости и оптимальности - пиши на ассемблере
Давно писал на ассемблере?

ark21

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

sakura

может надо шифт а не аншифт?
если мы хотим последний элемент массива, то нужен именно unshift.
Но конструкция, которую я применил действительно не работает. Перл ругается....
Это странно, потому что мне кажется такое написание вполне естественно...

sakura

по поводу производительности.
Извините, конечно, но, если мы говорим о Perl, то тут уж давайте не будем заострять внимание на производительности. Потому что главная сила перла, это не скорость работы перловых скриптов, а скорость их написания.
Кстати, если кому-нибудь интересно, то есть офигенный модуль - Inline.pm.
http://search.cpan.org/search?query=inline&mode=all
Он позволяет объединять совершенно разные языки программирования в перловом скрипете. Это просто офигенно! Потому что места программы, где нужны сложные и большие вычисления можно писать на С++, а разбор файлов и работу со строками - на перле.
Этот модуль вообще позволяем почти все языки программирования использовать (только вот ассемблера там, кажется, нет)

ava3443

если мы хотим последний элемент массива, то нужен именно unshift.
и вовсе не unshift, а pop

ava3443

Но конструкция, которую я применил действительно не работает. Перл ругается....
Это странно, потому что мне кажется такое написание вполне естественно...

Ботай разницу между list и array: perldoc -q "list and an array".
split создаёт list, в то время как pop и shift требуют array.
Выражаясь иначе, list, возвращаемый split модифицировать нельзя, в то время как shift и pop требуют lvalue (которое можно модифицировать).

ava3443


What is the difference between a list and an array?
An array has a changeable length. A list does not. An array is something you can push
or pop, while a list is a set of values. Some people make the distinction that a list
is a value while an array is a variable. Subroutines are passed and return lists, you
put things into list context, you initialize arrays with lists, and you foreach across
a list. "@" variables are arrays, anonymous arrays are arrays, arrays in scalar context
behave like the number of elements in them, subroutines access their arguments through
the array @_, and push/pop/shift only work on arrays.

ava3443

>> my $res = unshift split(/ /, $str);
> Type of arg 1 to unshift must be array (not split)[/quote]
Можно ещё анонимный array сделать
my $res = pop @{[split / /, $str ]};

wwoland

да дада,уншифт добавляет с конца в массив же =)

sany79

Есть функция, которая получает паттерн регэкспа. Но скобок которые из него нужны конечно всегда разное число, зато есть правила по которым их можно и нужно обрабатывать(независимые от их числа).
То есть смысла такой:
/$pattern/;
for (1 .. num_columns) {
сделать что-то с $i, ну то есть не с переменной i, а с $1, etc.
}
один из вариантов - написать евал
eval "my \$a=\$$_;";
Но он работает очень медленно(известный факт а это происходит в большом цикле.
Когда-то сам столкнулся с этой проблемой. Помогло следующее: ${$i}.
Вот иллюстрация: программа, находящая одно из решений линейного диофантова уравнения с положительными коэффициентами и положительной правой частью (и то и другое передаётся через командную строку). С помощью регулярных выражений. Зацените идею! Идея, к сожалению, не моя, я лишь в учебно-тренировочных целях адаптировал программу на случай произвольного количества неизвестных. Магическую букву 'o' можно заменить на любую другую.

#!/usr/bin/perl -w

sub solve
{
my $b=pop @_;
my @a=@_;
my @x;
my $k;

map {$_--} @a;

my $re='^';
for $k(1..@a)
{
$re.="(o+)\\$k\{".$a[$k-1]."}";
}
$re.='$';
if'o' x $b)=~m/$re/)
{
for $k(1..@a)
{
$x[$k-1]=length ${$k};
}
}
else
{
die "Не могу решить уравнение!\n";
}
return @x;
}

print join ' ', solve(@ARGV "\n");

Marinavo_0507

${$i} не работает с use strict

sany79

Это верно.
Я вот подумал, что если use strict совершенно необходим в программе, то ведь можно хулиганские места заключить в блок и вставить no strict... Так что это не очень серьёзное ограничение. Всё же эстетичнее, чем eval.

Sharp

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

sany79

Короче, идея такая.
Ищется решение уравнения a1*x1+...+a_n*x_n=b (все a и b>0). Формируется строка, состоящая из b букв (в данном примере 'o'). Строка сопоставляется с RE: одна или больше строк 'o....o' длины a_1 (обозначим, как в Перле, 'o' x a_1 затем одна или больше строк 'o....o' длины a_2 ('o' x a_2) и т. д. Все эти фрагменты заключаем в скобки, чтобы запомнить найденное в переменных $1, $2,...
Затем считаем длины $1, $2, ... Это в точности a_1*x_1, a_2*x_2,... Делим на a_1, a_2,... Получаем x_1, x_2,...
Особенности этой программы: необычное применение RE, формирование RE в цикле, и использование в цикле ${$k}, о чём спрашивал .
Вот и всё.

Marinavo_0507

А вот я не понял, что такое
"\\$k"
внутри выражения и зачем

sany79

При a_1=3, a_2=2, b=7 получается такое RE:
'ooooooo'=~m/^(o+)\1{2}(o+)\2{1}$/
То есть (o+ затем то же самое ещё 2=3-1 раза; затем снова (o+ затем то же самое ещё 1=2-1 раз. '\3' в RE — то же самое, что и $3, но предназначен для использования в самом RE. Перл не любит, когда $1, $2,... вставляют в RE.
Я был не совсем точен, описывая словами данный алгоритм. Искомые иксы — это длины строк $1, $2,...

Marinavo_0507

А почему не "o+){" . $a[$k] . "})" или типа того? Вот меня и смутили обратные слеши.

sany79

Кажется, можно и так, только с поправкой:
"(?:(o+){" . $a[$k-1] . "})"
И не делать map {$_--} @a.

sakura

Ботай разницу между list и array: perldoc -q "list and an array".
split создаёт list, в то время как pop и shift требуют array.
А как еще можно из листа сделать массив, кроме следующего геморойного варианта:
my $res = pop @{ [ split(/,/ , "1,2,3") ] };
Т.е., чтобы не далать сначала ссылку, а потом из ссылки массив... Должен же быть более красивый способ

sakura

и вовсе не unshift, а pop
да да. это я что-то глупость написал. извиняйте...
просто этими функциями не часто приходится пользоваться, вот и перепутал

wwoland

Проще наверное все таки не литерал сплиту передавать,а переменную,тогда с этим проблем не будет.

sakura

${$i} не работает с use strict
А что это вообще за переменная? или это типа приведения ссылки на массив к массиву:
@{[$aref]}
тольок для скалаяров?
никогда не сталкивался...

sakura

Проще наверное все таки не литерал сплиту передавать,а переменную,тогда с этим проблем не будет.
ты это имеешь ввиду:
my $var = '1 2 3';
my $head = pop split(/ /, $var);
все равно не работает...
Когда я спрашивал про преобразование листа в массив я хотел избежить такой конструкции:
my $head = pop @{[split(/ /, '1 2 3')]};

wwoland

my $var = '1 2 3';
my $head = pop split(/ /, $var);
а так
my @var=split(/ /,'1 2 3');
my $head = pop @var;

? =)

sany79

>>>> ${$i} не работает с use strict
>> А что это вообще за переменная?
Это способ обратиться к переменной, имя которой содержится в $i. Либо, если $i — ссылка на скаляр, то ${$i} или даже $$i — то, на что она указывает. Перл различает ссылки и нессылки и разбирает это выражение по ситуации.

wwoland

то есть зачем тебе обязательно поп от сплита?
сделай сначала сплит,засунь это куда нить,а потом от результата уже и попай =)

sakura

Кстати, хотел рассказать об одной приколькой баге.
my $space = ' ';
my $str = $space . 2 + 3;
print "without brackets: '$str'\n";
$str = $space . (2+3);
print "with brackets: '$str'\n";
# output is:
# without brackets: '5'
# with brackets: ' 5'
Разница в том, что без скобок пробел в начале строчки не печатается...
Я долго не мог понять эту ошибку. Фишка вот в чем. перл читает строку без скобок слева направо. т.е. сначала он склеивает $space и цифру 2. Затем он видит операцию сложения и автоматически преобразует строку ' 2' к числу 2 и склдывает. Поэтому-то пробел в первом случае и пропал.
my $two = ' 2'; # space at the begining!
my $three = 3;
my $res = $two + $three;
print "'$res'";
# output is: '5'
Прикольно, да?

sakura

а так
ну так в том и фишка, что я хочу в одну строчку без создания промежуточной ссылки на массив!

wwoland

хз,а нах тебе? =)

sakura

Это способ обратиться к переменной, имя которой содержится в $i
а почему тогда не работает?
my $var = 'Hello!';
my $var_name = 'var';
print "${$var_name}";
без strict и warnings он вообще ничего не печатает...

wwoland

блин ты даже на строку посмотри, там же двойка явно прилипает визуально к пробелу
а так, наверное все дело в приоритете =)

sany79

Из-за my в первой строчке. Какому именно правилу это соответствует, точно не скажу.
А можно ещё давать переменным неприличные имена:

${'Привет!'} = 'Hello!';
my $var_name = 'Привет!';
print "${$var_name}";
print "\n";

sakura

хз,а нах тебе? =)
ну так просто... красивее и понятнее было бы смотреть на код в одной строчке...
вариант с двумя строчками кода, конечно, понятный и прозрачный:
my @var=split(/ /,'1 2 3');
my $head = pop @var;
Но это ж целые ДВЕ строчки кода на Perl'e! к тому же дополнительная переменная... какое расточительство!
можно, конечно, и без pop. Вот варинт, который предложил :
my $head = (split(/ /, '1 2 3'[-1];

sakura

Из-за my в первой строчке
да, заработало... Буду знать. Но пользовать, наверное, не буду. Уж больно андерграундный вариант....

wwoland

а насчет твоей баги,вот та строчка тада ваще пипец =)
print (2+3)*4;

sany79

Да, строчка крайне неудачная. print напечатает 5, вернёт какой-то результат, который без рользы в пустом контексте будет умножен на 4. Нужны скобки.

sakura

прикольно.
Однако, в твоем примере перл warning'и выдает (у меня он вообще ничего ни на что не руглася):
print (...) interpreted as function at D:\Programming\Perl\_tmp\test1.pl line 7.
Useless use of multiplication (*) in void context at D:\Programming\Perl\_tmp\test1.pl line 7.
а ошибка понятна. просто функция print думает, что скбочки это не для математичской формулы, а скобочки для нее (для функции print то есть). Поэтому и вылазеет warning про void context. Если написать так:
print 4*(2+3);
то все работает и без ругательств....

ava3443

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

sakura

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

wwoland

вот что пишет лама бук по поводу :
print (2+3)*4; # Oops!
When Perl sees this line of code, it prints 5, just as you asked. Then it takes the return value from print, which is 1, and multiplies that times 4. It then throws away the product, wondering why you didn't tell it to do something else with it. And at this point, someone looking over your shoulder says, "Hey, Perl can't do math! That should have printed 20, rather than 5!"

ark21

насчет $i, я думаю самый норм вариант это то что предложил , я тоже в итоге к этому варианту сколнился.
substr($line, $-[$_ + 1], $+[$_ + 1] - $-[$_ + 1])

Ilya1974

Как это характерно для перла.
Начать с фразы "добрый день", закончить фразой "влоушов(dlfeушуоов;;*d=d dkdkjвлваоовал". И говорить, что обе понятны и прекрасны.
Оставить комментарий
Имя или ник:
Комментарий: