Советы по игре в гольф на Perl?

47

Какие общие советы у вас есть для игры в гольф в Perl? Я ищу идеи, которые могут быть применены к кодовым проблемам гольфа в целом, которые хотя бы несколько специфичны для Perl (например, «удалить комментарии» - это не ответ). Пожалуйста, оставьте один совет за ответ.

коммандос
источник

Ответы:

26

TMTOWTDI

Это самый важный совет по игре в гольф на 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может быть использовано в контексте выражения, даже если они не возвращаются! Помня об этих видах преобразований, вы сможете заменить блоки кода выражениями, которые можно встроить в более широкий контекст.

Хлебница
источник
$_=print""короче $_=print$foo.
ASCIIThenANSI
5
Идея в том, что вам уже нужно печатать $foo. В противном случае, $_=1намного короче $_=print""и имеет тот же эффект.
хлебница
3
Для третьего вы имеете в виду перебирать символы в $x? В противном случае вы могли бы просто сделать /./gsи /./g.
Redbmk
25

Злоупотребляйте специальными переменными Perl!

  • Как было отмечено в предыдущем ответе $/и $"инициализируются по умолчанию "\n"и " ", соответственно.

  • $,и $\оба установлены undefпо умолчанию, и на 3 символа короче.

  • Установка $\значения приведет к его добавлению к каждому print. Например: perl -ple '$\="=".hex.$/'это удобный шестнадцатеричный преобразователь.

  • Если вы не читаете файлы из командной строки, вы можете использовать -iпереключатель командной строки в качестве дополнительного канала для ввода строки. Его значение будет сохранено в $^I.

  • $=заставляет все, что ему назначено, быть целым числом. Попробуйте запустить perl -ple '$_=$==$_'и дать ему различные inupts. Аналогично, $-заставляет его значение быть неотрицательным целым числом (т. Е. Начальный тире обрабатывается как не числовой символ).

  • Вы можете использовать $.в качестве логического флага, который автоматически сбрасывается в истинное (ненулевое) значение на каждой итерации while(<>)цикла.

Хлебница
источник
20

