Ответы:
Это самый важный совет по игре в гольф на Perl, который вам нужно знать. Всякий раз, когда вы смотрите на какую-то слишком длинную последовательность символов, которая вам абсолютно необходима для выполнения вашей задачи, спросите себя, нет ли другого способа получить тот же эффект с помощью другой функции. Там обычно есть. Вот только несколько:
~~
обеспечивает скалярный контекст и на 4 символа короче, чем scalar
.
y///c
на один символ короче, чем length
при получении длины $_
.
Нужно перебрать символы $_
? Заменить split//
на /./gs
. (Или используйте, /./g
если вы также хотите пропустить переводы строки.) Это работает с другими переменными: замените split//,$x
на $x=~/./gs
.
Каждый встроенный Perl возвращает что-то. print
возвращает 1, например, чтобы указать успешный ввод / вывод. Если вам нужно инициализировать $_
к истинному значению, например, $_=print$foo
позволяет убить двух зайцев одним выстрелом.
Почти каждое утверждение в Perl может быть написано как выражение, что позволяет использовать его в более широком контексте. Несколько операторов могут стать множественными выражениями, соединенными запятыми. Тесты могут быть выполнены с операторами короткого замыкания ?:
&&
||
, а также с and
и or
, которые делают то же самое, но с приоритетом ниже, чем у всех других операторов (включая присвоение). Циклы могут быть сделаны через map
или grep
. Даже такие слова как next
, last
и return
может быть использовано в контексте выражения, даже если они не возвращаются! Помня об этих видах преобразований, вы сможете заменить блоки кода выражениями, которые можно встроить в более широкий контекст.
$foo
. В противном случае, $_=1
намного короче $_=print""
и имеет тот же эффект.
$x
? В противном случае вы могли бы просто сделать /./gs
и /./g
.
Злоупотребляйте специальными переменными Perl!
Как было отмечено в предыдущем ответе $/
и $"
инициализируются по умолчанию "\n"
и " "
, соответственно.
$,
и $\
оба установлены undef
по умолчанию, и на 3 символа короче.
Установка $\
значения приведет к его добавлению к каждому print
. Например: perl -ple '$\="=".hex.$/'
это удобный шестнадцатеричный преобразователь.
Если вы не читаете файлы из командной строки, вы можете использовать -i
переключатель командной строки в качестве дополнительного канала для ввода строки. Его значение будет сохранено в $^I
.
$=
заставляет все, что ему назначено, быть целым числом. Попробуйте запустить perl -ple '$_=$==$_'
и дать ему различные inupts. Аналогично, $-
заставляет его значение быть неотрицательным целым числом (т. Е. Начальный тире обрабатывается как не числовой символ).
Вы можете использовать $.
в качестве логического флага, который автоматически сбрасывается в истинное (ненулевое) значение на каждой итерации while(<>)
цикла.
-n
и непревзойденные фигурные скобкиХорошо известно, что ключ командной строки -n
можно использовать для выполнения скрипта один раз для каждой строки.
perl --help
говорит:
-n assume "while (<>) { ... }" loop around program
Что он не говорит явно, так это то, что Perl не просто предполагает цикл вокруг программы; это буквально оборачивается while (<>) { ... }
вокруг этого.
Таким образом, следующие команды эквивалентны друг другу:
perl -e 'while(<>){code}morecode'
perl -ne 'code;END{morecode}'
perl -ne 'code}{morecode'
-p
и непревзойденные фигурные скобкиКак и выше, -p
коммутатор оборачивается while (<>) { ... ; print }
вокруг программы.
При использовании несоответствующих фигурных скобок, perl -p 'code}{morecode'
печать будет выполняться только один раз после выполнения code
для всех строк ввода, после чего следует morecode
.
Поскольку $_
при morecode;print
выполнении не определено, разделитель выходной записи $\
может использоваться для печати фактического вывода.
Например
perl -pe '$\+=$_}{'
читает одно число в строке из STDIN и печатает их сумму.
#!perl -n
первой строки, верно?
-p
и $\
) в одном из ответов @primo на Perl. Читать его ответы - хороший совет Perl сам по себе.
}for(...){
между скобками также очень удобно, например, codegolf.stackexchange.com/a/25632
Используйте $_
для устранения скалярных ссылок. Это специальная переменная, которая используется по умолчанию большинством функций, и просто не указывать параметры - это ярлык для ссылки на эту переменную.
Изменяя $n
на $_
, вы можете изменить $n=<>;chop$n;print$n
на$_=<>;chop;print
Здесь print
функция печатает содержимое $_
по умолчанию, а chop
также работает $_
.
$_=<>;
требуется, не <>;
читать строки в $_
автоматически?
$_=<>;print
и <>;print
. Первый повторяет мне то, что я печатаю, а другой нет.
print while(<>)
. Не уверен, что это особый случай или за ним стоит какая-то логическая логика, но ни <>
часть, perlop
ни while
часть, perlsyn
похоже, не упоминают об этом поведении.
while(<>)
- это особый случай, задокументированный в perlsyn, Операторы ввода / вывода: «Если и только если входной символ является единственной вещью внутри условного выражения while (даже если замаскировано под« for (;;) » цикл), значение автоматически присваивается глобальной переменной $ _, уничтожая все, что было ранее. "
Всегда используйте специальные переменные Perl, например:
$"
вместо" "
$/
вместо"\n"
Они имеют дополнительное преимущество, заключающееся в том, что они являются гарантированным длинно-символьным идентификатором с помощью лексера. Это позволяет связать его с последующим ключевым словом, например:print$.for@_
Список всех специальных переменных доступен здесь: Специальные переменные
Не используйте qw
. Это пустая трата двух символов, которые можно было бы использовать лучше. Например, не пишите следующее.
@i=qw(unique value);
Вместо этого используйте голые слова.
@i=(unique,value);
Или, если вы не можете использовать голые слова, используйте glob
синтаксис.
@i=<unique value>;
glob
синтаксис также может быть использован для интересных эффектов.
@i=<item{1,2,3}>;
Используйте модификаторы операторов вместо составных операторов.
Сложные операторы, как правило, требуют скобок для аргумента и фигурных скобок для блока, тогда как модификаторы операторов не нуждаются ни в одном.
Для сравнения:
$a++,$b++while$n--
против while($n--){$a++;$b++}
chop$,if$c
против if($c){chop$,}
Обратите внимание, что последний пример связан с $c&&chop$,
, но начинает действительно сиять для большинства операций с несколькими операторами. В основном все, что теряет приоритет оператора &&
.
Не use strict
. (не цитируйте меня по этому поводу, контекст PCG.SE как бы имеет значение) И, что более важно, не кодируйте, как если бы он был строгим. Обычные подозреваемые:
my
указывайте переменные, если вы можете избежать этого. Единственные переменные, которые действительно нужны my
- это те, которые вы хотите получить в лексическом смысле. Это почти ни один из них при игре в гольф, где вам не нужна защита прицела и вы, как правило, полностью контролируете рекурсию.print hello
не сработает Это на самом деле означает print hello $_
(печать $_
в файл дескриптор hello
).
print
, и теперь я не могу найти хороший и короткий пример)
Я уверен, что у некоторых из них есть формальные имена, и я просто не знаю их.
print $n++ while ($n < 10)
$var = join('',<>)
print ('X'*10) . "\n";
дольше, чемprint ('X'*10) . $/;
say
Функция Perl короче print
, но вам придется запускать код -E
вместо-e
a..z
или даже aa..zz
. Если нужно в качестве строки, используйте join
.$z = 'z'; print ++$z;
будет отображатьсяaa
Это все, что я могу думать прямо сейчас. Я могу добавить еще немного позже.
print ('X'*10) . $/;
должен делать? Для меня это печатает 0
и без перевода строки. С одной стороны, круглые скобки становятся аргументом вызова в стиле функции print
, который связывается более жестко, чем .
. И ты имел в виду x
вместо *
или что-то?
while
, join'',<>;
также работает без них.
Использование $%
вместо $a
может позволить вам поместить имя переменной рядом с if
, for
или while
построить как в:
@r=(1,2,3,4,5);$%=4;
print$_*$%for@r
Многие из них можно использовать, но проверьте документы и ответ @ BreadBox, для которых есть магические эффекты!
Если вы не можете использовать модификаторы операторов согласно ответу @ JB , карта может сохранить байт:
for(@c){}
против map{}@c;
и полезно, если вы хотите делать вложенные итерации, поскольку вы можете поместить for
циклы postfix внутри map
.
В Perl есть магические переменные для «текст до совпадения» и «текст после совпадения», поэтому можно разбить на группы по два с потенциально меньшим количеством символов:
($x,$y)=split/,/,$_;
($x,$y)=/(.+),(.+)/;
/,/; # $x=$`, $y=$'
# Note: you will need to save the variables if you'll be using more regex matches!
Это также может хорошо подойти для замены substr
:
$s=substr$_,1;
/./;# $s=$'
$s=substr$_,4;
/.{4}/;# $s=$'
Если вам нужно содержимое матча, $&
можно использовать, например:
# assume input like '10 PRINT "Hello, World!"'
($n,$c,$x)=split/ /,$_;
/ .+ /; # $n=$`, $c=$&, $x=$'
Если print
в вашем коде вы говорите «скажем» четыре или более раз (это, очевидно, зависит от длины вызываемой вами подпрограммы), замените ее на более короткое имя:
sub p{print@_}p;p;p;p
против
print;print;print;print
Если у вас есть такой код:
$i--if$i>0
вы можете использовать:
$i-=$i>0
вместо этого, чтобы сохранить несколько байтов.
Если вы не присваиваете переменную и не можете использовать подсказку хлебной корзины , вы можете использовать выражение 0|
:
rand 25 # random float eg. 19.3560355885212
int rand 25 # random int
0|rand 25 # random int
rand 25|0 # random int
~~rand 25 # random int
Однако стоит отметить, что вам не нужно использовать целое число для доступа к индексу массива:
@letters = A..Z;
$letters[rand 26]; # random letter
redo
добавляет поведение цикла в блок без for
или while
. {redo}
это бесконечный цикл.
Не заключайте в скобки вызовы функций.
Perl позволяет вам вызывать известную (базовую или предварительно объявленную) функцию с использованием NAME LIST
синтаксиса. Это позволяет вам сбросить &
сигил (если вы все еще использовали его), а также скобки.
Например: $v=join'',<>
Попробуйте использовать значение выражения присваивания, например так:
# 14 characters
$n=pop;$o=$n&1
# 13 characters, saves 1
$o=($n=pop)&1
Это работает, потому что $n
в Perl 2 символа. Вы можете изменить , $n
чтобы ()
без каких - либо затрат, и сохранить 1 точку с запятой, перемещая задания в скобках.
Вы можете запустить несколько различных операторов в рамках вложенной троичной логики.
Предположим , у вас есть большой if
- elsif
заявление. Это может быть любая логика и любое количество утверждений.
if( $_ < 1 ) {
$a=1;
$b=2;
$c=3;
say $a.$b.$c;
} elsif($_ < 2 ) {
$a=3;
$b=2;
$c=1;
say $a.$b.$c;
} elsif( $_ < 3) {
$a=2;
$b=2;
$c=2;
say $a.$b.$c;
}
Вы можете использовать (cmd1, cmd2, cmd3)
внутри троичного оператора для запуска всех команд.
$_ < 1 ?
($a=1,$b=2,$c=3,say$a.$b.$c):
$_ < 2 ?
($a=3,$b=2,$c=1,say$a.$b.$c):
$_ < 3 ?
($a=2,$b=2,$c=2,say$a.$b.$c):
0; #put the else here if we have one
Вот фиктивный пример:
perl -nE'$_<1?($a=1,$b=2,$c=3,say$a.$b.$c):$_<2?($a=3,$b=2,$c=1,say$a.$b.$c):$_<3?($a=2,$b=2,$c=2,say$a.$b.$c):0;' <(echo 2)
select(undef,undef,undef,$timeout)
вместоTime::HiRes
(Взято с https://stackoverflow.com/a/896928/4739548 )
Многие проблемы требуют от вас спать с большей точностью, чем целые числа. select()
аргумент тайм-аут может сделать именно это.
select($u,$u,$u,0.1)
гораздо эффективнее, чем:
import Time::HiRes qw(sleep);sleep(0.1)
Первый занимает всего 20 байтов, тогда как последний занимает 39. Однако первый требует, чтобы вы не использовали его $u
и никогда не определяли.
Если вы собираетесь использовать его, импорт Time::HiRes
окупается, но если вам это нужно только один раз, использование select($u,$u,$u,0.1)
экономит 19 байт, что, безусловно, является улучшением в большинстве случаев.
Если в задании не указано иное, вам не нужны завершающие символы новой строки.
Наш «вызов» говорит «вывести случайное число от 0 до 9 для STDOUT». Мы можем взять этот код (28 байт):
$s=int(rand(10));print"$s\n"
И сократить его до этого (25 байт):
$s=int(rand(10));print $s
просто печатая переменную. Этот последний относится только к этой проблеме конкретно (19 байт):
print int(rand(10))
но это работает только тогда, когда вам не нужно ничего делать с переменной между присваиванием и печатью.
Изредка (часто при работе с квайнами или проблемами с ограниченными источниками ) вы получаете большую выгоду от способности вкладывать строковые литералы. Обычно, вы бы сделали это с q(…)
. Однако, в зависимости от того, какие символы вам нужны внутри строки, вы можете сохранить байт и использовать <…>
оператор glob. (Обратите внимание, что то, что находится внутри угловых скобок, не может выглядеть как дескриптор файла, и не может выглядеть так, как будто оно предназначено для расширения в список имен файлов, что означает, что довольно много символов не будут работать правильно.)
Достойной иллюстрацией этого является следующий код, формирующий вход в синусоидальную волну:
s/./print" "x(sin($i+=.5)*5+5).$&/eg;
Как видите, это довольно компактный способ перебора символов в стандартном вводе. Вы можете использовать другое регулярное выражение, чтобы изменить способ сопоставления вещей.
$_=print""
короче$_=print$foo
.