Оператор goatse в Перле

Внезапно вспомним Перл. Когда-то я на нём много и в удовольствием программировал, причём этот навык не утерян мною до сих пор. Хотя попрограммировать на этом девиантном языке мне удаётся редко, я по-прежнему им восхищён. Немного жаль, что он умирает, но право же, чаще программы на нём можно слать в передачу «А вам слабо?», чем в продакшн.

Есть такая шутка, что Перл единственный язык, программа на котором до и после RSA-шифрования выглядит одинаково. Честно сказать, я не уверен, что это шутка, так как вот это, например, вполне разумная программа на Перле:

not exp log srand xor s qq qx xor
s x x length uc ord and print chr
ord for qw q join use sub tied qx
xor eval xor print qq q q xor int
eval lc q m cos and print chr ord
for qw y abs ne open tied hex exp
ref y m xor scalar srand print qq
q q xor int eval lc qq y sqrt cos
and print chr ord for qw x printf
each return local x y or print qq
s s and eval q s undef or oct xor
time xor ref print chr int ord lc
foreach qw y hex alarm chdir kill
exec return y s gt sin sort split

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

Я постараюсь пропустить неаппетитные подробности, но есть такой мем «goatse» — это картинки мужичка, в деталях показывающего свою тёмную неизвестность, а так же пародии на неё. Не гуглите пожалуйста это, прошу, нет, даже умоляю, не надо. Та картинка, как любой шок-контент, используется в троллинге.

Для нас важно, что после того как картинка стала мемом, появился смайлик „=()=“, довольно точно отражающий её суть. Я знаю эту преамбулу, потому что однажды какой-то экспериментатор нашёл в Перле то, что назвали goatse operator — тот самый „=()=“, который, что интересно, позволяет делать довольно разумную вещь.

Например, вам нужно подсчитать сколько цифр содержится в строке. На помощь вам придёт goatse operator!

my $s = "Hello, world! This is a Perl! 123456";
my $c =()= $s =~ /\d/g;
print $c;

В результате будет напечатано число шесть.

Как это работает и почему нельзя как-то иначе?

Сначала отвечу на первую часть.

Самое правое выражение в рассматриваемой строчке — регулярное выражение, которое ищет одну цифру, оператором «=~» мы применяем его к конкретной переменной, а ключ «g» в конце регулярки говорит, что искать надо все вхождения.

В Перле есть понятие скалярного и списочного контекста. Результат может сильно отличаться в зависимости от того ожидается ли в каком-то месте кода скаляр или список (массив). Программист даже может проверить в каком контексте вызвана его функция при помощи специальной функции «wantarray».

Например, если я попробую напрямую присвоить результат выполнения регулярного выражения на переменной „s“ в скалярную переменную (начинается с доллара), то я получу флаг, показывающий встретились ли вообще цифры в рассматриваемой переменной. Если же присвоить тот же результат массиву (имя переменной начинается с «@»), то я получу массив совпадений.

С goatse operator происходит следующее.

На деле «() = что-то ещё» — это обычное присваивание массиву чего-то ещё. Пустые скобки — пустой анонимный массив, ему тоже можно что-то присвоить, синтаксис это позволяет. Таким образом контекст не скалярный и мы всего-то присваиваем одному массиву другой. Дальше чуть интереснее. По правилам Перла присваивание первого массива второму в скалярном контексте возвращает количество элементов первого.

Получается «SCALAR = LIST0 = LISTREGEXP», что возвращает нам количество элементов массива LISTREGEXP при любом не константном LIST0. Двумя присваиваниями, идущими подряд, вряд ли кого-то удивишь, цепочки присваиваний можно использовать в Си, JavaScript, PHP и куче других языков.

Теперь ответ на второй вопрос. Нельзя ли как-то иначе. Можно. Если религия вам не позволяет использовать «оператор» с такими сомнительными ассоциациями, то можно, например, присвоить результат работы регулярного выражения переменной-массиву, а потом взять его в скалярном контексте. Как-то так:

my $s = "Hello, world! This is a Perl! 123456";
my @c =$s =~ /\d/g;
print scalar @c; # массив в скалярном контексте вернёт количество элементов

Но это скучно.

25 комментариев

Макбук под рентгеном

Макбук и фотоаппарат (37.16КиБ)

Смотрите-ка, полиция после взрывов начала делать что-то более существенное, чем установка рамок, на писк которых никто внимание не обращает — установили у некоторых входов в метро сканеры для багажа, похожие на большие духовые шкафы.

Меня вчера на Охотном ряду остановили в метро, попросили рюкзак положить в эту «микроволновку» и даже совершенно не препятствовали тому, чтобы я это всё сфотографировал (правда на телефон, фотоаппарат в рюкзаке остался). Итого на фото видно мой Макбук (в алюминиевом корпусе) и фотоаппарат (продолговатое тёмное на фоне ноута).

7 комментариев

location.reload() ≠ location.href=location.href

Увидел у Никиты Васильева во внутренних бложиках, что вызов «location.reload()» не эквивалентен присваиванию location.href в себя же. Оказывается, последний перезагружает страницу из кеша во всех браузерах, кроме «Оперы».

6 комментариев