Pull to refresh

Comments 65

Позволю себе высказать две мысли.

1) Я понимаю, что присвоения в while и прочих «if»-ах — это общепринятая практика. Но на самом деле этот подход находится в прямом конфликте с концепцией структурного программирования. Присвоение, в данном случае, является side-effect-ом, и использование таких конструкций ни чем ну лучше (и не хуже) использования goto.

2) Perl — такой Perl :-)
В можете написать
open $fh, "/etc/passwd";
print join("", <$fh>);
но вы не можете использовать $fh внутри объекта.
Вот это сработает:
open $fh, "/etc/passwd";
$a->{'file'}=$fh;
А вот это — не сработает:
print join("", <$a->{'file'}>)'

Я очень люблю Perl :-) Могу ещё таких примеров накидать :-)
Возможно, проблема в том, что обращение $a->{file} содержит закрывающую стрелку?
Нет. Если написать my %a; $a{'file'} = $fh; … <$a{'file'}>; то тоже не работает.
ага! и скобки не помогают :-)
Perl — это просто бразец того, каким не должен быть язык.
Вот ещё пример (из любимого :-)):
$ perl -MData::Dumper -e '@a = map {        {$_ => $_} } qw/a b/; print Dumper(\@a)'
$VAR1 = [
          {
            'a' => 'a'
          },
          {
            'b' => 'b'
          }
        ];
$ perl -MData::Dumper -e '@a = map { $x = $_; {$x => $x} } qw/a b/; print Dumper(\@a)'
$VAR1 = [
          'a',
          'a',
          'b',
          'b'
        ];
Кто бы мог подумать, что резльтат будет разным! :-)
Хорошее слово: бразец. Надо запомнить! :)
А вот еще загадка из моих любимых (отвечать вслух необязательно): почему не мрет? ;-)
use strict;
$a = $a+1;
В колекцию: что делает такая штука?
${q-q-}-=${q=q=};
Отдадка белым: $q=0;
чёй-то я сегодня силён опечатываться :-) сори
Опять же если не пытаться говнокодить и использовать прагму strict, то о таких глупостях никогда и не узнаешь.

А туфту всякую можно на любом ЯП написать.
Чересчур банально для любимой загадки :)
$ perl -MData::Dumper -e '@a = map { $x = $_; +{$x => $x} } qw/a b/; print Dumper(\@a)'

Есть разница между блоком кода и ссылкой на хеш ;)
почему в перле сделано что это {{ $_, $_ }} не считается за блок?
Cразу видно человека не разбирающегося в Perl, во втором примере "=>" обозначает запятую, т.е. вы пытаетесь вернуть {} блок кода в котором возвращаются 2 переменные, потому переменные и удваиваются.

