Советы по игре в гольф на 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также работает $_.


Является ли $_=<>;требуется, не <>;читать строки в $_автоматически?
sundar - Восстановить Монику

Нет, я так не думаю. Я сравнил программы $_=<>;printи <>;print. Первый повторяет мне то, что я печатаю, а другой нет.
PhiNotPi

О, спасибо, получается, что происходит только в таких случаях, как print while(<>). Не уверен, что это особый случай или за ним стоит какая-то логическая логика, но ни <>часть, perlopни whileчасть, perlsynпохоже, не упоминают об этом поведении.
sundar - Восстановить Монику

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

17

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

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

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

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


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$,, но начинает действительно сиять для большинства операций с несколькими операторами. В основном все, что теряет приоритет оператора &&.


11

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

  • не myуказывайте переменные, если вы можете избежать этого. Единственные переменные, которые действительно нужны my- это те, которые вы хотите получить в лексическом смысле. Это почти ни один из них при игре в гольф, где вам не нужна защита прицела и вы, как правило, полностью контролируете рекурсию.
  • не заключайте в кавычки строки из одного слова: ( пример ). Убедитесь, что у вас нет функции с тем же именем.

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}это бесконечный цикл.


7

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

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

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

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


5

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

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

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

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


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)

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


1

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

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

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

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

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

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

print int(rand(10))

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


Последний уже указан здесь .
Sp3000

@ Sp3000 Спасибо, я обновил свой ответ.
ASCIIThenANSI

1

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

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


0

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.