Как работает этот Perl one liner, чтобы проверить, пуст ли каталог?

Сегодня я получил эту странную строку кода, она говорит мне «пусто» или «не пусто» в зависимости от того, есть ли в CWD какие-либо элементы (кроме . и ..).

Я хочу знать, как это работает, потому что для меня это не имеет смысла.

perl -le 'print+(q=not =)[2==(()=<.* *>)].empty'

Меня интересует бит <.* *>. Я не понимаю, как он получает имена всех файлов в каталоге.


person dsm    schedule 14.07.2009    source источник
comment
Не стесняйтесь редактировать теги на что-то более подходящее   -  person dsm    schedule 14.07.2009
comment
Ха-ха, ты такой веселый! Я катаюсь по полу от смеха. Вы назвали линейный шум Perl!!!! ХАХАХАХАХАХАХАХАХАХАХАХАХА!   -  person jrockway    schedule 14.07.2009
comment
Хорошая заявка на «обфускацию года». Мне нравится Perl, но еще больше мне нравятся комментарии в Perl-коде.   -  person Boldewyn    schedule 14.07.2009
comment
@jockway Нет, я назвал это Perl Line-Noise   -  person dsm    schedule 14.07.2009
comment
@dsm Кто-то еще опубликовал комментарий (удаленный), в котором говорится, что весь Perl - это линейный шум. Думаю, jrockway отреагировал на это.   -  person Sinan Ünür    schedule 14.07.2009
comment
Я действительно был. люблю удалять комментарии...   -  person jrockway    schedule 14.07.2009
comment
@jrockway: так пометьте свои комментарии, чтобы мы хотя бы знали, с кем вы не разговариваете. @Sinan Ünür: искушение удалить свой комментарий должно быть невыносимым :)   -  person ysth    schedule 15.07.2009


Ответы (5)


Это игра в гольф. Флаг -e означает выполнение остальной части командной строки как программы. -l включает автоматическую обработку конца строки.

Часть <.* *> представляет собой шар, содержащий два шаблона для расширения: .* и *.

Эта часть

(q=not =)

представляет собой список, содержащий одно значение — строку «не». q=...= — это альтернативный разделитель строк, который, по-видимому, используется, потому что одинарная кавычка используется для цитирования однострочника.

Часть [...] является индексом в этом списке. Значение нижнего индекса будет либо 0 (значение «не»), либо 1 (ничего, что печатается как пустая строка) в зависимости от результата этого сравнения:

2 == (()=<.* *>)

Здесь много чего происходит. Сравнение проверяет, вернул ли glob список ровно из двух элементов (предполагается, что это . и ..), но как это сделать, сложно. Внутренние скобки обозначают пустой список. Присвоение этому списку помещает глобус в контекст списка, так что он возвращает все файлы в каталоге. (В скалярном контексте он ведет себя как итератор и возвращает только один за раз.) Само присваивание оценивается в скалярном контексте (находится в правой части сравнения) и, следовательно, возвращает количество назначенных элементов.

Ведущий + предназначен для того, чтобы Perl не анализировал список как аргументы для print. Завершающий .empty объединяет строку «пусто» с тем, что вышло из списка (то есть либо «не», либо пустая строка).

person Michael Carman    schedule 14.07.2009
comment
Это немного вводит в заблуждение. Внутренний () делает его присваиванием списка, что дает его правильный контекст списка операндов, но это также может быть (2==(@a=‹.* *›)). - person ysth; 15.07.2009
comment
@ysth: я не понимаю, почему вы думаете, что это заблуждение. Поскольку во внутреннем () ничего нет, список отбрасывается. Цель круглых скобок (с точки зрения программиста) состояла в том, чтобы наложить контекст списка на glob. Вместо этого он мог бы назначить массив (что было бы яснее ИМХО), но он этого не сделал. Или вы имеете в виду тот факт, что пустое присваивание изменяет семантику списка, в скалярном контексте возвращает последний элемент, чтобы присваивание списка возвращало количество элементов? Я, вероятно, должен отредактировать ответ, чтобы уточнить этот бит... - person Michael Carman; 15.07.2009

<.* *>

— это глобус, состоящий из двух шаблонов: .* — все имена файлов, начинающиеся с ., а * соответствует всем файлам (это отличается от обычных соглашений DOS/Windows).

(()=<.* *>)

оценивает glob в контексте списка, возвращая все совпадающие имена файлов.

Затем сравнение с 2 помещает его в скалярный контекст, поэтому 2 сравнивается с количеством возвращенных файлов. Если это число 2, то единственными записями каталога будут . и .., точка. ;-)

person Sinan Ünür    schedule 14.07.2009

<.* *> означает (glob(".*"), glob("*")). glob расширяет шаблоны файлов так же, как это делает оболочка.

person dave4420    schedule 14.07.2009

Я считаю, что модуль B::Deparse немного помогает в расшифровке некоторых вещей, которые бросают глаза большинству программистов, таких как конструкция q=...=:

$ perl -MO=Deparse,-p,-q,-sC 2>/dev/null << EOF
> print+(q=not =)[2==(()=<.* *>)].empty
> EOF
use File::Glob ();
print((('not ')[(2 == (() = glob('.* *')))] . 'empty'));

Конечно, это не дает мгновенно «читабельного» кода, но, безусловно, устраняет некоторые камни преткновения.

person hillu    schedule 14.07.2009
comment
Хе. Никакой депарсинг не распутает использование логического значения для индексации в анонимный список. [антипедантичность: да, я знаю, что анонимный список излишен.] - person Michael Carman; 14.07.2009

Документация по этой функции находится здесь. (прокрутите ближе к концу раздела)

person dsm    schedule 14.07.2009
comment
Какая функция? Это должен быть комментарий к чьему-то ответу? - person Telemachus; 14.07.2009
comment
@Telemachus: ‹› предположительно означает шар. - person ysth; 15.07.2009
comment
@Telemachus: документация для glob/‹›, как упоминал @ysth - person dsm; 15.07.2009