Стыдно, товарищ… в любом языке незнание синтаксиса приводит к подобным недопониманиям.
вы правы! поэтому, если заменить => на запятую — ничего не изменится. Вот эти две штуки работают по-разному:
@a = map {{$_, $_}} qw/a b/;
@a = map {;{$_, $_}} qw/a b/;
Это просто другая сторона той же самой корявости :-)
Да это всё понятно. Я даже не против плюсика перед фигурными скобками. Не против не потому, что считаю это решение хорошим (весь map в Perl реализован как коллекция уродств, кои можно перечислять бесконечно), а потому, что плюсик хоть где-то отражён в документации. В своих комментариях, я не хотел рассказать вам то, что и без того есть в perldoc (я же понимаю, с какими гениальными перловиками я рискую общаться). Я хотел рассказать то, что не описано в документации. То, что можно узнать только почитав исходники и рассуждения самого Лари. Я предлагаю удивиться тому, что точка с запятой перед (!) конструкцией, полностью меняет смысл этой конструкции! Тогда как по документации точка с запятой полностью нейтральна. Вот эти две программы работаю совершенно по-разному :-)
1) @a = map {{$_ => $_} } qw/a b/;
2) @a = map {;{$_ => $_} } qw/a b/;
Где про это сказано в документации? ,-)
Ладно, насчёт незнания Perl, я наверное погорячился. Посмотрел Devel::Deparse, в примере явно видно что меняется контекст. На самом деле это не является уродством ни в коей мере, просто все эти микрооптимизации и фишечки — это и есть то самое почему Perl интересный и очень сильный язык программирования и в документации это называется не иначе как «магия» =)
Perl прошёл очень долгий путь от продукта для микроавтоматизаций до уже серьёзного языка на котором можно уже делать всё что угодно, поэтому в нём так много синтаксического сахара и всяких фишек которые не понятны людям со стороны. Какие-то из этих нюансов очень упрощают написание небольших скриптов (тот же «даймонд» — ), какие-то наоборот раскрываются только в крупных проектах (синтаксический сахар Moose), я знаком с Perl уже много лет и ни на одну из грабель (даже наподобие) что вы указали не наступал, про многие такие нюансы знаю, но если быть честными они есть во всех языках. Опять же в С++ есть куча кода в котором «без пузыря не разберёшься», но это не значит что язык уродский и непонятный, мало того в нём же есть примеры которые дают разный результат при одном и том же коде.
Просто если тебе нравиться язык и ты понимаешь что ты делаешь — в разумной мере можно пользоваться этими нюансами, но Perl настолько гибкий, что всё очень просто и лаконично реализуется и нормальными документированными возможностями. А если надо разбирать чужой код есть мощные инструменты для помощи разбора непонятных участков кода (Perl::Tidy, Devel::Deparse).
Не нравиться — не пользуйся, всё просто, но не надо таких громких слов только из-за того что есть недопонимание в концепции языка, либо она каким либо образом не нравиться лично вам.
B::Deparse модуль выше надо было написать, а не Devel::Deparse… чего-то я поторопился
Не очень догоняю почему тут меняется контекст.
Можете объяснить? Не показать, что он меняется, а объяснить.

Да и контекста я знаю три бывает: пустой, скалярный, списочный. Или есть ещё какой-то?
Это есть в документации по map + необходимо понимать что такое контексты и как используются (это основное знание языка Perl).
Читайте мануалы :) В первом случае перл понимает скобки как ссылку на хеш, во втором — как блок ;)

Правильно будет:
perl -MData::Dumper -e '@a = map { my $x = $_; +{$x => $x} } qw/a b/; print Dumper(\@a)'
Ых, поспешил %)
Но что такое контекст забывать нельзя :) А Perl это контекстный язык %)
про это написано в perldoc.perl.org

У map есть две формы

map <блок> <список> и
и
map <выражение>, список

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

perl -MO=Deparse -MData::Dumper -e "@a = map {{$_ => $_}} qw/a b/; print Dumper(\@a)"
=======================================================
use Data::Dumper;
@a = map({+{$_, $_};} 'a', 'b');
print Dumper(\@a);
-e syntax OK


Видимо перл, видя код
map {{$_ => $_}} <список> пытается привести его к коду map +{$_ => $_}, <список>
но делает это он так:
map {+{$_ => $_}} <список>

Чтобы второй пример работал как первый поставьте плюс перед {$x => $x}:
perl -MData::Dumper -e "@a = map {$x = $_; +{$x => $x}; } qw/a b/; print Dumper(\@a)"
$VAR1 = [
{
'a' => 'a'
},
{
'b' => 'b'
}
];


Вообщем помогайте перлу явно показывать, что вы хотите вернуть.
UFO just landed and posted this here
If what's within the angle brackets is neither a filehandle nor a simple scalar variable containing a filehandle name, typeglob, or typeglob reference, it is interpreted as a filename pattern to be globbed, and either a list of filenames or the next filename in the list is returned, depending on context.

Грубый перевод: Если в угловых скобках не filehandle или не простой скаляр, содержащий filehandle или typeglob или ссылку на typeglob, то содержимое будет проинтерпретировано как паттер который будет развернут в командной строке. Результатом разворачивания будет или список имен файлов или следующий файл в списке(определяется контекстом).
Абалдеть. Нет, я уже несколько лет как заметил, что читать perldoc можно бесконечно, и всегда будет хоть что-то новое.
По п. 2: в diamond operator допускаются простые скаляры, но не выражения или элементы хэшэй/массивов (тынц). А вот для readline ограничений нет.
Все верно, работа оператора <> зависит от контекста
когда внутри $переменная то она интерполируется и вызывается чтение

