Відповіді:
Це найважливіша порада з гольфу 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 '$_=$==$_'
і давати йому різні прищепки. Так само $-
змушує його значення бути невід'ємним цілим числом (тобто провідний тире трактується як нечисловий символ).
Ви можете використовувати $.
як булевий прапор, який автоматично скидається на справжнє (ненульове) значення при кожній ітерації 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
на першому рядку, правда?
}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){}
vs. map{}@c;
і корисно, якщо ви хочете зробити вкладені ітерації, оскільки ви можете помістити for
петлі постфікса всередині 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
vs.
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(…)
. Однак, залежно від того, які символи вам потрібні всередині рядка, ви, можливо, зможете зберегти байт та використати <…>
глобальний оператор. (Зверніть увагу, що те, що знаходиться у кутових дужках, не може виглядати як файл файлів і не може виглядати так, що це призначено для розширення до списку імен файлів, а це означає, що досить багато символів не працюватимуть правильно.)
Гідною ілюстрацією цього є наступний код, що формує вхід в синусоїду:
s/./print" "x(sin($i+=.5)*5+5).$&/eg;
Як бачите, це досить компактний спосіб перегляду символів у стандартному вводі. Ви можете використовувати інший регулярний вираз, щоб змінити спосіб узгодження речей.
$_=print""
коротше, ніж$_=print$foo
.