-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первой строки, верно?
ASCIIThenANSI
@ASCIIThenANSI: Да, это правильно. (Извините за поздний ответ.)
Деннис
1
Отдавая должное кредитам, я думаю, что впервые увидел сочетание этих трех приемов (непревзойденных фигурных скобок -pи $\​) в одном из ответов @primo на Perl. Читать его ответы - хороший совет Perl сам по себе.
Деннис
1
Бросать }for(...){между скобками также очень удобно, например, codegolf.stackexchange.com/a/25632
primo
Одна вещь, которую я нашел полезной в сочетании с -n, это оператор присваивания значения || = по умолчанию. Получает невозможность присвоить значение до цикла.
Deltaray
18

Используйте $_для устранения скалярных ссылок. Это специальная переменная, которая используется по умолчанию большинством функций, и просто не указывать параметры - это ярлык для ссылки на эту переменную.

Изменяя $nна $_, вы можете изменить $n=<>;chop$n;print$nна$_=<>;chop;print

Здесь printфункция печатает содержимое $_по умолчанию, а chopтакже работает $_.

PhiNotPi
источник
Является ли $_=<>;требуется, не <>;читать строки в $_автоматически?
sundar - Восстановить Монику
Нет, я так не думаю. Я сравнил программы $_=<>;printи <>;print. Первый повторяет мне то, что я печатаю, а другой нет.
PhiNotPi
О, спасибо, получается, что происходит только в таких случаях, как print while(<>). Не уверен, что это особый случай или за ним стоит какая-то логическая логика, но ни <>часть, perlopни whileчасть, perlsynпохоже, не упоминают об этом поведении.
sundar - Восстановить Монику
1
@sundar while(<>)- это особый случай, задокументированный в perlsyn, Операторы ввода / вывода: «Если и только если входной символ является единственной вещью внутри условного выражения while (даже если замаскировано под« for (;;) » цикл), значение автоматически присваивается глобальной переменной $ _, уничтожая все, что было ранее. "
kernigh
17

Всегда используйте специальные переменные Perl, например:

  • Используйте $"вместо" "
  • Используйте $/вместо"\n"

Они имеют дополнительное преимущество, заключающееся в том, что они являются гарантированным длинно-символьным идентификатором с помощью лексера. Это позволяет связать его с последующим ключевым словом, например:print$.for@_

Список всех специальных переменных доступен здесь: Специальные переменные

3aw5TZetdf
источник
15

Не используйте qw. Это пустая трата двух символов, которые можно было бы использовать лучше. Например, не пишите следующее.

@i=qw(unique value);

Вместо этого используйте голые слова.

@i=(unique,value);

Или, если вы не можете использовать голые слова, используйте globсинтаксис.

@i=<unique value>;

glob синтаксис также может быть использован для интересных эффектов.

@i=<item{1,2,3}>;
Конрад Боровски
источник
12

Используйте модификаторы операторов вместо составных операторов.

Сложные операторы, как правило, требуют скобок для аргумента и фигурных скобок для блока, тогда как модификаторы операторов не нуждаются ни в одном.

Для сравнения:

  • $a++,$b++while$n-- против while($n--){$a++;$b++}
  • chop$,if$c против if($c){chop$,}

Обратите внимание, что последний пример связан с $c&&chop$,, но начинает действительно сиять для большинства операций с несколькими операторами. В основном все, что теряет приоритет оператора &&.

JB
источник
11

Не use strict. (не цитируйте меня по этому поводу, контекст PCG.SE как бы имеет значение) И, что более важно, не кодируйте, как если бы он был строгим. Обычные подозреваемые:

  • не myуказывайте переменные, если вы можете избежать этого. Единственные переменные, которые действительно нужны my- это те, которые вы хотите получить в лексическом смысле. Это почти ни один из них при игре в гольф, где вам не нужна защита прицела и вы, как правило, полностью контролируете рекурсию.
  • не заключайте в кавычки строки из одного слова: ( пример ). Убедитесь, что у вас нет функции с тем же именем.
JB
источник
5
print helloне сработает Это на самом деле означает print hello $_(печать $_в файл дескриптор hello).
Конрад Боровски,
@ GlitchMr спасибо! (и теперь я обескуражен, потому что моя точка зрения остается в силе, но не с этим print, и теперь я не могу найти хороший и короткий пример)
JB
@JB вот хороший пример: codegolf.stackexchange.com/a/8746/3348
ardnew
11

Я уверен, что у некоторых из них есть формальные имена, и я просто не знаю их.

  • Если у вас есть цикл while (или цикл for, который вы можете превратить в цикл while), вы можете определить «while» после команды: print $n++ while ($n < 10)
  • Если вам нужно прочитать все из STDIN в строку: $var = join('',<>)
  • Как указал CeilingSpy, использование $ / вместо \ n в некоторых ситуациях быстрее: print ('X'*10) . "\n";дольше, чемprint ('X'*10) . $/;
  • sayФункция Perl короче print, но вам придется запускать код -Eвместо-e
  • Используйте диапазоны как a..zили даже aa..zz. Если нужно в качестве строки, используйте join.
  • Инкрементные строки: $z = 'z'; print ++$z;будет отображатьсяaa

Это все, что я могу думать прямо сейчас. Я могу добавить еще немного позже.

Мистер лама
источник
1
Что print ('X'*10) . $/;должен делать? Для меня это печатает 0и без перевода строки. С одной стороны, круглые скобки становятся аргументом вызова в стиле функции print, который связывается более жестко, чем .. И ты имел в виду xвместо *или что-то?
aschepler
Никаких скобок в postfix не требуется while, join'',<>;также работает без них.
choroba
10

Используйте несловесные символы в качестве имен переменных

Использование $%вместо $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
Дом Гастингс
источник
9

redoдобавляет поведение цикла в блок без forили while. {redo}это бесконечный цикл.

user130144
источник
7

Не заключайте в скобки вызовы функций.

Perl позволяет вам вызывать известную (базовую или предварительно объявленную) функцию с использованием NAME LISTсинтаксиса. Это позволяет вам сбросить &сигил (если вы все еще использовали его), а также скобки.

Например: $v=join'',<>

Полная документация

JB
источник
5

Попробуйте использовать значение выражения присваивания, например так:

# 14 characters
$n=pop;$o=$n&1

# 13 characters, saves 1
$o=($n=pop)&1

Это работает, потому что $nв Perl 2 символа. Вы можете изменить , $nчтобы ()без каких - либо затрат, и сохранить 1 точку с запятой, перемещая задания в скобках.

kernigh
источник
5

Вы можете запустить несколько различных операторов в рамках вложенной троичной логики.

Предположим , у вас есть большой 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)
hmatt1
источник
4

Используйте 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 байт, что, безусловно, является улучшением в большинстве случаев.

ASCIIThenANSI
источник
1

Сократите свои печатные заявления

Если в задании не указано иное, вам не нужны завершающие символы новой строки.
Наш «вызов» говорит «вывести случайное число от 0 до 9 для STDOUT». Мы можем взять этот код (28 байт):

$s=int(rand(10));print"$s\n"

И сократить его до этого (25 байт):

$s=int(rand(10));print $s

просто печатая переменную. Этот последний относится только к этой проблеме конкретно (19 байт):

print int(rand(10))

но это работает только тогда, когда вам не нужно ничего делать с переменной между присваиванием и печатью.

ASCIIThenANSI
источник
Последний уже указан здесь .
Sp3000
@ Sp3000 Спасибо, я обновил свой ответ.
ASCIIThenANSI
1

Используйте глобусы как строковые литералы

Изредка (часто при работе с или проблемами с ) вы получаете большую выгоду от способности вкладывать строковые литералы. Обычно, вы бы сделали это с q(…). Однако, в зависимости от того, какие символы вам нужны внутри строки, вы можете сохранить байт и использовать <…>оператор glob. (Обратите внимание, что то, что находится внутри угловых скобок, не может выглядеть как дескриптор файла, и не может выглядеть так, как будто оно предназначено для расширения в список имен файлов, что означает, что довольно много символов не будут работать правильно.)


источник
0

Используйте выражение регулярное выражение.

Достойной иллюстрацией этого является следующий код, формирующий вход в синусоидальную волну:

s/./print" "x(sin($i+=.5)*5+5).$&/eg;

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

Кшиштоф Шевчик
источник