Остальное <$a->{'file'}> уже воспринимается как TYPEGLOB
Не-а, в данном случае TYPEGLOB не причём :-)
Проблема глубже. Вкратце, дело в том, что все языки разбираются двумя анализаторами: лексическим и синтаксическим. Первый — простой — разбирает текст на слова. Второй — на много сложнее — разбирает последовательность слов на набор вложенных конструкций. Когда Лари начал писать Perl, он честно реализовал только лексический анализатор. Поковырявшись с синтаксическим, он решил, что это зло (не могу найти ссылку на эту эпическую его статью). Он провозгласил, что систематический подход, используемый для построения непротиворечивых языков — это детский сад. А он — Лари — напишет язык без всякого синтаксиса, без BNF нотации, и будет этот язык прекрасен. Сказано — сделано :-) Поэтому в перле есть внутренние противоречия. И это одно из них. Оно возникает до того, как на сцену выходят TYPEGLOBы.
(у 5.10, вроде, всё же сделали BNF нотацию, но я её не видел)
Тут достаточно вспомнить, что <> — это шорткат к readline (или к glob в зависимости от параметра) и в сложных случаях писать полностью. Такой код работает:
print join("", readline($a->{'file'}))
В Perl всегда есть второе решение… на случай, если первое не работает :-)
И это здорово, чёрт подери!!! Никогда не возникает тупиковых позиций. Не понимаешь как сделать одним способом — реализуй другим и будь счастлив!
Проблема не в избыточной гибкости, а в том, что Perl делает массу не-очевидных вещей и никак об этом не предупреждает. И без средств типа Devel::Deparse разобраться невозможно. Читать код становится невозможно. Даже свой :-)
Вот ещё примерчик:
$ perl -e 'print 1?$x=1:$x=2;'
$ perl -e 'print (1)?$x=1:$x=2;'
$ perl -e 'print ((1)?$x=1:$x=2);'
$ perl -e 'print (1?$x=1:$x=2);'
Один из вариантов работает не как другие — какой? Можете легко это понять, не запуская и не раздумывая десять минут над четырьмя простенькими строчками? Я — скажу честно — не могу :-)

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

Я избегаю перла там, где надо чтобы работало надёжно и предсказуемо. Мне перл нравится только для «поковыряться на досуге».
Люблю перл за три вещи:
1) В перле прекрасно сделаны мелочи. На пример сортировка — просто учебник, как правильно делать сортировку.
2) В исходниках перла много любопытных комментариев. Не редко попадаются интересные цитаты из книжек, фильмов… Просто интересно почитать :-)
3) Глобально пел сделано без всякой системы. Это жуткое нагромождение заплаток, костылей, подпорок… Грандиозная развалюха! Это впечатляет! Смотришь и думаешь: «ого! как же всё стройно в моих проектах». И тихо радуешься :-)
Не хотелось бы показаться занудой, но и этот вариант в perldoc'е есть (причём тщательно описан) =) хехе

Для того чтобы всё работало нажёдно и предсказуемого просто надо придерживаться правильного стиля написания программ и это проблема не языка, а программиста, просто у Perl слишком много свобод и у многих людей голова идёт кругом и они начинают лепить что попало (опять же много примеров якобы говнокода, но они в основном высосаны из пальца). А на самом деле в той же документации есть советы по написанию программ и если им придерживаться код будет абсолютно понятен и всегда будет работать так как надо. И там рекомендуют обрамлять выражения в скобочки.
Интересно сосуществование двух тезисов: «хороший Perl-программист знает все возможности Perl» и «хороший Perl-программист не использует все возможности Perl» :-) Те, кто усматривают в этом противоречие (я — в их числе) — программируют на Python :-)
«Хороший Perl-программист знает какие возможности использовать в проектах какого типа», Ларри Уолл говорил: «Простые вещи должны быть простыми, а сложные — возможными». Т.е. для простых небольших скриптов мы используем всю мощь Perl, а для сложных проектов соответстенно вспоминаем все рекомендации по написанию красивого и грамотного кода и пишем хороший код.
Конечно если люди сами не могут разрешить противоречия подобного плана они выбирают другой вариант (в данном случае Python), ваше право. Лично я для себя это противоречие разрешил и очень счастлив этому.
Спасибо за очень интересную дискуссию. Призовая игра специально для вас:
работает (хотя не верно):
perl -e 'map {last, last} ()'
не работает (хотя тоже самое) (компилируется, но слетает в tuntime):
perl -e 'map {last, last} (1)'
все точно также, если добавлять скобок типа такого:
perl -e 'map {{{last, last}}} ()'
но как только появляются ';' начинаются полнейшие чудеса:
вот это работает:
perl -e 'map {;{last, last}} (1)'
это компилится, но слетает в runtime:
perl -e 'map {{last, last};} (1)'
это не компилится — «syntax error»(!!!):
perl -e 'map {{{last, last};}} ()'
хотя, казалось бы, в чём тут синтаксическая неверность, если считать, что прошлые примеры верны?.. ,-)

Можете самостоятельно поиграться с return — он тоже работает не в любых блоках.

А сколько радости могут могут доставить примеры из серии:
perl -e 'map {{last => 0}} (1)'
vs
perl -e 'map {{last, 0}} (1)'

О!.. я обожаю Perl :-)
Наверное стоит Perl обновить, у меня 5.10.1 часть примеров тихо выполнились, а те что с ошибками написали Can't «Last» outside a loop block at -e line 1
Эти примеры вообще нереально увидеть вживую, т.к. last должна использоваться только в циклах, а map циклом не является.

Я не вижу проблем в том что код который и не должен работать не работает.
Ну я снимаю шляпу! Вы меня раскусили!
Последнее время я забросил перл и как раз мои знания остановились на 5.8, а 5.10, мне кажется, должны были очень сильно доработать в вопросах парсинга и диагностики. Вы подтвердили мои подозрения :-)
Кстати, last не должна использоваться только в циклах. Даже родная документация говорит об использовании last для выхода из блока
perl -e '{print 1; last if (1); print 2}'
perl -e '{print 1; last if (0); print 2}'

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

«last» cannot be used to exit a block which returns a value such as «eval {}», «sub {}» or «do {}», and should not be used to exit a grep() or map() operation.

стыдно товарищ!? © :-)))


Что-то я не пойму где вы шутите %)
Если что, то для меня все примеры понятны :)

Поигравшись с last (кстати next ведет себя так же в ваших примерах и коментариях ниже) сообразил для себя прикольный финт ушами :)

if (1) {{
print 1;
next if 1;
print 2;
}};

Мне всегда не хватало такой конструкции )))
Вот еще:

perl -MData::Dumper -e 'warn Dumper [map {$_ if $_ == 4} 1..10];'

Без интерпретатора не так просто угадать результат )))

Из той же оперы:
sub some {return 'A' if 0} что вернет?

Или еще из прекрасного )))
sub some {return}
warn Dumper {
some => some,
some => some,
};
warn Dumper {
some, some,
some, some,
};
Продолжать можно долго )))))
Не перестаю удивляться!
$ perl -MO=Deparse -e 'map {$_ if $_ == 4} 1..10'
map {$_ if $_ == 4;} 1..10;
-e syntax OK
$ perl -MO=Deparse,-p -e 'map {$_ if $_ == 4} 1..10'
map({(($_ == 4) and $_);} (1..10));
-e syntax OK
сюрприз!?
Ну как сказать :)
TMTOWTDI во всей красе ))
Второй вариант кстати проще к понимаю что происходит. Конечно при условии, что знаешь, что не истинное логическое выражение возвращает пустую строку, а истинное единицу )))
Я не люблю строить такие логические цепочки. Хотя вру. Люблю, но только с || или or ))) Они как-то понятнее смотрятся ) Да и применение находят чаще )
Так не понятно, что же происходит на самом деле! :-) Perl превращает if в and?
Я всегда думал, что программа должна парситься однозначно (пусть непредсказемо для программиста, но хотя бы однозначно для самого перла), а тут… просто не слов! Perl не раскрывает своих загадок никогда :-)
Похоже, что в этом случае действильно есть неоднозначность. Обычно интерпретация одинакова (независимо от того, просим мы показывать скобки или нет):
~$ perl -MO=Deparse, -e 'map {if ($_ == 4) {$_}} 1..10'
map {if ($_ == 4) {
    $_;
}} 1..10;
-e syntax OK
~$ perl -MO=Deparse,-p -e 'map {if ($_ == 4) {$_}} 1..10'
map({if (($_ == 4)) {
    $_;
}} (1..10));
-e syntax OK
Я был уверен, что Perl все условия раскрывает как тернарный оператор. ))

Я не экспериментировал с депарсом. Но что-то мне теперь подсказывает, что постфиксные условия Deparse раскрывает через логические операторы ))

Я and гарантированно использую в одном случае, когда не хочется отдельно объявлять переменную, а в одну строчку нельзя.
Писать
my $var = 'some' if $a == $b;
не есть правильно.
Я пишу
my $var = $a == $b && 'some'; # and писать нельзя! ))
Конечно при условии, что тут не получится откровенной фигни )))
Что-то мне подсказывает, что Perl поступил бы примерно так же ))

А вообще да, тут есть неоднозначность. Вернее сказать, неоднозначность для неопытного программиста )) Думаю большинство предположат, что мап сформирует список из одного элемента. Здесь же мап, в случаях, когда $_ != 4, вставит пустую строку, т.к. пустая строка есть последнее вычисленное значение внутри блока.

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

Мне помогло обрести понимание в данном вопросе.
Только если заранее знаешь об этом. Для меня в своё время обнаружение сей неприметной особенности было откровением.
Если память не изменяет, то в perl cookbook это подробно расписано
В ламабуке — точно))) И там ещё ЕМНИП написано что <> пишется в $_ только внутри условия while)))
Ну встречал я людей которые учили perl интуитивно и всяческие ламабуки не читали… А зачем? Зато потом сильно удивляются когда встречают всякие «недокументированные» особенности языка. :(

Никто не говорит, что perl лёгкий язык, но и не такой уж запутанный каким некоторые граждане пытаются его представить. Хотя… Те кто сначала «учит» php, а затем садиться за perl он может показаться нагромождением непонятностей :(

Лично мне pel, несмотря ни на что, нравится. :)
Дык стоп, кто говорит про недокументированные особенности? Всё прекрасно документировано, были б глаза прочитать. Перл очень прост и изящен, а главное — выразителен. Просто он находится в несколько другой нише, нежели большинство остальных языков. К нему надо привыкнуть. Прочувствовать его внутреннюю структуру. Тогда будет интуитовно понятно, почему ваша конструкция работает именно так, а не как вы хотели бы чтоб она работала)))
для чтения строки лучше использовать
my $line = readline($fh)

(нет, я не возражаю, я интересуюсь)
достаточно того, что код будет более понятным и не нужно выдумывать костыли.
<> — это не костыль, а шорткат, это разные вещи :-) А короткий код в данном случае понятней, дело привычки :-)
А что тут загадочного? Прочитайте любой мануал по работе условных конструкций.
Я реально напарывался на подобную ситуацию, когда итерации идут по результату запроса к базе данных:
while(my $ref = $sth->fetchrow_hashref()) {
}

Здесь всё хорошо, потому что в $ref — хэш. А потом делаешь запрос в одну колонку и думаешь, зачем тебе хэш, когда можно просто в строку читать? Ага:
while(my $str = $sth->fetchrow()) {
}

А когда в результате оказывается 0 (а он, как назло, почему-то не встретился в тестах, а встретился только в продакшне), тут и начинаются интересности.
Ну уж это результат грубого нечтения документации. Вот с map пример очень хороший…
Да нет, почему? Все всё читали. Насколько был бы прекрасней мир, если бы после чтения документации ни один программист не допускал таких ошибок :-)
заключай $str в скобки:
while(my ($str) = $sth->fetchrow()) {
}

тогда таких проблем не будет. Будет списочный контекст.
Легко понять, что делает перл с помощью модуля B::Deparse
perl -MO=Deparse -e 'while ($line = <STDIN>) {}'
---------------------------------------------------------------------------------
while (defined($line = <STDIN>)) {
();
}

Sign up to leave a comment.

Articles